from __future__ import annotations

from .decision import OpportunityDecision, WorkflowAction
from .evidence import OpportunityEvidence
from .profiles import CompanyWorkflowProfile
from .types import (
    ACTION_CONFIRM_ORDER,
    ACTION_CONFIRM_PAYMENT,
    ACTION_CREATE_QUOTE,
    ACTION_FOLLOW_UP,
    ACTION_FOLLOW_UP_PAYMENT,
    ACTION_NO_ACTION,
    ACTION_PREPARE_ORDER,
    ACTION_RECONCILE_DOCUMENTS,
    ACTION_REVIEW,
    ACTION_SEND_INVOICE,
    ACTION_SHIP_ORDER,
    ACTION_VALIDATE_FISCAL_CUSTOMER,
    ACTION_WAIT_PRODUCTION,
    COMMERCIAL_STAGE_IN_EXECUTION,
    COMMERCIAL_STAGE_PAYMENT_CONFIRMED,
    COMMERCIAL_STAGE_QUOTE_SENT,
    COMMERCIAL_STAGE_REVIEW,
    COMMERCIAL_STAGE_WAITING_PAYMENT,
    COMMERCIAL_STAGE_WON,
    PAYMENT_AFTER_DELIVERY,
    PAYMENT_BEFORE_SHIPPING,
)


SENSITIVE_DOCUMENT_ACTIONS = {ACTION_CREATE_QUOTE, ACTION_SEND_INVOICE, ACTION_CONFIRM_PAYMENT}


def _action(profile: CompanyWorkflowProfile, code: str, description: str = "", **kwargs: object) -> WorkflowAction:
    return WorkflowAction(
        code=code,
        label=profile.action_label(code, kwargs.pop("label", None)),
        description=description or profile.action_description(code, ""),
        **kwargs,
    )


def _blocked(profile: CompanyWorkflowProfile, code: str, reason: str) -> WorkflowAction:
    return _action(profile, code, can_execute=False, reason_if_blocked=reason)


def _financial_state(e: OpportunityEvidence) -> str:
    if e.payment_confirmed:
        return "payment_confirmed"
    if e.has_invoice:
        return "invoice_payment_pending"
    if e.has_quote:
        return "quote_payment_pending"
    return "no_document"


def _physical_state(e: OpportunityEvidence) -> str:
    if e.order_delivered:
        return "delivered"
    if e.order_shipped:
        return "shipped"
    if e.odoo_ready:
        return "ready_to_ship"
    if e.odoo_in_production:
        return "in_production"
    if e.has_odoo_sale:
        return "odoo_sale"
    return "none"


def _base_warnings(e: OpportunityEvidence) -> list[str]:
    warnings: list[str] = []
    if e.is_reconstructed:
        warnings.append("Registo reconstruído: validar pagamento, valor e documentos antes de executar ações sensíveis.")
    if e.fiscal_identity_validated and not e.fiscal_data_complete:
        warnings.append("Identidade fiscal associada, mas dados fiscais/envio podem estar incompletos.")
    if e.has_nif_conflict or e.has_fiscal_conflict:
        warnings.append("Existe conflito fiscal/NIF: bloquear documentos e pagamentos até validação.")
    if e.has_pending_task and e.pending_task_action_code == ACTION_SEND_INVOICE and e.has_invoice and e.payment_confirmed and e.odoo_in_production:
        warnings.append("Há uma task pendente de envio de fatura, mas a evidência indica fatura existente e produção em curso. Rever se a task está obsoleta ou se falta apenas enviar o PDF ao cliente.")
    return warnings


def decide_blif_next_action(e: OpportunityEvidence, profile: CompanyWorkflowProfile) -> OpportunityDecision:
    """BLIF operational rules isolated from DB and HTML.

    Protected sequence for the normal profile:
    information -> quote -> payment -> invoice -> production/shipping.
    The after-delivery payment term relaxes payment as a shipping blocker but
    keeps payment follow-up explicit after shipment/delivery.
    """

    warnings = _base_warnings(e)
    blocked_actions: list[WorkflowAction] = []
    available_actions: list[WorkflowAction] = []

    if e.is_terminal:
        next_action = _action(profile, ACTION_NO_ACTION, target_url=f"/opportunities/{e.opportunity_id}" if e.opportunity_id else None)
        return OpportunityDecision(
            next_action=next_action,
            reason="A oportunidade está concluída/fechada.",
            warnings=warnings,
            commercial_stage=COMMERCIAL_STAGE_WON,
            financial_state=_financial_state(e),
            physical_state=_physical_state(e),
            profile_name=profile.name,
            decision_version=profile.version,
        )

    if e.has_nif_conflict or e.has_fiscal_conflict:
        blocked_actions.extend(_blocked(profile, code, "conflito fiscal/NIF") for code in SENSITIVE_DOCUMENT_ACTIONS)
        next_action = _action(profile, ACTION_REVIEW, "Resolver conflito fiscal/NIF antes de avançar.", priority="alta", target_url=f"/opportunities/{e.opportunity_id}#cliente" if e.opportunity_id else None)
        return OpportunityDecision(next_action, "Conflito fiscal/NIF bloqueia ações financeiras.", blocked_actions=blocked_actions, warnings=warnings, commercial_stage=COMMERCIAL_STAGE_REVIEW, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if not e.has_fiscal_customer:
        blocked_actions.extend(_blocked(profile, code, "cliente fiscal por associar") for code in SENSITIVE_DOCUMENT_ACTIONS)
        next_action = _action(profile, ACTION_VALIDATE_FISCAL_CUSTOMER, "Associar/validar cliente fiscal antes de documentos oficiais.", priority="alta", target_url=f"/opportunities/{e.opportunity_id}#cliente" if e.opportunity_id else None)
        return OpportunityDecision(next_action, "Cliente fiscal ainda não associado.", blocked_actions=blocked_actions, warnings=warnings, commercial_stage=COMMERCIAL_STAGE_REVIEW, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.has_reconciliation_candidate:
        next_action = _action(profile, ACTION_RECONCILE_DOCUMENTS, f"Confirmar evidência encontrada: {e.reconciliation_label or 'documento/candidato'}.", priority="alta", target_url="/reconciliation")
        return OpportunityDecision(next_action, "Há evidência de reconciliação por validar.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_REVIEW, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if not e.has_quote and not e.has_invoice:
        next_action = _action(profile, ACTION_CREATE_QUOTE, "Criar/enviar orçamento antes de pedir pagamento ou emitir fatura.", target_url=f"/opportunities/{e.opportunity_id}#documentos" if e.opportunity_id else None)
        available_actions.append(next_action)
        return OpportunityDecision(next_action, "Ainda não há orçamento/fatura associado.", available_actions=available_actions, warnings=warnings, commercial_stage=COMMERCIAL_STAGE_REVIEW, financial_state="no_document", physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.payment_terms == PAYMENT_AFTER_DELIVERY:
        if e.order_delivered and not e.payment_confirmed:
            next_action = _action(profile, ACTION_FOLLOW_UP_PAYMENT, "Encomenda entregue com pagamento pós-entrega pendente.", priority="alta", target_url=f"/opportunities/{e.opportunity_id}#operacao" if e.opportunity_id else None)
            return OpportunityDecision(next_action, "Pagamento pós-entrega deve ser acompanhado depois da entrega.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_IN_EXECUTION, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)
        if e.order_shipped and not e.payment_confirmed:
            next_action = _action(profile, ACTION_FOLLOW_UP_PAYMENT, "Encomenda enviada; agendar/acompanhar pagamento pós-entrega.", priority="normal", target_url=f"/opportunities/{e.opportunity_id}#operacao" if e.opportunity_id else None)
            return OpportunityDecision(next_action, "Pagamento pós-entrega pendente após envio.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_IN_EXECUTION, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)
        if not e.has_odoo_sale:
            next_action = _action(profile, ACTION_PREPARE_ORDER, "Pagamento após entrega: criar/associar venda Odoo e avançar preparação sem exigir pagamento confirmado.", priority="alta", target_url=f"/opportunities/{e.opportunity_id}#odoo" if e.opportunity_id else None)
            return OpportunityDecision(next_action, "Condição pós-entrega permite avançar Odoo/preparação sem pagamento prévio.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_IN_EXECUTION, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)
        if e.odoo_in_production and not e.order_shipped:
            next_action = _action(profile, ACTION_WAIT_PRODUCTION, "Pagamento após entrega: venda Odoo criada; aguardar produção/preparação.", priority="normal", target_url=f"/opportunities/{e.opportunity_id}#odoo" if e.opportunity_id else None)
            return OpportunityDecision(next_action, "Aguardar produção/preparação no Odoo; pagamento será acompanhado depois.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_IN_EXECUTION, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)
        if e.odoo_ready and not e.order_shipped:
            next_action = _action(profile, ACTION_SHIP_ORDER, "Pagamento após entrega: encomenda pronta; criar envio/tracking.", priority="alta", target_url=f"/opportunities/{e.opportunity_id}#odoo" if e.opportunity_id else None)
            return OpportunityDecision(next_action, "Pagamento não bloqueia envio porque a condição é pós-entrega.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_IN_EXECUTION, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    # Default/BLIF normal sequence: budget document, payment, invoice, then preparation/shipping.
    if e.payment_terms in {PAYMENT_BEFORE_SHIPPING, "", "undefined", "agreement"} and e.has_quote and not e.payment_confirmed:
        next_action = _action(
            profile,
            ACTION_CONFIRM_PAYMENT,
            f"Orçamento {e.quote_number or ''} associado. Confirmar pagamento antes de emitir fatura.",
            priority="alta",
            target_url=f"/opportunities/{e.opportunity_id}#operacao" if e.opportunity_id else None,
            document_id=e.quote_id,
            document_number=e.quote_number,
        )
        available_actions.append(next_action)
        return OpportunityDecision(next_action, "Fluxo normal BLIF exige pagamento confirmado depois do orçamento e antes da fatura.", available_actions=available_actions, warnings=warnings, commercial_stage=COMMERCIAL_STAGE_WAITING_PAYMENT, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.payment_confirmed and not e.has_invoice and e.has_fiscal_customer and not e.fiscal_data_complete:
        blocked_actions.append(_blocked(profile, ACTION_SEND_INVOICE, "dados fiscais incompletos"))
        next_action = _action(
            profile,
            ACTION_VALIDATE_FISCAL_CUSTOMER,
            "Pagamento confirmado, mas faltam dados fiscais obrigatórios antes de emitir/enviar a fatura.",
            label="Completar dados fiscais",
            priority="alta",
            target_url=f"/opportunities/{e.opportunity_id}#cliente" if e.opportunity_id else None,
        )
        return OpportunityDecision(next_action, "Pagamento confirmado com dados fiscais incompletos; bloquear emissão de fatura até completar a ficha fiscal.", blocked_actions=blocked_actions, warnings=warnings, commercial_stage=COMMERCIAL_STAGE_REVIEW, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.payment_confirmed and not e.has_invoice:
        next_action = _action(
            profile,
            ACTION_SEND_INVOICE,
            f"Pagamento confirmado com base em {e.quote_number or 'orçamento'}. Emitir/enviar fatura de seguida.",
            priority="alta",
            target_url=f"/opportunities/{e.opportunity_id}#documentos" if e.opportunity_id else None,
            document_id=e.quote_id,
            document_number=e.quote_number,
        )
        return OpportunityDecision(next_action, "Pagamento confirmado e ainda não há fatura associada.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_PAYMENT_CONFIRMED, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.has_invoice and not e.payment_confirmed and e.payment_terms != PAYMENT_AFTER_DELIVERY:
        next_action = _action(
            profile,
            ACTION_CONFIRM_PAYMENT,
            f"Fatura {e.invoice_number or ''} associada; confirmar pagamento antes de envio/preparação final.",
            priority="alta",
            target_url=f"/opportunities/{e.opportunity_id}#operacao" if e.opportunity_id else None,
            document_id=e.invoice_id,
            document_number=e.invoice_number,
        )
        return OpportunityDecision(next_action, "Fatura existe mas pagamento ainda não está confirmado.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_WAITING_PAYMENT, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    # A fatura pode já existir no Jasmin mas ainda faltar enviá-la ao cliente.
    # Isto é uma ação de comunicação/documento diferente de “criar fatura” e
    # deve aparecer antes de aguardar produção, sobretudo quando há task
    # SEND_INVOICE pendente com anexo disponível.
    if e.has_invoice and e.payment_confirmed and (e.invoice_sent is False or e.pending_task_action_code == ACTION_SEND_INVOICE):
        next_action = _action(
            profile,
            ACTION_SEND_INVOICE,
            f"Fatura {e.invoice_number or ''} criada/associada. Enviar PDF ao cliente; depois acompanhar produção/preparação.",
            priority="alta",
            target_url=f"/tasks/{e.pending_task_id}" if e.pending_task_id and e.pending_task_action_code == ACTION_SEND_INVOICE else (f"/opportunities/{e.opportunity_id}#documentos" if e.opportunity_id else None),
            document_id=e.invoice_id,
            document_number=e.invoice_number,
        )
        return OpportunityDecision(next_action, "Fatura existe, mas o envio ao cliente ainda não está confirmado.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_PAYMENT_CONFIRMED, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.has_invoice and e.payment_confirmed and e.odoo_in_production:
        next_action = _action(profile, ACTION_WAIT_PRODUCTION, f"Fatura {e.invoice_number or ''} e pagamento confirmados; Odoo ainda está em produção/preparação.", priority="normal", target_url=f"/opportunities/{e.opportunity_id}#odoo" if e.opportunity_id else None, document_id=e.invoice_id, document_number=e.invoice_number)
        return OpportunityDecision(next_action, "Aguardar conclusão da produção/preparação no Odoo.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_IN_EXECUTION, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.has_invoice and e.payment_confirmed and e.odoo_ready and not e.order_shipped:
        next_action = _action(profile, ACTION_SHIP_ORDER, "Encomenda pronta; criar envio/tracking.", priority="alta", target_url=f"/opportunities/{e.opportunity_id}#odoo" if e.opportunity_id else None)
        return OpportunityDecision(next_action, "Pagamento/fatura OK e encomenda pronta para envio.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_IN_EXECUTION, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.has_invoice and e.payment_confirmed and not e.has_odoo_sale:
        next_action = _action(profile, ACTION_PREPARE_ORDER, f"Fatura {e.invoice_number or ''} e pagamento confirmados. Criar/validar venda Odoo e preparação.", priority="alta", target_url=f"/opportunities/{e.opportunity_id}#odoo" if e.opportunity_id else None)
        return OpportunityDecision(next_action, "Fatura e pagamento OK; falta validar execução/Odoo.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_IN_EXECUTION, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    if e.order_delivered and e.payment_confirmed:
        next_action = _action(profile, ACTION_NO_ACTION, "Pagamento confirmado e encomenda entregue.", priority="baixa", target_url=f"/opportunities/{e.opportunity_id}" if e.opportunity_id else None)
        return OpportunityDecision(next_action, "Processo aparentemente concluído.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_WON, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)

    next_action = _action(profile, ACTION_FOLLOW_UP, "Rever tarefas, documentos e próximos contactos.", priority="baixa", target_url=f"/opportunities/{e.opportunity_id}" if e.opportunity_id else None)
    return OpportunityDecision(next_action, "Sem regra específica aplicável; manter em acompanhamento.", warnings=warnings, commercial_stage=COMMERCIAL_STAGE_QUOTE_SENT, financial_state=_financial_state(e), physical_state=_physical_state(e), profile_name=profile.name, decision_version=profile.version)
