//! ISO/IEC 7816-4 Type 4 Tag operations for NFC Forum compliance.
//!
//! APDU builders for SELECT, ReadBinary, UpdateBinary, and parsers for
//! Capability Container (CC) and NDEF file content. Pure functions — no
//! transport dependency.

/// NDEF Tag Application AID (D2760000850101).
pub const NDEF_APP_AID: [u8; 7] = [0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01];

pub const CC_FILE_FID: u16 = 0xE103;
pub const NDEF_FILE_FID: u16 = 0xE104;

// ---------------------------------------------------------------------------
// APDU builders
// ---------------------------------------------------------------------------

/// ISO SELECT by DF name (P1=04, P2=0C — no FCI returned).
pub fn cmd_select_application(aid: &[u8]) -> Vec<u8> {
    let mut apdu = vec![0x00, 0xA4, 0x04, 0x0C, aid.len() as u8];
    apdu.extend_from_slice(aid);
    apdu.push(0x00); // Le
    apdu
}

/// ISO SELECT by file ID (P1=00, P2=0C — no FCI returned).
pub fn cmd_select_file(fid: u16) -> Vec<u8> {
    vec![
        0x00, 0xA4, 0x00, 0x0C, 0x02,
        (fid >> 8) as u8,
        (fid & 0xFF) as u8,
    ]
}

/// Convenience: SELECT NDEF application.
pub fn cmd_select_ndef_app() -> Vec<u8> {
    cmd_select_application(&NDEF_APP_AID)
}

/// Convenience: SELECT CC file (E103h).
pub fn cmd_select_cc_file() -> Vec<u8> {
    cmd_select_file(CC_FILE_FID)
}

/// Convenience: SELECT NDEF file (E104h).
pub fn cmd_select_ndef_file() -> Vec<u8> {
    cmd_select_file(NDEF_FILE_FID)
}

/// ISO ReadBinary from current selected file.
/// `offset` is 15-bit (max 0x7FFF). `length` is Le byte (0 = 256).
pub fn cmd_read_binary(offset: u16, length: u8) -> Vec<u8> {
    vec![
        0x00,
        0xB0,
        (offset >> 8) as u8 & 0x7F,
        (offset & 0xFF) as u8,
        length,
    ]
}

/// ISO UpdateBinary to current selected file.
/// `offset` is 15-bit. `data` max 255 bytes per command.
pub fn cmd_update_binary(offset: u16, data: &[u8]) -> Vec<u8> {
    let mut apdu = vec![
        0x00,
        0xD6,
        (offset >> 8) as u8 & 0x7F,
        (offset & 0xFF) as u8,
        data.len() as u8,
    ];
    apdu.extend_from_slice(data);
    apdu
}

/// Check if an ISO response indicates success (SW=9000).
pub fn is_iso_success(response: &[u8]) -> bool {
    response.len() >= 2
        && response[response.len() - 2] == 0x90
        && response[response.len() - 1] == 0x00
}

/// Extract payload from a successful ISO response (strip SW 9000).
pub fn extract_iso_payload(response: &[u8]) -> Result<Vec<u8>, String> {
    if !is_iso_success(response) {
        if response.len() >= 2 {
            return Err(format!(
                "ISO command failed: SW={:02X}{:02X}",
                response[response.len() - 2],
                response[response.len() - 1]
            ));
        }
        return Err("ISO response too short".to_string());
    }
    Ok(response[..response.len() - 2].to_vec())
}

// ---------------------------------------------------------------------------
// Capability Container parser
// ---------------------------------------------------------------------------

/// Parsed NFC Forum Type 4 Capability Container.
#[derive(Debug, Clone)]
pub struct CapabilityContainer {
    pub cc_len: u16,
    pub mapping_version: u8,
    pub max_le: u16,
    pub max_lc: u16,
    pub ndef_file_control: Option<NdefFileControl>,
}

/// NDEF File Control TLV from CC.
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NdefFileControl {
    pub file_id: u16,
    pub max_ndef_size: u16,
    pub read_access: u8,
    pub write_access: u8,
}

/// Parse a CC file payload (minimum 7 bytes header + optional TLVs).
pub fn parse_cc_file(data: &[u8]) -> Result<CapabilityContainer, String> {
    if data.len() < 7 {
        return Err(format!("CC file too short: {} bytes", data.len()));
    }
    let cc_len = u16::from_be_bytes([data[0], data[1]]);
    let mapping_version = data[2];
    let max_le = u16::from_be_bytes([data[3], data[4]]);
    let max_lc = u16::from_be_bytes([data[5], data[6]]);

    let mut ndef_file_control = None;
    // Parse NDEF File Control TLV (tag 0x04, length 0x06)
    if data.len() >= 15 && data[7] == 0x04 && data[8] >= 0x06 {
        let fid = u16::from_be_bytes([data[9], data[10]]);
        let max_size = u16::from_be_bytes([data[11], data[12]]);
        ndef_file_control = Some(NdefFileControl {
            file_id: fid,
            max_ndef_size: max_size,
            read_access: data[13],
            write_access: data[14],
        });
    }

    Ok(CapabilityContainer {
        cc_len,
        mapping_version,
        max_le,
        max_lc,
        ndef_file_control,
    })
}

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

    #[test]
    fn select_ndef_app_apdu() {
        let apdu = cmd_select_ndef_app();
        assert_eq!(
            apdu,
            vec![0x00, 0xA4, 0x04, 0x0C, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00]
        );
    }

    #[test]
    fn select_file_apdu() {
        let apdu = cmd_select_file(0xE104);
        assert_eq!(apdu, vec![0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x04]);
    }

    #[test]
    fn read_binary_apdu() {
        let apdu = cmd_read_binary(0, 32);
        assert_eq!(apdu, vec![0x00, 0xB0, 0x00, 0x00, 0x20]);
    }

    #[test]
    fn cc_file_parse() {
        // Typical NTAG 424 DNA CC: 32 bytes
        let mut cc = vec![
            0x00, 0x20, // CCLEN=32
            0x20, // mapping v2.0
            0x01, 0x00, // MLe=256
            0x00, 0xFF, // MLc=255
            0x04, 0x06, // NDEF File Control TLV
            0xE1, 0x04, // FID
            0x01, 0x00, // max NDEF size = 256
            0x00, // read access = free
            0x00, // write access = free
        ];
        cc.resize(32, 0x00);
        let parsed = parse_cc_file(&cc).unwrap();
        assert_eq!(parsed.cc_len, 32);
        assert_eq!(parsed.mapping_version, 0x20);
        assert_eq!(parsed.max_le, 256);
        assert_eq!(parsed.max_lc, 255);
        let nfc = parsed.ndef_file_control.unwrap();
        assert_eq!(nfc.file_id, 0xE104);
        assert_eq!(nfc.max_ndef_size, 256);
    }
}
