use std::error::Error;
use std::fs;
use std::path::Path;

use super::header::{WIN_COMMON_HEADER_LEN, analyze_common_header};
use super::references::{
    build_delta_histogram, build_resource_record_samples, collect_imb_references,
    shared_prelude_prefix_hex,
};
use super::selectors::{build_resource_selector_records, collect_anonymous_selector_records};
use super::types::WinInspectionReport;

pub fn inspect_win_file(path: &Path) -> Result<WinInspectionReport, Box<dyn Error>> {
    let bytes = fs::read(path)?;
    inspect_win_bytes(&bytes)
}

pub fn inspect_win_bytes(bytes: &[u8]) -> Result<WinInspectionReport, Box<dyn Error>> {
    if bytes.len() < WIN_COMMON_HEADER_LEN {
        return Err(format!(
            "window resource is too short for the observed common header: {} < 0x{WIN_COMMON_HEADER_LEN:x}",
            bytes.len()
        )
        .into());
    }

    let header = analyze_common_header(bytes);
    let all_imb_references = collect_imb_references(bytes);
    let resource_record_samples = build_resource_record_samples(bytes, &all_imb_references);
    let resource_selector_records = build_resource_selector_records(&resource_record_samples);
    let anonymous_selector_records = collect_anonymous_selector_records(bytes, &all_imb_references);
    let common_resource_record_prelude_prefix_words =
        shared_prelude_prefix_hex(&resource_record_samples);
    let name_len_matches_prelude_word_3_plus_nul_count = resource_record_samples
        .iter()
        .filter(|sample| {
            sample.prelude_words.len() == 4
                && sample.prelude_words[3].value == (sample.name.len() as u32 + 1)
        })
        .count();
    let mut unique_imb_references = Vec::new();
    for reference in &all_imb_references {
        if !unique_imb_references.contains(&reference.name) {
            unique_imb_references.push(reference.name.clone());
        }
    }

    let mut notes = Vec::new();
    if header.matches_observed_common_signature {
        notes.push(
            "Header matches the observed shared .win signature seen in Campaign.win, CompanyDetail.win, and setup.win."
                .to_string(),
        );
    } else {
        notes.push(
            "Header diverges from the currently observed shared .win signature; treat field meanings as provisional."
                .to_string(),
        );
    }
    if header.inline_root_resource_name.is_some() {
        notes.push(
            "The blob carries an inline root .imb resource name immediately after the common 0x50-byte header."
                .to_string(),
        );
    } else {
        notes.push(
            "No inline root .imb resource name appears at 0x50; this window likely starts directly with control records."
                .to_string(),
        );
    }
    notes.push(
        "Embedded .imb strings are reported as resource references with selector lanes; this inspector still does not decode full control record semantics."
            .to_string(),
    );

    Ok(WinInspectionReport {
        file_size: bytes.len(),
        common_header_len: WIN_COMMON_HEADER_LEN,
        common_header_len_hex: format!("0x{WIN_COMMON_HEADER_LEN:02x}"),
        shared_header_words: header.shared_header_words,
        matches_observed_common_signature: header.matches_observed_common_signature,
        common_resource_record_prelude_prefix_words,
        name_len_matches_prelude_word_3_plus_nul_count,
        inline_root_resource_name: header.inline_root_resource_name,
        inline_root_resource_offset: header.inline_root_resource_offset,
        inline_root_resource_offset_hex: header.inline_root_resource_offset_hex,
        imb_reference_count: all_imb_references.len(),
        unique_imb_reference_count: unique_imb_references.len(),
        unique_imb_references,
        dominant_reference_deltas: build_delta_histogram(&resource_record_samples),
        resource_selector_records,
        anonymous_selector_records,
        first_resource_record_samples: resource_record_samples.into_iter().take(32).collect(),
        first_imb_references: all_imb_references.into_iter().take(32).collect(),
        notes,
    })
}
