"""Sales target and management forecast dashboard."""
from __future__ import annotations

from urllib.parse import urlencode

from fastapi import APIRouter, Form
from fastapi.responses import HTMLResponse, RedirectResponse

from app.admin_dashboard import esc, money_html, stage_label
from app.admin_ui.components import kpi_card
from app.admin_ui.layout import layout
from app.revenue_forecast_service import TARGET_METRICS, get_revenue_forecast, set_sales_target

router = APIRouter()


def _pct(value: object, digits: int = 0) -> str:
    try:
        return f"{float(value or 0) * 100:.{digits}f}%"
    except Exception:
        return "0%"


def _number(value: object) -> float:
    try:
        return float(value or 0)
    except Exception:
        return 0.0


def _status_alert(status: dict) -> str:
    tone = str(status.get("tone") or "gray")
    css = {"green": "success", "orange": "warning", "red": "danger", "gray": "secondary"}.get(tone, "secondary")
    return f'<div class="alert alert-{css}"><strong>{esc(status.get("label"))}</strong><div>{esc(status.get("message"))}</div></div>'


def _class_badge(value: str) -> str:
    mapping = {
        "realised": ("Realizado", "success"),
        "committed": ("Comprometido", "primary"),
        "probable": ("Provável", "warning"),
        "realised_other_period": ("Já realizado", "secondary"),
    }
    label, css = mapping.get(str(value), (value or "—", "secondary"))
    return f'<span class="badge text-bg-{css}">{esc(label)}</span>'


@router.post("/forecast/target")
@router.post("/finance/forecast/target")
@router.post("/financeiro/previsao/meta")
async def revenue_forecast_target_save(
    month: str = Form(...),
    metric: str = Form("invoiced"),
    target_amount: str = Form("0"),
):
    raw = str(target_amount or "0").strip().replace(" ", "")
    if "," in raw:
        raw = raw.replace(".", "").replace(",", ".")
    try:
        amount = float(raw)
    except ValueError:
        amount = 0.0
    set_sales_target(month=month, metric=metric, target_amount=amount, updated_by="operator")
    return RedirectResponse(f"/forecast?{urlencode({'month': month, 'metric': metric})}", status_code=303)


@router.get("/finance/forecast")
@router.get("/financeiro/previsao")
async def legacy_revenue_forecast(month: str | None = None, metric: str = "invoiced"):
    params = {"metric": metric}
    if month:
        params["month"] = month
    return RedirectResponse(f"/forecast?{urlencode(params)}", status_code=302)


@router.get("/forecast", response_class=HTMLResponse)
async def revenue_forecast_page(month: str | None = None, metric: str = "invoiced"):
    forecast = get_revenue_forecast(limit=1000, month=month, metric=metric)
    summary = forecast["summary"]
    management = forecast["management"]
    period = forecast["period"]
    target = forecast["target"]
    month_data = management["month"]
    next30 = management["next_30_days"]
    recoverable = management.get("recoverable") or {}
    target_amount = _number(management["target_amount"])

    metric_options = "".join(
        f'<option value="{esc(key)}" {"selected" if key == management["metric"] else ""}>{esc(label)}</option>'
        for key, label in TARGET_METRICS.items()
    )
    month_value = str(period["month_start"])[:7]

    # Progress bar segments stop at the target; any surplus is shown separately.
    if target_amount > 0:
        realised_ratio = min(_number(month_data["realised"]) / target_amount, 1.0)
        remaining = max(1.0 - realised_ratio, 0.0)
        committed_ratio = min(_number(month_data["committed"]) / target_amount, remaining)
        remaining = max(remaining - committed_ratio, 0.0)
        probable_ratio = min(_number(month_data["probable"]) / target_amount, remaining)
        missing_ratio = max(1.0 - realised_ratio - committed_ratio - probable_ratio, 0.0)
        progress = f"""
        <div class="cf-target-progress" role="img" aria-label="Cumprimento previsto da meta">
          <div class="cf-target-segment cf-target-realised" style="width:{realised_ratio*100:.2f}%" title="Realizado"></div>
          <div class="cf-target-segment cf-target-committed" style="width:{committed_ratio*100:.2f}%" title="Comprometido"></div>
          <div class="cf-target-segment cf-target-probable" style="width:{probable_ratio*100:.2f}%" title="Provável"></div>
          <div class="cf-target-segment cf-target-missing" style="width:{missing_ratio*100:.2f}%" title="Falta"></div>
        </div>
        <div class="d-flex flex-wrap gap-3 small mt-2">
          <span><i class="cf-dot cf-dot-realised"></i> Realizado {money_html(month_data['realised'])}</span>
          <span><i class="cf-dot cf-dot-committed"></i> Comprometido {money_html(month_data['committed'])}</span>
          <span><i class="cf-dot cf-dot-probable"></i> Provável {money_html(month_data['probable'])}</span>
          <span><i class="cf-dot cf-dot-missing"></i> Falta {money_html(management['gap'])}</span>
        </div>
        """
    else:
        progress = '<div class="text-secondary">Configura uma meta para visualizar o progresso e o desvio.</div>'

    diagnostics_html = "".join(
        f"""
        <div class="d-flex gap-3 py-3 border-bottom">
          <div><span class="badge text-bg-{'danger' if d.get('severity') == 'high' else 'warning'}">{esc(d.get('severity'))}</span></div>
          <div><strong>{esc(d.get('label'))}</strong><div class="small text-secondary">{esc(d.get('detail'))}</div></div>
        </div>
        """
        for d in forecast.get("diagnostics", [])
    ) or '<div class="text-secondary py-4">Sem riscos relevantes identificados com os dados atuais.</div>'

    actions_rows = ""
    for action in forecast.get("priority_actions", []):
        impact = action.get("impact_amount") or 0
        flags = []
        if action.get("overdue_tasks"):
            flags.append(f"{action['overdue_tasks']} task(s) vencida(s)")
        if action.get("has_conflict"):
            flags.append("conflito de identidade")
        actions_rows += f"""
        <tr>
          <td><span class="badge text-bg-light border">{esc(action.get('action_group'))}</span></td>
          <td><a href="/opportunities/{esc(action.get('id'))}"><strong>{esc(action.get('customer_name') or action.get('title') or 'Oportunidade')}</strong></a><div class="small text-secondary">{esc(action.get('title') or '')}</div></td>
          <td>{esc(stage_label(action.get('stage')))}</td>
          <td>{money_html(action.get('amount') or 0)}</td>
          <td><strong>{esc(action.get('recommended_action'))}</strong><div class="small text-secondary">{esc(', '.join(flags) or '—')}</div></td>
          <td>{money_html(impact)}</td>
        </tr>
        """
    if not actions_rows:
        actions_rows = '<tr><td colspan="6" class="text-center text-secondary py-5">Sem ações prioritárias calculadas.</td></tr>'

    realised_rows = ""
    for item in forecast.get("realised_items", [])[:80]:
        realised_rows += f"""
        <tr>
          <td><strong>{esc(item.get('reference') or 'Realizado')}</strong></td>
          <td>{money_html(item.get('amount') or 0)}</td>
          <td>{esc(str(item.get('realised_at') or '')[:10])}</td>
          <td>{f'<a class="btn btn-sm btn-outline-primary" href="/opportunities/{esc(item.get("opportunity_id"))}">Abrir</a>' if item.get('opportunity_id') else '—'}</td>
        </tr>
        """
    if not realised_rows:
        realised_rows = '<tr><td colspan="4" class="text-center text-secondary py-4">Sem valor realizado para esta métrica no mês selecionado.</td></tr>'

    opportunity_rows = ""
    future_valued = [i for i in forecast["items"] if i.get("forecast_class") in {"committed", "probable"} and _number(i.get("amount")) > 0]
    for item in future_valued[:80]:
        flags = []
        if item.get("has_conflict"):
            flags.append("conflito fiscal")
        if item.get("is_stale"):
            flags.append("inativa >30d")
        if item.get("overdue_tasks"):
            flags.append(f"{item['overdue_tasks']} task(s) vencida(s)")
        sample = item.get("probability_sample") or {}
        sample_hint = ""
        if item.get("probability_source") == "historical_blended":
            sample_hint = f" · {int(sample.get('won') or 0)}/{int(sample.get('resolved') or 0)} ganhas"
        probability_cell = (
            '<strong>100%</strong><div class="small text-secondary">receita comprometida</div>'
            if item.get("forecast_class") == "committed"
            else f"<strong>{_pct(item.get('effective_probability'))}</strong><div class=\"small text-secondary\">fase {_pct(item.get('probability'))} × atividade {esc(item.get('activity_factor'))}{esc(sample_hint)}</div>"
        )
        expected_value = item.get("amount") if item.get("forecast_class") == "committed" else item.get("weighted_amount")
        opportunity_rows += f"""
        <tr>
          <td><a href="/opportunities/{esc(item.get('id'))}"><strong>{esc(item.get('customer_name') or item.get('title') or 'Oportunidade')}</strong></a><div class="small text-secondary">{esc(item.get('title') or '')}</div></td>
          <td>{esc(stage_label(item.get('stage')))}<div class="mt-1">{_class_badge(item.get('forecast_class'))}</div></td>
          <td>{money_html(item.get('amount') or 0)}<div class="small text-secondary">{esc(item.get('value_source'))}</div></td>
          <td>{probability_cell}</td>
          <td><strong>{money_html(expected_value or 0)}</strong></td>
          <td>{esc(str(item.get('expected_date') or '')[:10])}</td>
          <td>{esc(', '.join(flags) or '—')}</td>
        </tr>
        """
    if not opportunity_rows:
        opportunity_rows = '<tr><td colspan="7" class="text-center text-secondary py-5">Sem oportunidades futuras valorizadas.</td></tr>'

    stage_rows = "".join(
        f"<tr><td>{esc(stage_label(row.get('stage')))}</td><td>{esc(row.get('count'))}</td><td>{esc(row.get('valued'))}</td><td>{money_html(row.get('gross') or 0)}</td><td>{money_html(row.get('weighted') or 0)}</td></tr>"
        for row in forecast.get("stage_summary", [])
    )

    zero_value_items = [i for i in forecast["items"] if i.get("forecast_class") in {"committed", "probable"} and _number(i.get("amount")) <= 0]
    zero_rows = "".join(
        f'<tr><td><a href="/opportunities/{esc(item.get("id"))}"><strong>{esc(item.get("customer_name") or item.get("title") or "Oportunidade")}</strong></a><div class="small text-secondary">{esc(item.get("title") or "")}</div></td><td>{esc(stage_label(item.get("stage")))}</td><td>{esc(str(item.get("updated_at") or "")[:10])}</td><td><a class="btn btn-sm btn-outline-primary" href="/opportunities/{esc(item.get("id"))}">Valorizar</a></td></tr>'
        for item in zero_value_items[:30]
    ) or '<tr><td colspan="4" class="text-center text-secondary py-4">Todas as oportunidades futuras têm valor.</td></tr>'

    scenarios = management["scenarios"]
    body = f"""
    <style>
      .cf-target-progress {{display:flex;height:22px;border-radius:999px;overflow:hidden;background:#e9ecef}}
      .cf-target-segment {{min-width:0;transition:width .2s ease}}
      .cf-target-realised {{background:#198754}} .cf-target-committed {{background:#0d6efd}}
      .cf-target-probable {{background:#ffc107}} .cf-target-missing {{background:#e9ecef}}
      .cf-dot {{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:5px}}
      .cf-dot-realised {{background:#198754}} .cf-dot-committed {{background:#0d6efd}}
      .cf-dot-probable {{background:#ffc107}} .cf-dot-missing {{background:#ced4da}}
    </style>

    <div class="alert alert-info">Dashboard de gestão comercial. Separa o que já conta para a meta, o que está comprometido e o que ainda depende de conversão. Não representa tesouraria nem substitui validação contabilística.</div>

    <section class="card cf-card mb-3"><div class="card-body p-4">
      <form method="post" action="/forecast/target" class="row g-3 align-items-end">
        <div class="col-md-3"><label class="form-label fw-bold">Mês da meta</label><input class="form-control" type="month" name="month" value="{esc(month_value)}" required></div>
        <div class="col-md-4"><label class="form-label fw-bold">Métrica</label><select class="form-select" name="metric">{metric_options}</select></div>
        <div class="col-md-3"><label class="form-label fw-bold">Meta (€)</label><input class="form-control" name="target_amount" inputmode="decimal" value="{esc(f'{target_amount:.2f}'.replace('.', ','))}" placeholder="10000,00"></div>
        <div class="col-md-2"><button class="btn btn-primary w-100" type="submit">Guardar meta</button></div>
      </form>
      <div class="small text-secondary mt-2">Período: {esc(period['month_start'])} a {esc(period['month_end'])} · critério: {esc(management['metric_label'])}</div>
    </div></section>

    {_status_alert(management['status'])}

    <section class="cf-kpi-grid">
      {kpi_card('Meta mensal', money_html(target_amount), '/forecast', management['metric_label'], 'bi-bullseye')}
      {kpi_card('Realizado', money_html(month_data['realised']), '/forecast', f"{summary['realised_count']} registo(s) no mês", 'bi-check2-circle', 'cf-kpi-tone-green')}
      {kpi_card('Comprometido', money_html(month_data['committed']), '/forecast', f"{month_data['committed_count']} oportunidade(s) até ao fim do mês", 'bi-lock')}
      {kpi_card('Pipeline provável', money_html(month_data['probable']), '/forecast', f"{month_data['probable_count']} oportunidade(s) ponderadas", 'bi-graph-up-arrow')}
      {kpi_card('Previsão total', money_html(month_data['forecast_total']), '/forecast', f"cumprimento {_pct(management['attainment'])}", 'bi-speedometer2')}
      {kpi_card('Desvio', money_html(management['gap']), '/forecast', 'falta para suportar a meta' if management['gap'] else f"excedente {money_html(management['surplus'])}", 'bi-exclamation-triangle', 'cf-kpi-tone-red' if management['gap'] else 'cf-kpi-tone-green')}
    </section>

    <section class="card cf-card mb-3"><div class="card-body p-4">
      <div class="d-flex flex-wrap justify-content-between gap-2 mb-3"><div><h2 class="cf-section-title mb-1">Progresso da meta</h2><div class="small text-secondary">Sem dupla contagem entre realizado, comprometido e provável.</div></div><strong>{_pct(management['attainment'])}</strong></div>
      {progress}
    </div></section>

    <div class="row g-3 mb-3">
      <div class="col-xl-6"><section class="card cf-card h-100"><div class="card-body p-4"><h2 class="cf-section-title">Até ao fim do mês</h2><div class="display-6 fw-bold">{money_html(month_data['forecast_total'])}</div><div class="small text-secondary">Realizado {money_html(month_data['realised'])} · futuro adicional {money_html(month_data['future_total'])}</div></div></section></div>
      <div class="col-xl-6"><section class="card cf-card h-100"><div class="card-body p-4"><h2 class="cf-section-title">Próximos 30 dias adicionais</h2><div class="display-6 fw-bold">{money_html(next30['future_total'])}</div><div class="small text-secondary">Até {esc(period['next_30_end'])}; não inclui o realizado do mês.</div></div></section></div>
    </div>

    <section class="card cf-card mb-3"><div class="card-body p-4">
      <div class="d-flex justify-content-between gap-3 flex-wrap">
        <div><h2 class="cf-section-title mb-1">Capacidade de recuperação</h2><div class="small text-secondary">Pagamentos já existentes previstos após o fim do mês que podem ser acelerados. Não entram na previsão base.</div></div>
        <div class="text-end"><div class="h3 fw-bold mb-0">{money_html(recoverable.get('weighted') or 0)}</div><div class="small text-secondary">{esc(recoverable.get('count') or 0)} pagamento(s) · bruto {money_html(recoverable.get('gross') or 0)}</div></div>
      </div>
      <div class="row g-3 mt-1">
        <div class="col-md-4"><div class="cf-soft-box"><span class="small text-secondary">Previsão com aceleração</span><strong class="d-block h4 mb-0">{money_html(recoverable.get('accelerated_total') or month_data['forecast_total'])}</strong></div></div>
        <div class="col-md-4"><div class="cf-soft-box"><span class="small text-secondary">Cumprimento acelerado</span><strong class="d-block h4 mb-0">{_pct(recoverable.get('accelerated_attainment'))}</strong></div></div>
        <div class="col-md-4"><div class="cf-soft-box"><span class="small text-secondary">Desvio residual</span><strong class="d-block h4 mb-0">{money_html(recoverable.get('residual_gap') or 0)}</strong></div></div>
      </div>
    </div></section>

    <div class="row g-3 mb-3">
      <div class="col-xl-7"><section class="card cf-card h-100"><div class="card-body p-4"><h2 class="cf-section-title mb-2">Diagnóstico do desvio</h2>{diagnostics_html}</div></section></div>
      <div class="col-xl-5"><section class="card cf-card h-100"><div class="card-body p-4"><h2 class="cf-section-title mb-3">Cenários até ao fim do mês</h2><table class="table cf-table"><tbody><tr><th>Conservador</th><td class="text-end">{money_html(scenarios['conservative'])}</td></tr><tr><th>Provável</th><td class="text-end fw-bold">{money_html(scenarios['probable'])}</td></tr><tr><th>Com aceleração</th><td class="text-end">{money_html(scenarios['optimistic'])}</td></tr><tr><th>Potencial máximo conhecido</th><td class="text-end">{money_html(scenarios.get('maximum_known') or scenarios['optimistic'])}</td></tr></tbody></table><hr><div class="small text-secondary">Novo pipeline necessário</div><div class="h3 fw-bold">{money_html(management['new_pipeline_required'])}</div><div class="small text-secondary">Conversão usada {_pct(management['new_pipeline_conversion'])} · aproximadamente {esc(management['new_opportunities_required'])} nova(s) oportunidade(s), quando existe valor médio suficiente.</div></div></section></div>
    </div>

    <section class="card cf-card mb-3"><div class="card-body p-0"><div class="p-3 border-bottom"><h2 class="cf-section-title">Ações de maior impacto</h2><div class="small text-secondary">O que executar hoje para proteger ou recuperar a meta.</div></div><div class="cf-table-wrap border-0 rounded-0"><table class="table cf-table"><thead><tr><th>Grupo</th><th>Oportunidade</th><th>Fase</th><th>Valor</th><th>Ação recomendada</th><th>Impacto</th></tr></thead><tbody>{actions_rows}</tbody></table></div></div></section>

    <section class="card cf-card mb-3"><div class="card-body p-0"><div class="p-3 border-bottom"><h2 class="cf-section-title">Realizado no mês</h2><div class="small text-secondary">Registos que já contam para a métrica selecionada; não voltam a ser somados no pipeline futuro.</div></div><div class="cf-table-wrap border-0 rounded-0"><table class="table cf-table"><thead><tr><th>Referência</th><th>Valor</th><th>Data</th><th></th></tr></thead><tbody>{realised_rows}</tbody></table></div></div></section>

    <section class="card cf-card mb-3"><div class="card-body p-0"><div class="p-3 border-bottom"><h2 class="cf-section-title">Oportunidades que suportam a previsão futura</h2><div class="small text-secondary">Realizado no mês é apresentado separadamente; esta tabela mostra apenas valor adicional comprometido ou provável.</div></div><div class="cf-table-wrap border-0 rounded-0"><table class="table cf-table"><thead><tr><th>Oportunidade</th><th>Fase</th><th>Valor</th><th>Probabilidade</th><th>Valor esperado</th><th>Data</th><th>Alertas</th></tr></thead><tbody>{opportunity_rows}</tbody></table></div></div></section>

    <div class="row g-3">
      <div class="col-xl-6"><section class="card cf-card h-100"><div class="card-body p-0"><div class="p-3 border-bottom"><h2 class="cf-section-title">Funil por fase</h2><div class="small text-secondary">Quantidade, cobertura e valor.</div></div><div class="cf-table-wrap border-0 rounded-0"><table class="table cf-table"><thead><tr><th>Fase</th><th>Total</th><th>Com valor</th><th>Bruto</th><th>Ponderado</th></tr></thead><tbody>{stage_rows}</tbody></table></div></div></section></div>
      <div class="col-xl-6"><section class="card cf-card h-100"><div class="card-body p-0"><div class="p-3 border-bottom"><h2 class="cf-section-title">Oportunidades por valorizar</h2><div class="small text-secondary">{esc(summary['unvalued_opportunities'])} de {esc(summary['opportunities'])} sem valor · cobertura {_pct(summary['value_coverage'])} · qualidade {_pct(summary['quality_score'])}.</div></div><div class="cf-table-wrap border-0 rounded-0"><table class="table cf-table"><thead><tr><th>Oportunidade</th><th>Fase</th><th>Atualizada</th><th></th></tr></thead><tbody>{zero_rows}</tbody></table></div></div></section></div>
    </div>
    """
    return layout("Meta e desempenho comercial", "Acompanhar vendas e decidir quando mudar a abordagem", body, "forecast")
