from datetime import datetime, timedelta, timezone
from pathlib import Path

from app.domain.opportunity_flow.evidence import (
    OpportunityEvidence,
    _completed_send_quote_task_evidence,
)
from app.domain.opportunity_flow.profiles import load_company_profile
from app.domain.opportunity_flow.rules import decide_blif_next_action
from app.fiscal_enrichment_service import (
    _apply_source_name_confidence_guard,
    _should_auto_apply,
)
from app.revenue_forecast_service import (
    activity_factor,
    forecast_bucket,
    stage_probability,
)


def test_completed_send_quote_task_is_evidence():
    assert _completed_send_quote_task_evidence([
        {"action_code": "SEND_QUOTE", "status": "done"}
    ]) is True


def test_pending_send_quote_task_is_not_sent_evidence():
    assert _completed_send_quote_task_evidence([
        {"action_code": "SEND_QUOTE", "status": "pending"}
    ]) is False


def test_quote_sent_without_document_requires_reconciliation():
    evidence = OpportunityEvidence(
        opportunity_id="opp-1",
        has_fiscal_customer=True,
        fiscal_identity_validated=True,
        fiscal_data_complete=True,
        quote_sent=True,
        has_quote=False,
        has_invoice=False,
    )
    decision = decide_blif_next_action(evidence, load_company_profile("blif"))
    assert decision.next_action.code == "RECONCILE_DOCUMENTS"
    assert decision.next_action.label == "Associar orçamento enviado"


def test_no_quote_evidence_still_creates_quote():
    evidence = OpportunityEvidence(
        opportunity_id="opp-2",
        has_fiscal_customer=True,
        fiscal_identity_validated=True,
        fiscal_data_complete=True,
    )
    decision = decide_blif_next_action(evidence, load_company_profile("blif"))
    assert decision.next_action.code == "CREATE_JASMIN_QUOTE"


def test_send_proforma_policy_schedules_payment_followup():
    text = Path("app/followup_service.py").read_text(encoding="utf-8")
    block = text.split('"SEND_PROFORMA": {', 1)[1].split("},", 1)[0]
    assert '"action_code": "FOLLOW_UP_PAYMENT"' in block


def test_followup_alignment_function_is_present():
    text = Path("app/followup_service.py").read_text(encoding="utf-8")
    assert "def align_pending_followups_for_opportunity" in text
    assert "superseded_by_pending_action" in text


def test_exact_nif_with_different_company_name_is_capped():
    opportunity = {"customer_name": "EFILUZ, LDA"}
    company = {"nome": "AGOSTINHO ARAÚJO SILVA - CONSTRUÇÕES, UNIPESSOAL, LDA"}
    confidence = _apply_source_name_confidence_guard(
        opportunity, company, lookup_type="nif", confidence=100.0
    )
    assert confidence == 70.0
    assert company["source_name_conflict"] is True


def test_exact_nif_with_matching_company_name_keeps_confidence():
    opportunity = {"customer_name": "EFILUZ, LDA"}
    company = {"nome": "Efiluz Lda"}
    confidence = _apply_source_name_confidence_guard(
        opportunity, company, lookup_type="nif", confidence=100.0
    )
    assert confidence == 100.0
    assert "source_name_conflict" not in company


def test_source_name_conflict_blocks_auto_apply():
    assert _should_auto_apply(
        {},
        {
            "nome": "Empresa Errada, LDA",
            "nif": "515913596",
            "match_type": "nif_exato",
            "source_name_conflict": True,
        },
        confidence=100.0,
    ) is False


def test_forecast_uses_default_when_history_is_small():
    probability, source = stage_probability(
        "QUOTE_SENT", {"QUOTE_SENT": {"resolved": 7, "win_rate": 0.9}}
    )
    assert probability == 0.40
    assert source == "stage_default"


def test_forecast_blends_history_with_minimum_sample():
    probability, source = stage_probability(
        "QUOTE_SENT", {"QUOTE_SENT": {"resolved": 8, "win_rate": 0.625}}
    )
    assert probability == 0.5125
    assert source == "historical_blended"


def test_identity_conflict_reduces_activity_factor():
    now = datetime.now(timezone.utc)
    normal = activity_factor(updated_at=now, has_conflict=False)
    conflicted = activity_factor(updated_at=now, has_conflict=True)
    assert conflicted < normal


def test_forecast_bucket_boundaries():
    now = datetime.now(timezone.utc)
    assert forecast_bucket(now + timedelta(days=30), now=now) == "0_30"
    assert forecast_bucket(now + timedelta(days=31), now=now) == "31_60"
    assert forecast_bucket(now + timedelta(days=61), now=now) == "61_90"
    assert forecast_bucket(now + timedelta(days=91), now=now) == "90_plus"
