# grantflow/tests/test_strategies.py

import pytest
from pydantic import BaseModel

from grantflow.core.strategies.catalog import list_supported_donors
from grantflow.core.strategies.adb import ADBStrategy
from grantflow.core.strategies.afd import AFDStrategy
from grantflow.core.strategies.eu import EUStrategy
from grantflow.core.strategies.factory import DonorFactory, strategy_factory
from grantflow.core.strategies.fcdo import FCDOStrategy
from grantflow.core.strategies.giz import GIZStrategy
from grantflow.core.strategies.jica import JICAStrategy
from grantflow.core.strategies.state_department import StateDepartmentStrategy
from grantflow.core.strategies.usaid import USAIDStrategy
from grantflow.core.strategies.worldbank import WorldBankStrategy


def test_usaid_strategy():
    strategy = strategy_factory("USAID")
    assert isinstance(strategy, USAIDStrategy)
    assert strategy.donor_id == "USAID"
    assert strategy.get_rag_collection() == "usaid_ads201"
    prompts = strategy.get_system_prompts()
    assert "Architect" in prompts
    assert "MEL_Specialist" in prompts
    assert "Red_Team_Critic" in prompts
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_eu_strategy():
    strategy = strategy_factory("EU")
    assert isinstance(strategy, EUStrategy)
    assert strategy.donor_id == "EU"
    assert strategy.get_rag_collection() == "eu_intpa"
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_worldbank_strategy():
    strategy = strategy_factory("WorldBank")
    assert isinstance(strategy, WorldBankStrategy)
    assert strategy.donor_id == "WorldBank"
    assert strategy.get_rag_collection() == "worldbank_ads301"
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_giz_strategy():
    strategy = strategy_factory("giz")
    assert isinstance(strategy, GIZStrategy)
    assert strategy.get_rag_collection() == "giz_guidance"
    prompts = strategy.get_system_prompts()
    assert "Architect" in prompts and "MEL_Specialist" in prompts and "Red_Team_Critic" in prompts
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_state_department_strategy():
    strategy = strategy_factory("us_state_department")
    assert isinstance(strategy, StateDepartmentStrategy)
    assert strategy.get_rag_collection() == "us_state_department_guidance"
    prompts = strategy.get_system_prompts()
    assert "Architect" in prompts and "MEL_Specialist" in prompts and "Red_Team_Critic" in prompts
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_fcdo_strategy():
    strategy = strategy_factory("fcdo")
    assert isinstance(strategy, FCDOStrategy)
    assert strategy.donor_id == "FCDO"
    assert strategy.get_rag_collection() == "fcdo_guidance"
    prompts = strategy.get_system_prompts()
    assert "Architect" in prompts
    assert "MEL_Specialist" in prompts
    assert "Red_Team_Critic" in prompts
    # Prompts must reference FCDO-specific concepts
    assert "logframe" in prompts["Architect"].lower()
    assert "VfM" in prompts["Architect"] or "Value for Money" in prompts["Architect"]
    assert "safeguarding" in prompts["Architect"].lower()
    assert "OECD DAC" in prompts["MEL_Specialist"]
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_fcdo_strategy_aliases():
    assert isinstance(DonorFactory.get_strategy("ukaid"), FCDOStrategy)
    assert isinstance(DonorFactory.get_strategy("uk_aid"), FCDOStrategy)
    assert isinstance(DonorFactory.get_strategy("dfid"), FCDOStrategy)
    assert isinstance(DonorFactory.get_strategy("foreign_commonwealth_development_office"), FCDOStrategy)


def test_fcdo_toc_schema_fields():
    from grantflow.core.strategies.fcdo import FCDO_TOC

    fields = FCDO_TOC.model_fields
    assert "impact_statement" in fields
    assert "outcome_statement" in fields
    assert "outputs" in fields
    assert "outcomes" in fields
    assert "assumptions" in fields
    assert "value_for_money_rationale" in fields
    assert "safeguarding_considerations" in fields


def test_fcdo_mel_indicator_schema_fields():
    from grantflow.core.strategies.fcdo import FCDO_MELIndicator

    fields = FCDO_MELIndicator.model_fields
    assert "logframe_level" in fields
    assert "oecd_dac_criterion" in fields
    assert "means_of_verification" in fields
    assert "disaggregation" in fields


def test_adb_strategy():
    strategy = strategy_factory("adb")
    assert isinstance(strategy, ADBStrategy)
    assert strategy.donor_id == "ADB"
    assert strategy.get_rag_collection() == "adb_guidance"
    prompts = strategy.get_system_prompts()
    assert "Architect" in prompts and "MEL_Specialist" in prompts and "Red_Team_Critic" in prompts
    assert "DMF" in prompts["Architect"] or "Design and Monitoring Framework" in prompts["Architect"]
    assert "Strategy 2030" in prompts["Architect"]
    assert "climate" in prompts["Architect"].lower()
    assert "gender" in prompts["Architect"].lower()
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_adb_strategy_aliases():
    assert isinstance(DonorFactory.get_strategy("asian_development_bank"), ADBStrategy)
    assert isinstance(DonorFactory.get_strategy("adb_asia"), ADBStrategy)


def test_adb_dmf_schema_fields():
    from grantflow.core.strategies.adb import ADB_DMF

    fields = ADB_DMF.model_fields
    assert "impact" in fields
    assert "outcome" in fields
    assert "outputs" in fields
    assert "strategy_2030_op_priorities" in fields
    assert "climate_finance_rationale" in fields
    assert "gender_equity_category" in fields
    assert "sovereign_or_nonsovereign" in fields


def test_adb_mel_indicator_schema_fields():
    from grantflow.core.strategies.adb import ADB_MELIndicator

    fields = ADB_MELIndicator.model_fields
    assert "dmf_level" in fields
    assert "strategy_2030_op_priority" in fields
    assert "climate_finance_eligible" in fields
    assert "gender_disaggregated" in fields
    assert "data_source" in fields


def test_afd_strategy():
    strategy = strategy_factory("afd")
    assert isinstance(strategy, AFDStrategy)
    assert strategy.donor_id == "AFD"
    assert strategy.get_rag_collection() == "afd_guidance"
    prompts = strategy.get_system_prompts()
    assert "Architect" in prompts and "MEL_Specialist" in prompts and "Red_Team_Critic" in prompts
    assert "climate" in prompts["Architect"].lower()
    assert "gender" in prompts["Architect"].lower()
    assert "cadre logique" in prompts["Architect"].lower()
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_afd_strategy_aliases():
    assert isinstance(DonorFactory.get_strategy("agence_francaise_de_developpement"), AFDStrategy)
    assert isinstance(DonorFactory.get_strategy("french_development_agency"), AFDStrategy)


def test_afd_toc_schema_fields():
    from grantflow.core.strategies.afd import AFD_TOC

    fields = AFD_TOC.model_fields
    assert "overall_objective" in fields
    assert "specific_objective" in fields
    assert "climate_environment_nexus" in fields
    assert "gender_social_inclusion" in fields
    assert "french_expertise_partnership" in fields


def test_afd_mel_indicator_climate_gender_markers():
    from grantflow.core.strategies.afd import AFD_MELIndicator

    fields = AFD_MELIndicator.model_fields
    assert "climate_marker" in fields
    assert "gender_marker" in fields
    assert "logframe_level" in fields


def test_jica_strategy():
    strategy = strategy_factory("jica")
    assert isinstance(strategy, JICAStrategy)
    assert strategy.donor_id == "JICA"
    assert strategy.get_rag_collection() == "jica_guidance"
    prompts = strategy.get_system_prompts()
    assert "Architect" in prompts and "MEL_Specialist" in prompts and "Red_Team_Critic" in prompts
    assert "PCM" in prompts["Architect"] or "PDM" in prompts["Architect"]
    assert "assumption" in prompts["Architect"].lower()
    assert "transfer" in prompts["Architect"].lower()
    mel_schema = strategy.get_mel_schema()
    assert isinstance(mel_schema, type)
    assert issubclass(mel_schema, BaseModel)


def test_jica_strategy_aliases():
    assert isinstance(DonorFactory.get_strategy("japan_international_cooperation_agency"), JICAStrategy)
    assert isinstance(DonorFactory.get_strategy("jica_japan"), JICAStrategy)


def test_jica_pdm_schema_fields():
    from grantflow.core.strategies.jica import JICA_PDM

    fields = JICA_PDM.model_fields
    assert "overall_goal" in fields
    assert "project_purpose" in fields
    assert "important_assumptions" in fields
    assert "japanese_oda_rationale" in fields
    assert "technology_transfer_plan" in fields
    assert "counterpart_capacity" in fields


def test_jica_mel_indicator_pdm_level():
    from grantflow.core.strategies.jica import JICA_MELIndicator

    fields = JICA_MELIndicator.model_fields
    assert "pdm_level" in fields
    assert "responsible_party" in fields


def test_donor_factory_aliases():
    assert isinstance(DonorFactory.get_strategy("usaid.gov"), USAIDStrategy)
    assert isinstance(DonorFactory.get_strategy("european-union"), EUStrategy)
    assert isinstance(DonorFactory.get_strategy("world_bank"), WorldBankStrategy)
    # New catalog aliases
    assert DonorFactory.get_strategy("undp").get_rag_collection() == "un_agencies_guidance"
    assert isinstance(DonorFactory.get_strategy("giz"), GIZStrategy)
    assert isinstance(DonorFactory.get_strategy("state_department"), StateDepartmentStrategy)
    assert DonorFactory.get_strategy("gates").get_rag_collection() == "gates_foundation_guidance"
    assert DonorFactory.get_strategy("gates").get_mel_schema() is None


def test_all_catalog_donors_supported():
    donors = list_supported_donors()
    assert len(donors) >= 30
    for donor in donors:
        strategy = DonorFactory.get_strategy(donor["id"])
        assert strategy is not None
        assert strategy.get_rag_collection()


def test_unknown_donor():
    with pytest.raises(ValueError):
        strategy_factory("UnknownDonor")
