use super::contracts::{ambiguous_company_prompt, ResolvedOrg};
use super::org_units::{OrgUnit, ORG_UNITS};

fn normalize(value: &str) -> String {
    value.chars().filter(|ch| !ch.is_whitespace()).collect()
}

fn candidate_names(unit: &'static OrgUnit) -> impl Iterator<Item = &'static str> {
    std::iter::once(unit.label).chain(unit.aliases.iter().copied())
}

fn to_resolved_org(unit: &OrgUnit) -> ResolvedOrg {
    ResolvedOrg {
        label: unit.label.to_string(),
        code: unit.code.to_string(),
    }
}

pub fn resolve_org(input: &str) -> Result<ResolvedOrg, String> {
    let normalized = normalize(input);
    if normalized.is_empty() {
        return Err(super::contracts::missing_company_prompt());
    }

    let exact_matches: Vec<&OrgUnit> = ORG_UNITS
        .iter()
        .filter(|unit| candidate_names(unit).any(|name| normalize(name) == normalized))
        .collect();
    if exact_matches.len() == 1 {
        return Ok(to_resolved_org(exact_matches[0]));
    }
    if exact_matches.len() > 1 {
        return Err(ambiguous_company_prompt());
    }

    let fuzzy_matches: Vec<&OrgUnit> = ORG_UNITS
        .iter()
        .filter(|unit| {
            candidate_names(unit).any(|name| {
                let normalized_name = normalize(name);
                normalized_name.contains(&normalized) || normalized.contains(&normalized_name)
            })
        })
        .collect();
    if fuzzy_matches.len() == 1 {
        return Ok(to_resolved_org(fuzzy_matches[0]));
    }
    if fuzzy_matches.len() > 1 {
        return Err(ambiguous_company_prompt());
    }

    Err(super::contracts::missing_company_prompt())
}

pub fn resolve_org_from_instruction(instruction: &str) -> Result<Option<ResolvedOrg>, String> {
    let normalized_instruction = normalize(instruction);
    let direct_matches: Vec<&OrgUnit> = ORG_UNITS
        .iter()
        .filter(|unit| {
            candidate_names(unit).any(|name| normalized_instruction.contains(&normalize(name)))
        })
        .collect();
    if direct_matches.len() == 1 {
        return Ok(Some(to_resolved_org(direct_matches[0])));
    }
    if direct_matches.len() > 1 {
        return Err(ambiguous_company_prompt());
    }

    Ok(None)
}
