mod common;

use sqpack_rs::Index2;
use sqpack_rs::header::SqPackFileType;

const SMALL_INDEX2_NAMES: &[&str] = &[
    "120000.win32.index2",
    "130000.win32.index2",
    "000000.win32.index2",
    "050000.win32.index2",
    "0c0000.win32.index2",
];

#[test]
#[ignore = "requires real FFXIV .index2 file; run with `cargo test -- --ignored`"]
fn opens_and_parses_small_index2() {
    let Some(dir) = common::maybe_data_dir() else {
        eprintln!("skipping: no sqpack data available (set SQPACK_TEST_DATA_DIR)");
        return;
    };
    let path = dir.join("000000.win32.index2");
    let idx = Index2::open(&path).expect("Index2::open should succeed on a valid file");

    assert_eq!(
        idx.header().magic,
        sqpack_rs::header::SQPACK_MAGIC,
        "SqPack magic mismatch"
    );
    assert_eq!(
        idx.header().typed_file_type(),
        SqPackFileType::Index,
        "file_type should be Index (2)"
    );
    assert!(
        !idx.is_empty(),
        "000000.win32.index2 should contain at least one entry"
    );
    assert_eq!(idx.source_path(), Some(path.as_path()));
}

#[test]
#[ignore = "requires real FFXIV .index2 file; run with `cargo test -- --ignored`"]
fn small_index2_entry_field_constraints() {
    let Some(dir) = common::maybe_data_dir() else {
        eprintln!("skipping: no sqpack data available (set SQPACK_TEST_DATA_DIR)");
        return;
    };
    let path = dir.join("000000.win32.index2");
    let idx = Index2::open(&path).expect("Index2::open failed");

    for entry in idx.entries() {
        assert!(
            entry.data_file_id() <= 7,
            "data_file_id out of 3-bit range: {}",
            entry.data_file_id()
        );
        assert!(
            entry.byte_offset() < 4 * 1024 * 1024 * 1024u64,
            "byte_offset implausibly large: {:#x}",
            entry.byte_offset()
        );
        let loc = entry.dat_location();
        assert_eq!(loc.data_file_id, entry.data_file_id());
        assert_eq!(loc.byte_offset, entry.byte_offset());
    }

    eprintln!("000000.win32.index2: {} entries", idx.len());
}

#[test]
#[ignore = "requires real FFXIV .index2 file; run with `cargo test -- --ignored`"]
fn small_index2_no_duplicate_hashes() {
    let Some(dir) = common::maybe_data_dir() else {
        eprintln!("skipping: no sqpack data available (set SQPACK_TEST_DATA_DIR)");
        return;
    };
    let path = dir.join("000000.win32.index2");
    let _idx = Index2::open(&path).expect("real index2 must parse without DuplicateHash");
}

#[test]
#[ignore = "requires real FFXIV .index2 file; run with `cargo test -- --ignored`"]
fn parses_several_small_index2_files() {
    let Some(dir) = common::maybe_data_dir() else {
        eprintln!("skipping: no sqpack data available (set SQPACK_TEST_DATA_DIR)");
        return;
    };

    for name in SMALL_INDEX2_NAMES {
        let path = dir.join(name);
        if !path.exists() {
            eprintln!("skipping {name}: file not present");
            continue;
        }
        let idx =
            Index2::open(&path).unwrap_or_else(|e| panic!("{name}: Index2::open failed: {e}"));

        assert_eq!(
            idx.header().typed_file_type(),
            SqPackFileType::Index,
            "{name}: file_type mismatch"
        );

        for entry in idx.entries() {
            assert!(
                entry.data_file_id() <= 7,
                "{name}: data_file_id > 7 ({})",
                entry.data_file_id()
            );
            assert!(
                entry.byte_offset() < 4 * 1024 * 1024 * 1024u64,
                "{name}: byte_offset implausibly large: {:#x}",
                entry.byte_offset()
            );
        }

        eprintln!("{name}: {} entries", idx.len());
    }
}

#[test]
#[ignore = "requires real FFXIV .index2 file; run with `cargo test -- --ignored`"]
fn index2_header_fields_are_sensible() {
    let Some(dir) = common::maybe_data_dir() else {
        eprintln!("skipping: no sqpack data available (set SQPACK_TEST_DATA_DIR)");
        return;
    };
    let path = dir.join("000000.win32.index2");
    let idx = Index2::open(&path).expect("open failed");

    let ih = idx.index_header();
    assert_eq!(idx.header().size, 1024, "SqPack header size");
    assert_eq!(ih.size, 1024, "index header size");
    assert!(
        ih.index_data_offset >= 2048,
        "index_data_offset should be >= 2048, got {}",
        ih.index_data_offset
    );
    assert_eq!(
        ih.index_data_size % sqpack_rs::index2::INDEX2_ENTRY_SIZE,
        0,
        "index_data_size not aligned to entry size"
    );
    assert!(
        ih.number_of_data_file >= 1 && ih.number_of_data_file <= 8,
        "implausible number_of_data_file: {}",
        ih.number_of_data_file
    );
}

#[test]
#[ignore = "requires real FFXIV .index2 file; run with `cargo test -- --ignored`"]
fn reports_entry_counts_across_index2_files() {
    let Some(dir) = common::maybe_data_dir() else {
        eprintln!("skipping: no sqpack data available (set SQPACK_TEST_DATA_DIR)");
        return;
    };

    for name in SMALL_INDEX2_NAMES {
        let path = dir.join(name);
        if !path.exists() {
            continue;
        }
        let idx = Index2::open(&path).unwrap_or_else(|e| panic!("{name}: open failed: {e}"));
        eprintln!("{name}: total={}", idx.len());
    }
}
