use std::fs;
use std::path::{Path, PathBuf};

pub(crate) const SPECIAL_CONDITIONS_OFFSET: usize = 0x0d64;
pub(crate) const SPECIAL_CONDITION_COUNT: usize = 36;
pub(crate) const SPECIAL_CONDITION_HIDDEN_SENTINEL_SLOT: usize = 35;
pub(crate) const SMP_ALIGNED_RUNTIME_RULE_DWORD_COUNT: usize = 50;
pub(crate) const SMP_ALIGNED_RUNTIME_RULE_KNOWN_EDITOR_RULE_COUNT: usize = 49;
pub(crate) const SMP_ALIGNED_RUNTIME_RULE_END_OFFSET: usize =
    SPECIAL_CONDITIONS_OFFSET + SMP_ALIGNED_RUNTIME_RULE_DWORD_COUNT * 4;
pub(crate) const POST_SPECIAL_CONDITIONS_SCALAR_OFFSET: usize = 0x0df4;
pub(crate) const POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET: usize = 0x0f30;
pub(crate) const POST_SPECIAL_CONDITIONS_SCALAR_TAIL_OFFSET: usize =
    SMP_ALIGNED_RUNTIME_RULE_END_OFFSET;
pub(crate) const SPECIAL_CONDITION_LABELS: [&str; SPECIAL_CONDITION_COUNT] = [
    "Disable Stock Buying and Selling",
    "Disable Margin Buying/Short Selling Stock",
    "Disable Company Issue/Buy Back Stock",
    "Disable Issuing/Repaying Bonds",
    "Disable Declaring Bankruptcy",
    "Disable Changing the Dividend Rate",
    "Disable Replacing a Locomotive",
    "Disable Retiring a Train",
    "Disable Changing Cargo Consist On Train",
    "Disable Buying a Train",
    "Disable All Track Building",
    "Disable Unconnected Track Building",
    "Limited Track Building Amount",
    "Disable Building Stations",
    "Disable Building Hotel/Restaurant/Tavern/Post Office",
    "Disable Building Customs House",
    "Disable Building Industry Buildings",
    "Disable Buying Existing Industry Buildings",
    "Disable Being Fired As Chairman",
    "Disable Resigning as Chairman",
    "Disable Chairmanship Takeover",
    "Disable Starting Any Companies",
    "Disable Starting Multiple Companies",
    "Disable Merging Companies",
    "Disable Bulldozing",
    "Show Visited Track",
    "Show Visited Stations",
    "Use Slow Date",
    "Completely Disable Money-Related Things",
    "Use Bio-Accelerator Cars",
    "Disable Cargo Economy",
    "Use Wartime Cargos",
    "Disable Train Crashes",
    "Disable Train Crashes AND Breakdowns",
    "AI Ignore Territories At Startup",
    "Hidden sentinel",
];

pub(crate) fn collect_special_conditions_input_paths(
    root_path: &Path,
    out: &mut Vec<PathBuf>,
) -> Result<(), Box<dyn std::error::Error>> {
    let metadata = match fs::symlink_metadata(root_path) {
        Ok(metadata) => metadata,
        Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => return Ok(()),
        Err(err) => return Err(err.into()),
    };
    if metadata.file_type().is_symlink() {
        return Ok(());
    }

    if root_path.is_file() {
        if root_path
            .extension()
            .and_then(|ext| ext.to_str())
            .is_some_and(|ext| matches!(ext.to_ascii_lowercase().as_str(), "gmp" | "gms" | "gmx"))
        {
            out.push(root_path.to_path_buf());
        }
        return Ok(());
    }

    let entries = match fs::read_dir(root_path) {
        Ok(entries) => entries,
        Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => return Ok(()),
        Err(err) => return Err(err.into()),
    };

    for entry in entries {
        let entry = entry?;
        let path = entry.path();
        if path.is_dir() {
            collect_special_conditions_input_paths(&path, out)?;
            continue;
        }
        if path
            .extension()
            .and_then(|ext| ext.to_str())
            .is_some_and(|ext| matches!(ext.to_ascii_lowercase().as_str(), "gmp" | "gms" | "gmx"))
        {
            out.push(path);
        }
    }

    Ok(())
}

pub(crate) fn parse_special_condition_slot_index(label: &str) -> Option<u8> {
    let suffix = label.strip_prefix("slot ")?;
    let (slot_index, _) = suffix.split_once(':')?;
    slot_index.parse().ok()
}

pub(crate) fn parse_hex_offset(text: &str) -> Option<usize> {
    text.strip_prefix("0x")
        .and_then(|digits| usize::from_str_radix(digits, 16).ok())
}

pub(crate) fn aligned_runtime_rule_lane_kind(band_index: usize) -> &'static str {
    if band_index < SPECIAL_CONDITION_COUNT {
        "known-special-condition-dword"
    } else if band_index < SMP_ALIGNED_RUNTIME_RULE_KNOWN_EDITOR_RULE_COUNT {
        "unlabeled-editor-rule-dword"
    } else {
        "trailing-runtime-scalar"
    }
}

pub(crate) fn aligned_runtime_rule_known_label(band_index: usize) -> Option<&'static str> {
    if band_index < SPECIAL_CONDITION_LABELS.len() {
        Some(SPECIAL_CONDITION_LABELS[band_index])
    } else {
        None
    }
}
