//! CAR (Content Addressed aRchive) file support for trustless verification.
//!
//! CAR files bundle content with its Merkle proofs, allowing clients to
//! verify content without trusting the gateway.
//!
//! # Format
//!
//! ```text
//! CAR v1 Format:
//! ┌─────────────────────────────────────────┐
//! │ Header (CBOR)                           │
//! │   - version: 1                          │
//! │   - roots: [CID...]                     │
//! └─────────────────────────────────────────┘
//! ┌─────────────────────────────────────────┐
//! │ Block 1                                 │
//! │   - CID (varint length + bytes)         │
//! │   - Data (varint length + bytes)        │
//! └─────────────────────────────────────────┘
//! │ Block 2...N                             │
//! └─────────────────────────────────────────┘
//! ```
//!
//! # Security
//!
//! Clients verify by:
//! 1. Parsing the CAR file
//! 2. Hashing each block
//! 3. Verifying hash matches declared CID
//! 4. Building Merkle tree from blocks
//! 5. Verifying root CID matches expected

use crate::cid::ContentId;
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// CAR file version
pub const CAR_VERSION: u64 = 1;

/// CAR file header
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CarHeader {
    /// Version (always 1)
    pub version: u64,
    /// Root CIDs
    pub roots: Vec<ContentId>,
}

impl CarHeader {
    /// Create a new CAR header with single root
    pub fn new(root: ContentId) -> Self {
        Self {
            version: CAR_VERSION,
            roots: vec![root],
        }
    }

    /// Create a header with multiple roots
    pub fn with_roots(roots: Vec<ContentId>) -> Self {
        Self {
            version: CAR_VERSION,
            roots,
        }
    }

    /// Encode header to bytes (simplified CBOR-like format)
    pub fn encode(&self) -> Vec<u8> {
        let mut buf = Vec::new();

        // Version
        buf.extend(encode_varint(self.version));

        // Number of roots
        buf.extend(encode_varint(self.roots.len() as u64));

        // Each root CID
        for root in &self.roots {
            let cid_bytes = root.to_string_repr().into_bytes();
            buf.extend(encode_varint(cid_bytes.len() as u64));
            buf.extend(&cid_bytes);
        }

        buf
    }

    /// Decode header from bytes
    pub fn decode(data: &[u8]) -> Result<(Self, usize)> {
        let mut offset = 0;

        // Version
        let (version, read) = decode_varint(&data[offset..])?;
        offset += read;

        if version != CAR_VERSION {
            return Err(Error::InvalidFormat(format!(
                "Unsupported CAR version: {}",
                version
            )));
        }

        // Number of roots
        let (num_roots, read) = decode_varint(&data[offset..])?;
        offset += read;

        // Each root CID
        let mut roots = Vec::with_capacity(num_roots as usize);
        for _ in 0..num_roots {
            let (cid_len, read) = decode_varint(&data[offset..])?;
            offset += read;

            let cid_str = std::str::from_utf8(&data[offset..offset + cid_len as usize])
                .map_err(|e| Error::InvalidFormat(e.to_string()))?;
            offset += cid_len as usize;

            let cid = ContentId::from_string(cid_str)
                .map_err(|e| Error::InvalidCid(e.to_string()))?;
            roots.push(cid);
        }

        Ok((Self { version, roots }, offset))
    }
}

/// A block in a CAR file
#[derive(Debug, Clone)]
pub struct CarBlock {
    /// Content ID
    pub cid: ContentId,
    /// Block data
    pub data: Vec<u8>,
}

impl CarBlock {
    /// Create a new block from data
    pub fn new(data: Vec<u8>) -> Self {
        let cid = ContentId::from_content(&data);
        Self { cid, data }
    }

    /// Create a block with pre-computed CID
    pub fn with_cid(cid: ContentId, data: Vec<u8>) -> Self {
        Self { cid, data }
    }

    /// Verify that data matches CID
    pub fn verify(&self) -> bool {
        self.cid.verify(&self.data)
    }

    /// Encode block to bytes
    pub fn encode(&self) -> Vec<u8> {
        let mut buf = Vec::new();

        // CID
        let cid_bytes = self.cid.to_string_repr().into_bytes();
        buf.extend(encode_varint(cid_bytes.len() as u64));
        buf.extend(&cid_bytes);

        // Data
        buf.extend(encode_varint(self.data.len() as u64));
        buf.extend(&self.data);

        buf
    }

    /// Decode block from bytes
    pub fn decode(data: &[u8]) -> Result<(Self, usize)> {
        let mut offset = 0;

        // CID
        let (cid_len, read) = decode_varint(&data[offset..])?;
        offset += read;

        let cid_str = std::str::from_utf8(&data[offset..offset + cid_len as usize])
            .map_err(|e| Error::InvalidFormat(e.to_string()))?;
        offset += cid_len as usize;

        let mut cid = ContentId::from_string(cid_str)
            .map_err(|e| Error::InvalidCid(e.to_string()))?;

        // Data
        let (data_len, read) = decode_varint(&data[offset..])?;
        offset += read;

        let block_data = data[offset..offset + data_len as usize].to_vec();
        offset += data_len as usize;

        // Set the size field since it's not encoded in the CID string
        cid.size = block_data.len() as u64;

        Ok((Self { cid, data: block_data }, offset))
    }
}

/// CAR file builder
pub struct CarBuilder {
    /// Header with roots
    header: CarHeader,
    /// Blocks in order
    blocks: Vec<CarBlock>,
    /// Block index by CID
    index: HashMap<String, usize>,
}

impl CarBuilder {
    /// Create a new CAR builder with a root CID
    pub fn new(root: ContentId) -> Self {
        Self {
            header: CarHeader::new(root),
            blocks: Vec::new(),
            index: HashMap::new(),
        }
    }

    /// Add a block to the CAR file
    pub fn add_block(&mut self, block: CarBlock) {
        let cid_str = block.cid.to_string_repr();
        if !self.index.contains_key(&cid_str) {
            self.index.insert(cid_str, self.blocks.len());
            self.blocks.push(block);
        }
    }

    /// Add content and return its CID
    pub fn add_content(&mut self, data: Vec<u8>) -> ContentId {
        let block = CarBlock::new(data);
        let cid = block.cid.clone();
        self.add_block(block);
        cid
    }

    /// Build the CAR file
    pub fn build(self) -> CarFile {
        CarFile {
            header: self.header,
            blocks: self.blocks,
        }
    }

    /// Number of blocks
    pub fn num_blocks(&self) -> usize {
        self.blocks.len()
    }
}

/// Complete CAR file
#[derive(Debug, Clone)]
pub struct CarFile {
    /// Header
    pub header: CarHeader,
    /// Blocks
    pub blocks: Vec<CarBlock>,
}

impl CarFile {
    /// Create from content with automatic chunking
    pub fn from_content(data: &[u8]) -> Self {
        let root_cid = ContentId::from_content(data);
        let mut builder = CarBuilder::new(root_cid.clone());

        // For large files, we would chunk here
        // For now, single block
        builder.add_block(CarBlock::with_cid(root_cid, data.to_vec()));

        builder.build()
    }

    /// Encode the entire CAR file to bytes
    pub fn encode(&self) -> Vec<u8> {
        let mut buf = Vec::new();

        // Header with length prefix
        let header_bytes = self.header.encode();
        buf.extend(encode_varint(header_bytes.len() as u64));
        buf.extend(&header_bytes);

        // Blocks
        for block in &self.blocks {
            let block_bytes = block.encode();
            buf.extend(&block_bytes);
        }

        buf
    }

    /// Decode a CAR file from bytes
    pub fn decode(data: &[u8]) -> Result<Self> {
        let mut offset = 0;

        // Header length
        let (header_len, read) = decode_varint(&data[offset..])?;
        offset += read;

        // Header
        let (header, _) = CarHeader::decode(&data[offset..offset + header_len as usize])?;
        offset += header_len as usize;

        // Blocks
        let mut blocks = Vec::new();
        while offset < data.len() {
            let (block, read) = CarBlock::decode(&data[offset..])?;
            blocks.push(block);
            offset += read;
        }

        Ok(Self { header, blocks })
    }

    /// Verify all blocks in the CAR file
    pub fn verify(&self) -> Result<bool> {
        for block in &self.blocks {
            if !block.verify() {
                return Ok(false);
            }
        }
        Ok(true)
    }

    /// Get the root CID(s)
    pub fn roots(&self) -> &[ContentId] {
        &self.header.roots
    }

    /// Get a block by CID
    pub fn get_block(&self, cid: &ContentId) -> Option<&CarBlock> {
        let cid_str = cid.to_string_repr();
        self.blocks.iter().find(|b| b.cid.to_string_repr() == cid_str)
    }

    /// Total size in bytes
    pub fn total_size(&self) -> u64 {
        self.blocks.iter().map(|b| b.data.len() as u64).sum()
    }

    /// Number of blocks
    pub fn num_blocks(&self) -> usize {
        self.blocks.len()
    }
}

/// Response with CAR file and verification headers
#[derive(Debug, Clone)]
pub struct TrustlessResponse {
    /// CAR file data
    pub car_data: Vec<u8>,
    /// Root CID string for X-Ipfs-Roots header
    pub root_cid: String,
    /// Content type
    pub content_type: String,
    /// Whether verification passed
    pub verified: bool,
}

impl TrustlessResponse {
    /// Create a trustless response from content
    pub fn from_content(data: &[u8]) -> Self {
        let car_file = CarFile::from_content(data);
        let root_cid = car_file.roots().first()
            .map(|c| c.to_string_repr())
            .unwrap_or_default();

        let verified = car_file.verify().unwrap_or(false);
        let car_data = car_file.encode();

        Self {
            car_data,
            root_cid,
            content_type: "application/vnd.ipld.car".to_string(),
            verified,
        }
    }

    /// Get HTTP headers for trustless response
    pub fn headers(&self) -> HashMap<String, String> {
        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), self.content_type.clone());
        headers.insert("X-Ipfs-Roots".to_string(), self.root_cid.clone());
        headers.insert("X-Content-Verified".to_string(), self.verified.to_string());
        headers.insert(
            "Cache-Control".to_string(),
            "public, max-age=31536000, immutable".to_string(),
        );
        headers
    }
}

/// Encode a varint (unsigned LEB128)
fn encode_varint(mut value: u64) -> Vec<u8> {
    let mut buf = Vec::new();
    loop {
        let mut byte = (value & 0x7f) as u8;
        value >>= 7;
        if value != 0 {
            byte |= 0x80;
        }
        buf.push(byte);
        if value == 0 {
            break;
        }
    }
    buf
}

/// Decode a varint (unsigned LEB128)
fn decode_varint(data: &[u8]) -> Result<(u64, usize)> {
    let mut value = 0u64;
    let mut shift = 0;
    let mut bytes_read = 0;

    for &byte in data {
        bytes_read += 1;
        value |= ((byte & 0x7f) as u64) << shift;
        if byte & 0x80 == 0 {
            break;
        }
        shift += 7;
        if shift >= 64 {
            return Err(Error::InvalidFormat("Varint overflow".to_string()));
        }
    }

    Ok((value, bytes_read))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_varint_roundtrip() {
        let values = [0, 1, 127, 128, 255, 256, 16383, 16384, u64::MAX];
        for &val in &values {
            let encoded = encode_varint(val);
            let (decoded, _) = decode_varint(&encoded).unwrap();
            assert_eq!(val, decoded, "Failed for value {}", val);
        }
    }

    #[test]
    fn test_car_block_verify() {
        let data = b"Hello, CAR!".to_vec();
        let block = CarBlock::new(data.clone());

        assert!(block.verify());

        // Tampered block should fail
        let tampered = CarBlock::with_cid(block.cid.clone(), b"Tampered!".to_vec());
        assert!(!tampered.verify());
    }

    #[test]
    fn test_car_file_roundtrip() {
        let data = b"Test content for CAR file";
        let car = CarFile::from_content(data);

        assert_eq!(car.num_blocks(), 1);
        assert!(car.verify().unwrap());

        // Encode and decode
        let encoded = car.encode();
        let decoded = CarFile::decode(&encoded).unwrap();

        assert_eq!(car.num_blocks(), decoded.num_blocks());
        assert!(decoded.verify().unwrap());
    }

    #[test]
    fn test_car_builder() {
        let root = ContentId::from_content(b"root");
        let mut builder = CarBuilder::new(root.clone());

        let cid1 = builder.add_content(b"block 1".to_vec());
        let _cid2 = builder.add_content(b"block 2".to_vec());

        // Adding same content again should not create new block
        let cid1_again = builder.add_content(b"block 1".to_vec());

        assert_eq!(builder.num_blocks(), 2);
        assert_eq!(cid1.to_string_repr(), cid1_again.to_string_repr());
    }

    #[test]
    fn test_trustless_response() {
        let data = b"Trustless verification test";
        let response = TrustlessResponse::from_content(data);

        assert!(response.verified);
        assert!(!response.root_cid.is_empty());
        assert_eq!(response.content_type, "application/vnd.ipld.car");

        let headers = response.headers();
        assert!(headers.contains_key("X-Ipfs-Roots"));
        assert!(headers.contains_key("Cache-Control"));
    }

    #[test]
    fn test_car_header_encode_decode() {
        let root = ContentId::from_content(b"header test");
        let header = CarHeader::new(root);

        let encoded = header.encode();
        let (decoded, _) = CarHeader::decode(&encoded).unwrap();

        assert_eq!(decoded.version, CAR_VERSION);
        assert_eq!(decoded.roots.len(), 1);
    }
}
