use crate::terminal::Frequency;

use super::super::types::EarningsPeriodKey;
use super::facts::{find_text_fact, FilingXbrlDocument};

const DOCUMENT_FISCAL_YEAR_FOCUS: (&str, &str) = ("dei", "DocumentFiscalYearFocus");
const DOCUMENT_FISCAL_PERIOD_FOCUS: (&str, &str) = ("dei", "DocumentFiscalPeriodFocus");
const DOCUMENT_PERIOD_END_DATE: (&str, &str) = ("dei", "DocumentPeriodEndDate");

/// Helper to extract fiscal year from period end date
fn extract_fiscal_year_from_period_end(period_end: &str) -> Option<i32> {
    // Period end dates are typically YYYY-MM-DD
    // For fiscal year, we need to determine which fiscal year this belongs to
    // This is a simplified approach - assumes calendar year alignment
    period_end.split('-').next()?.parse::<i32>().ok()
}

/// Helper to infer fiscal period from filing form and period dates
fn infer_fiscal_period_from_duration(
    _period_start: &str,
    period_end: &str,
    frequency: Frequency,
) -> Option<String> {
    match frequency {
        Frequency::Annual => Some("FY".to_string()),
        Frequency::Quarterly => {
            // Try to determine quarter from the period end date month
            let month = period_end.split('-').nth(1)?;
            match month {
                "03" | "04" => Some("Q1".to_string()),
                "06" | "07" => Some("Q2".to_string()),
                "09" | "10" => Some("Q3".to_string()),
                "12" | "01" => Some("Q4".to_string()),
                _ => None,
            }
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) struct PeriodIdentity {
    pub key: EarningsPeriodKey,
    pub fiscal_year: String,
    pub fiscal_period: Option<String>,
    pub period_end: String,
    pub display_label: String,
}

pub(crate) fn extract_period_identity(
    document: &FilingXbrlDocument,
    frequency: Frequency,
) -> Option<PeriodIdentity> {
    // Try to extract DEI facts first
    let fiscal_year_from_dei = find_text_fact(
        document,
        DOCUMENT_FISCAL_YEAR_FOCUS.0,
        DOCUMENT_FISCAL_YEAR_FOCUS.1,
    )
    .and_then(|value| value.trim().parse::<i32>().ok());

    let fiscal_period_from_dei = find_text_fact(
        document,
        DOCUMENT_FISCAL_PERIOD_FOCUS.0,
        DOCUMENT_FISCAL_PERIOD_FOCUS.1,
    )
    .map(|value| value.trim().to_ascii_uppercase());

    let period_end_from_dei = find_text_fact(
        document,
        DOCUMENT_PERIOD_END_DATE.0,
        DOCUMENT_PERIOD_END_DATE.1,
    )
    .map(|value| value.trim().to_string());

    // Find a duration context to extract fallback period information
    let duration_context = document
        .contexts
        .values()
        .find(|ctx| ctx.period_start.is_some() && ctx.period_end.is_some());

    // Determine fiscal year (prefer DEI, fallback to period end extraction)
    let fiscal_year = fiscal_year_from_dei.or_else(|| {
        period_end_from_dei
            .as_ref()
            .or_else(|| duration_context.and_then(|ctx| ctx.period_end.as_ref()))
            .and_then(|period_end| extract_fiscal_year_from_period_end(period_end))
    })?;

    // Determine period end (prefer DEI, fallback to context)
    let period_end = period_end_from_dei
        .or_else(|| duration_context.and_then(|ctx| ctx.period_end.clone()))
        .unwrap_or_default();

    // Determine fiscal period (prefer DEI, fallback to inference)
    let fiscal_period = fiscal_period_from_dei.or_else(|| {
        duration_context.and_then(|ctx| {
            let period_start = ctx.period_start.as_ref()?;
            infer_fiscal_period_from_duration(period_start, &period_end, frequency)
        })
    });

    // Build the period key
    let key = match frequency {
        Frequency::Annual => {
            let period = fiscal_period.as_deref().unwrap_or("FY");
            (period == "FY").then_some(EarningsPeriodKey::Annual { fiscal_year })?
        }
        Frequency::Quarterly => {
            let quarter = fiscal_period.as_deref().and_then(parse_quarter)?;
            EarningsPeriodKey::Quarterly {
                fiscal_year,
                quarter,
            }
        }
    };

    Some(PeriodIdentity {
        key,
        fiscal_year: fiscal_year.to_string(),
        fiscal_period: fiscal_period.clone(),
        display_label: display_label(
            fiscal_year,
            fiscal_period.as_deref(),
            &period_end,
            frequency,
        ),
        period_end,
    })
}

pub(crate) fn display_label(
    fiscal_year: i32,
    fiscal_period: Option<&str>,
    period_end: &str,
    frequency: Frequency,
) -> String {
    let month_suffix = month_abbreviation(period_end)
        .map(|month| format!(" ({month})"))
        .unwrap_or_default();

    match frequency {
        Frequency::Annual => format!("FY{fiscal_year}{month_suffix}"),
        Frequency::Quarterly => match fiscal_period {
            Some(period) if period.starts_with('Q') => {
                format!("FY{fiscal_year} {period}{month_suffix}")
            }
            _ => period_end.to_string(),
        },
    }
}

pub(crate) fn parse_quarter(value: &str) -> Option<u8> {
    value
        .strip_prefix('Q')
        .and_then(|quarter| quarter.parse::<u8>().ok())
        .filter(|quarter| (1..=4).contains(quarter))
}

fn month_abbreviation(value: &str) -> Option<&'static str> {
    let month = value.split('-').nth(1)?;
    match month {
        "01" => Some("Jan"),
        "02" => Some("Feb"),
        "03" => Some("Mar"),
        "04" => Some("Apr"),
        "05" => Some("May"),
        "06" => Some("Jun"),
        "07" => Some("Jul"),
        "08" => Some("Aug"),
        "09" => Some("Sep"),
        "10" => Some("Oct"),
        "11" => Some("Nov"),
        "12" => Some("Dec"),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use crate::terminal::Frequency;

    use super::*;

    #[test]
    fn display_label_should_include_month_for_annual_periods() {
        assert_eq!(
            display_label(2024, Some("FY"), "2024-09-28", Frequency::Annual),
            "FY2024 (Sep)"
        );
    }

    #[test]
    fn parse_quarter_should_extract_quarter_number() {
        assert_eq!(parse_quarter("Q3"), Some(3));
    }
}
