mod common;

use std::path::PathBuf;

use chrono::{Datelike, Local};
use zeroclaw::skills::load_skills_from_directory;

use sgclaw::compat::deterministic_submit::{
    decide_deterministic_submit, DeterministicSubmitDecision,
};
use sgclaw::compat::tq_lineloss::{
    contracts::{PeriodMode, ResolvedOrg, ResolvedPeriod},
    org_resolver::resolve_org,
    period_resolver::resolve_period,
};
use sgclaw::runtime::is_zhihu_hotlist_task;

fn expected_default_month() -> String {
    let today = Local::now().date_naive();
    let (year, month) = if today.month() == 1 {
        (today.year() - 1, 12)
    } else {
        (today.year(), today.month() - 1)
    };
    format!("{year}-{month:02}")
}

fn expected_default_week_range() -> (String, String, String) {
    let today = Local::now().date_naive();
    let month_start = today.with_day(1).expect("current month should have day 1");
    let start = month_start.format("%Y-%m-%d").to_string();
    let end = today.format("%Y-%m-%d").to_string();
    (format!("{start}至{end}"), start, end)
}

#[test]
fn deterministic_submit_discovers_tq_lineloss_skill_contract() {
    let skills_root = PathBuf::from("D:/data/ideaSpace/rust/sgClaw/claw/claw/skills/skill_staging/skills");
    let skills = load_skills_from_directory(&skills_root, true);

    let skill = skills
        .iter()
        .find(|skill| skill.name == "tq-lineloss-report")
        .expect("tq-lineloss-report should be discoverable from staged skills root");

    let tool = skill
        .tools
        .iter()
        .find(|tool| tool.name == "collect_lineloss")
        .expect("collect_lineloss tool should be discoverable");

    assert_eq!(tool.kind, "browser_script");
    assert_eq!(tool.command, "scripts/collect_lineloss.js");

    let required_args = [
        "expected_domain",
        "org_label",
        "org_code",
        "period_mode",
        "period_mode_code",
        "period_value",
        "period_payload",
    ];

    for arg in required_args {
        assert!(
            tool.args.contains_key(arg),
            "expected required arg {arg} in tq-lineloss-report.collect_lineloss"
        );
    }

    assert_eq!(tool.args.len(), required_args.len());
}

#[test]
fn deterministic_submit_requires_exact_suffix() {
    assert!(matches!(
        decide_deterministic_submit("兰州公司 月累计 2026-03。。。", None, None),
        DeterministicSubmitDecision::Execute(_)
    ));

    assert!(matches!(
        decide_deterministic_submit("兰州公司 月累计 2026-03", None, None),
        DeterministicSubmitDecision::NotDeterministic
    ));
}

#[test]
fn deterministic_submit_nonmatch_returns_supported_scene_message() {
    let decision = decide_deterministic_submit("帮我打开百度。。。", None, None);

    match decision {
        DeterministicSubmitDecision::Prompt { summary } => {
            assert!(summary.contains("台区线损") || summary.contains("支持场景"));
        }
        other => panic!("expected deterministic prompt for unsupported scene, got {other:?}"),
    }
}

#[test]
fn deterministic_submit_rejects_page_context_mismatch() {
    let decision = decide_deterministic_submit(
        "兰州公司 月累计 2026-03。。。",
        Some("https://www.zhihu.com/hot"),
        Some("知乎热榜"),
    );

    match decision {
        DeterministicSubmitDecision::Prompt { summary } => {
            assert!(summary.contains("台区线损") || summary.contains("页面") || summary.contains("不匹配"));
        }
        other => panic!("expected deterministic mismatch prompt, got {other:?}"),
    }
}

#[test]
fn zhihu_hotlist_request_without_suffix_keeps_existing_route() {
    assert!(is_zhihu_hotlist_task(
        "打开知乎热榜",
        Some("https://www.zhihu.com/hot"),
        Some("知乎热榜")
    ));

    assert!(matches!(
        decide_deterministic_submit(
            "打开知乎热榜",
            Some("https://www.zhihu.com/hot"),
            Some("知乎热榜")
        ),
        DeterministicSubmitDecision::NotDeterministic
    ));
}

#[test]
fn deterministic_submit_rejects_non_exact_suffix_variants() {
    for instruction in [
        "兰州公司 月累计 2026-03...",
        "兰州公司 月累计 2026-03。。。。",
        "兰州公司。。。月累计 2026-03",
        "兰州公司 月累计 2026-03。。。 ",
    ] {
        assert!(matches!(
            decide_deterministic_submit(instruction, None, None),
            DeterministicSubmitDecision::NotDeterministic
        ));
    }
}

#[test]
fn lineloss_org_resolver_matches_city_alias() {
    assert_eq!(
        resolve_org("兰州公司").unwrap(),
        ResolvedOrg {
            label: "国网兰州供电公司".to_string(),
            code: "62401".to_string(),
        }
    );

    assert_eq!(
        resolve_org("天水公司").unwrap(),
        ResolvedOrg {
            label: "国网天水供电公司".to_string(),
            code: "62403".to_string(),
        }
    );
}

#[test]
fn lineloss_org_resolver_matches_county_alias() {
    assert_eq!(
        resolve_org("榆中县公司").unwrap(),
        ResolvedOrg {
            label: "国网榆中县供电公司".to_string(),
            code: "6240121".to_string(),
        }
    );

    assert_eq!(
        resolve_org("城关供电分公司").unwrap(),
        ResolvedOrg {
            label: "城关供电分公司".to_string(),
            code: "6240108".to_string(),
        }
    );
}

#[test]
fn lineloss_org_resolver_prompts_on_ambiguity() {
    let summary = resolve_org("城关")
        .expect_err("ambiguous alias should prompt instead of guessing");

    assert!(summary.contains("供电单位存在歧义") || summary.contains("更完整名称"));
}

#[test]
fn deterministic_submit_lineloss_missing_company_prompts() {
    let decision = decide_deterministic_submit("月累计 2026-03。。。", None, None);

    match decision {
        DeterministicSubmitDecision::Prompt { summary } => {
            assert!(summary.contains("缺少供电单位") || summary.contains("兰州公司"));
        }
        other => panic!("expected missing-company prompt, got {other:?}"),
    }
}

#[test]
fn lineloss_period_resolver_parses_month_text() {
    assert_eq!(
        resolve_period("月累计 2026-03").unwrap(),
        ResolvedPeriod {
            mode: PeriodMode::Month,
            mode_code: "1".to_string(),
            value: "2026-03".to_string(),
            payload: serde_json::json!({
                "fdate": "2026-03",
            }),
        }
    );

    assert_eq!(
        resolve_period("月累计 2026年3月").unwrap(),
        ResolvedPeriod {
            mode: PeriodMode::Month,
            mode_code: "1".to_string(),
            value: "2026-03".to_string(),
            payload: serde_json::json!({
                "fdate": "2026-03",
            }),
        }
    );
}

#[test]
fn lineloss_period_resolver_parses_week_text() {
    let resolved = resolve_period("周累计 2026年第12周").unwrap();

    assert_eq!(resolved.mode, PeriodMode::Week);
    assert_eq!(resolved.mode_code, "2");
    assert_eq!(resolved.value, "2026-W12");
    assert_eq!(resolved.payload["tjzq"], "week");
    assert_eq!(resolved.payload["level"], "00");
    assert_eq!(resolved.payload["weekSfdate"], "2026-03-16");
    assert_eq!(resolved.payload["weekEfdate"], "2026-03-22");
}

#[test]
fn lineloss_period_resolver_defaults_month_period_from_page_semantics() {
    let expected_month = expected_default_month();

    assert_eq!(
        resolve_period("兰州公司 月累计").unwrap(),
        ResolvedPeriod {
            mode: PeriodMode::Month,
            mode_code: "1".to_string(),
            value: expected_month.clone(),
            payload: serde_json::json!({
                "fdate": expected_month,
            }),
        }
    );
}

#[test]
fn lineloss_period_resolver_defaults_week_period_from_page_semantics() {
    let (expected_value, expected_start, expected_end) = expected_default_week_range();

    assert_eq!(
        resolve_period("兰州公司 周累计").unwrap(),
        ResolvedPeriod {
            mode: PeriodMode::Week,
            mode_code: "2".to_string(),
            value: expected_value,
            payload: serde_json::json!({
                "tjzq": "week",
                "level": "00",
                "weekSfdate": expected_start,
                "weekEfdate": expected_end,
            }),
        }
    );
}

#[test]
fn lineloss_period_resolver_prompts_for_missing_year_on_week() {
    let summary = resolve_period("周累计 第12周")
        .expect_err("bare week should prompt for year instead of guessing");

    assert!(summary.contains("年份") || summary.contains("第12周"));
}

#[test]
fn lineloss_period_resolver_rejects_contradictory_mode() {
    let summary = resolve_period("月累计 周累计 2026-03")
        .expect_err("contradictory month/week intent should not execute");

    assert!(summary.contains("月/周") || summary.contains("冲突") || summary.contains("歧义"));
}

#[test]
fn lineloss_period_resolver_prompts_for_missing_mode() {
    let summary = resolve_period("兰州公司 2026-03")
        .expect_err("missing mode should prompt instead of guessing");

    assert!(summary.contains("月/周类型") || summary.contains("月累计") || summary.contains("周累计"));
}

#[test]
fn lineloss_period_resolver_prompts_for_missing_period() {
    let summary = resolve_period("兰州公司 月累计")
        .expect_err("missing period should prompt instead of guessing");

    assert!(summary.contains("周期") || summary.contains("时间") || summary.contains("2026-03"));
}

#[test]
fn deterministic_lineloss_execution_plan_contains_canonical_args() {
    let decision = decide_deterministic_submit(
        "兰州公司 月累计 2026-03。。。",
        Some("http://20.76.57.61:8080/#/lineloss"),
        Some("台区线损报表"),
    );

    match decision {
        DeterministicSubmitDecision::Execute(plan) => {
            let debug = format!("{plan:?}");
            assert!(debug.contains("国网兰州供电公司"), "missing canonical org label: {debug}");
            assert!(debug.contains("62401"), "missing canonical org code: {debug}");
            assert!(debug.contains("2026-03"), "missing canonical period value: {debug}");
            assert!(debug.contains("month") || debug.contains("Month"), "missing canonical month mode: {debug}");
            assert!(debug.contains("fdate"), "missing canonical month payload: {debug}");
        }
        other => panic!("expected deterministic execute plan, got {other:?}"),
    }
}

#[test]
fn deterministic_lineloss_missing_period_uses_default_month_execution_plan() {
    let expected_month = expected_default_month();
    let decision = decide_deterministic_submit("兰州公司 月累计。。。", None, None);

    match decision {
        DeterministicSubmitDecision::Execute(plan) => {
            assert_eq!(plan.period_mode, "month");
            assert_eq!(plan.period_mode_code, "1");
            assert_eq!(plan.period_value, expected_month);
            assert!(plan.period_payload.contains("fdate"));
        }
        other => panic!("expected missing month period to default into execution, got {other:?}"),
    }
}

#[test]
fn deterministic_lineloss_missing_period_uses_default_week_execution_plan() {
    let (expected_value, expected_start, expected_end) = expected_default_week_range();
    let decision = decide_deterministic_submit("兰州公司 周累计。。。", None, None);

    match decision {
        DeterministicSubmitDecision::Execute(plan) => {
            assert_eq!(plan.period_mode, "week");
            assert_eq!(plan.period_mode_code, "2");
            assert_eq!(plan.period_value, expected_value);
            assert!(plan.period_payload.contains(&expected_start));
            assert!(plan.period_payload.contains(&expected_end));
        }
        other => panic!("expected missing week period to default into execution, got {other:?}"),
    }
}

#[test]
fn deterministic_lineloss_partial_artifact_summary_contract_is_locked() {
    let artifact = serde_json::json!({
        "type": "report-artifact",
        "report_name": "tq-lineloss-report",
        "status": "partial",
        "org": {
            "label": "国网兰州供电公司",
            "code": "62401"
        },
        "period": {
            "mode": "month",
            "mode_code": "1",
            "value": "2026-03",
            "payload": {
                "fdate": "2026-03"
            }
        },
        "columns": ["ORG_NAME", "LINE_LOSS_RATE"],
        "rows": [
            { "ORG_NAME": "国网兰州供电公司", "LINE_LOSS_RATE": "3.21" }
        ],
        "counts": {
            "rows": 1
        },
        "export": {
            "attempted": true,
            "status": "failed",
            "message": "report_log_failed"
        },
        "reasons": ["report_log_failed"]
    });

    assert_eq!(artifact["type"], "report-artifact");
    assert_eq!(artifact["report_name"], "tq-lineloss-report");
    assert_eq!(artifact["status"], "partial");
    assert_eq!(artifact["org"]["label"], "国网兰州供电公司");
    assert_eq!(artifact["period"]["value"], "2026-03");
    assert_eq!(artifact["counts"]["rows"], 1);
    assert_eq!(artifact["reasons"][0], "report_log_failed");
}

#[test]
fn deterministic_lineloss_blocked_and_error_artifact_statuses_are_failure_contracts() {
    for status in ["blocked", "error"] {
        let artifact = serde_json::json!({
            "type": "report-artifact",
            "report_name": "tq-lineloss-report",
            "status": status,
            "org": {
                "label": "国网兰州供电公司",
                "code": "62401"
            },
            "period": {
                "mode": "week",
                "mode_code": "2",
                "value": "2026-W12",
                "payload": {
                    "tjzq": "week",
                    "level": "00",
                    "weekSfdate": "2026-03-16",
                    "weekEfdate": "2026-03-22"
                }
            },
            "columns": [],
            "rows": [],
            "counts": {
                "rows": 0
            },
            "export": {
                "attempted": false,
                "status": "skipped",
                "message": null
            },
            "reasons": ["selected_range_unavailable"]
        });

        assert_eq!(artifact["status"], status);
        assert_eq!(artifact["type"], "report-artifact");
        assert_eq!(artifact["period"]["mode"], "week");
        assert_eq!(artifact["reasons"][0], "selected_range_unavailable");
    }
}
