from typing import Tuple, Optional

from src.core.config import policy_v1
from src.core.models.inputs import CreditRequest
from typing import Tuple, Optional

from src.core.config import policy_v1
from src.core.models.inputs import CreditRequest
from src.core.models.document_signals import DocumentSignals


# -------------------------------------------------
# IND-01 — Tempo de actividade
# -------------------------------------------------
def ind_tempo_atividade(input_data: CreditRequest) -> Tuple[int, Optional[str]]:
    meses = input_data.meses_atividade

    for threshold, score in policy_v1.TEMPO_ATIVIDADE_SCORE:
        if meses >= threshold:
            if score < 0:
                return score, "Empresa com tempo de actividade reduzido"
            return score, None

    return 0, None


# -------------------------------------------------
# IND-02 — Faturamento mensal
# -------------------------------------------------
def ind_faturamento(input_data: CreditRequest) -> Tuple[int, Optional[str]]:
    faturamento = input_data.faturamento_mensal

    if faturamento is None:
        return -100, "Faturamento não informado"

    for threshold, score in policy_v1.FATURAMENTO_SCORE:
        if threshold is not None and faturamento >= threshold:
            return score, None

    return 0, None


# -------------------------------------------------
# IND-03 — Exposição relativa (valor_credito / faturamento)
# -------------------------------------------------
def ind_exposicao(input_data: CreditRequest) -> Tuple[int, Optional[str]]:
    faturamento = input_data.faturamento_mensal
    valor = input_data.valor_credito

    if faturamento is None or faturamento <= 0:
        return -180, "Exposição não calculável por falta de faturamento"

    ratio = valor / faturamento

    for threshold, score in policy_v1.EXPOSICAO_SCORE:
        if threshold is not None and ratio <= threshold:
            if score < 0:
                return score, "Exposição elevada face ao faturamento"
            return score, None

    return -180, "Exposição extremamente elevada"


# -------------------------------------------------
# IND-04 — Documentação submetida
# -------------------------------------------------
def ind_documentacao(input_data: CreditRequest) -> Tuple[int, Optional[str]]:
    docs = input_data.num_documentos

    if docs in policy_v1.DOCUMENTOS_SCORE:
        return policy_v1.DOCUMENTOS_SCORE[docs], None

    if docs < 3:
        return policy_v1.DOCUMENTOS_PENALTY_MIN, "Documentação insuficiente"

    return 0, None


# -------------------------------------------------
# IND-05 — Completude do formulário
# -------------------------------------------------
def ind_completude(input_data: CreditRequest) -> Tuple[int, Optional[str]]:
    completude = input_data.percentual_completude

    for threshold, score in policy_v1.COMPLETUDE_SCORE:
        if completude >= threshold:
            if score < 0:
                return score, "Baixa completude das informações fornecidas"
            return score, None

    return 0, None


# -------------------------------------------------
# IND-06 — Coerência faturamento × crédito
# -------------------------------------------------
def ind_coerencia_valor(input_data: CreditRequest) -> Tuple[int, Optional[str]]:
    faturamento = input_data.faturamento_mensal
    valor = input_data.valor_credito

    if faturamento is None or faturamento <= 0:
        return 0, None

    if valor > faturamento * 0.8:
        return -150, "Valor solicitado muito elevado face ao faturamento"

    return 0, None


# -------------------------------------------------
# IND-07 — Coerência temporal (idade × faturamento)
# -------------------------------------------------
def ind_coerencia_temporal(input_data: CreditRequest) -> Tuple[int, Optional[str]]:
    meses = input_data.meses_atividade
    faturamento = input_data.faturamento_mensal

    if faturamento is None:
        return 0, None

    if meses < 12 and faturamento > 150_000:
        return -100, "Faturamento elevado para empresa com pouca maturidade"

    return 0, None

# -------------------------------------------------
# Analista de documentação
# -------------------------------------------------
def ind_document_quality(doc_analysis):
    penalidade = 0
    motivos = []

    if doc_analysis.legibilidade_score < 0.6:
        penalidade -= 80
        motivos.append("Documentos com baixa legibilidade")

    if doc_analysis.validacao_score < 0.6:
        penalidade -= 100
        motivos.append("Documentos inválidos ou incompletos")

    if doc_analysis.coerencia_score < 0.6:
        penalidade -= 120
        motivos.append("Documentos incoerentes com dados declarados")

    return penalidade, motivos



# -------------------------------------------------
# EXECUÇÃO DE TODOS OS INDICADORES
# -------------------------------------------------
def calcular_indicadores(input_data: CreditRequest, doc_signals: Optional[DocumentSignals] = None):

    """
    Executa todos os indicadores e devolve:
    - componentes: dict com score por indicador
    - motivos: lista de explicações
    """

    componentes = {}
    motivos = []

    indicadores = {
        "tempo_atividade": ind_tempo_atividade,
        "faturamento": ind_faturamento,
        "exposicao": ind_exposicao,
        "documentacao": ind_documentacao,
        "completude": ind_completude,
        "coerencia_valor": ind_coerencia_valor,
        "coerencia_temporal": ind_coerencia_temporal,
    }

    for nome, func in indicadores.items():
        score, motivo = func(input_data)
        componentes[nome] = score
        if motivo:
            motivos.append(motivo)
    # IND-08 (extra): sinais documentais agregados
    pontos_doc, motivo_doc = ind_document_signals(doc_signals)
    componentes["doc_signals"] = pontos_doc
    if motivo_doc:
        motivos.append(motivo_doc)

    return componentes, motivos
def ind_document_signals(doc: Optional[DocumentSignals]) -> Tuple[int, Optional[str]]:
    """
    Penaliza score com base em legibilidade/correctude/coerência dos documentos.
    Se doc for None (sem análise), não penaliza aqui (penalização já existe via num_documentos/completude).
    """
    if doc is None:
        return 0, None

    pontos = 0
    motivos = []

    th = policy_v1.DOC_SIGNAL_THRESHOLDS
    pen = policy_v1.DOC_SIGNAL_PENALTIES

    if doc.legibilidade_score < th["legibilidade_min"]:
        pontos += pen["legibilidade"]
        motivos.append("Documentos com baixa legibilidade")

    if doc.validacao_score < th["validacao_min"]:
        pontos += pen["validacao"]
        motivos.append("Documentos inválidos ou incompletos")

    if doc.coerencia_score < th["coerencia_min"]:
        pontos += pen["coerencia"]
        motivos.append("Documentos incoerentes com dados declarados")

    if motivos:
        # devolve uma mensagem resumida (motivos detalhados entram na resposta final pelo pipeline)
        return pontos, "; ".join(motivos)

    return 0, None
