"""Definições gerais e o painel de notificações por email.

Antes, o comportamento dos avisos estava espalhado: o destinatário numa linha de
`alerts_config`, a decisão de quem gera aviso implícita no SQL, e nada para
desligar tudo de uma vez. Aqui fica a cadeia toda, editável e visível:

    interruptor geral -> excepção da empresa -> defeito do tipo -> destinatários

Duas coisas existem para se poder **confiar** nisto, e não são acessórios:

* o **teste** manda para um endereço escrito à mão e nunca para o pré-definido —
  numa sessão anterior um teste meu saiu para o `geral@segunor.pt` a sério e
  chegou lá um aviso de uma empresa que não era cliente;
* o **histórico de envios** (`alerts_log`) fica visível. Já era escrito e nunca
  aparecia em lado nenhum, o que é a diferença entre achar e saber.
"""
from typing import Annotated, Any

from fastapi import APIRouter, Body, Depends, HTTPException, Query
from pydantic import BaseModel, EmailStr
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.auth.deps import CurrentUser, current_user, require_admin
from app.db import get_session
from app.services import app_settings
from app.services.alerts import send_email_raw

router = APIRouter(prefix="/settings", tags=["settings"])

MONITORING_TYPES = ("internal", "competitor", "client", "analysis", "related")


class SettingsPatch(BaseModel):
    email_notifications_enabled: bool | None = None
    default_alert_email: str | None = None
    alert_defaults_by_type: dict[str, bool] | None = None


class TestEmail(BaseModel):
    # Sem defeito de propósito: um teste tem de ser inofensivo por construção e
    # não por cuidado de quem o carrega.
    to: EmailStr


@router.get("")
async def read_settings(
    session: Annotated[AsyncSession, Depends(get_session)],
    _user: Annotated[CurrentUser, Depends(current_user)],
) -> dict[str, Any]:
    values = await app_settings.get_all(session)
    counts = (
        await session.execute(
            text(
                """
                SELECT monitoring_type,
                       count(*) AS total,
                       count(*) FILTER (WHERE alerts_enabled) AS com_avisos
                  FROM companies WHERE active GROUP BY monitoring_type
                """
            )
        )
    ).mappings().all()
    recipients = (
        await session.execute(
            text(
                "SELECT id::text, kind, type, target, enabled FROM alerts_config"
                " ORDER BY kind, type, target"
            )
        )
    ).mappings().all()
    return {
        "settings": values,
        "por_tipo": [dict(r) for r in counts],
        "destinatarios": [dict(r) for r in recipients],
    }


@router.patch("")
async def patch_settings(
    session: Annotated[AsyncSession, Depends(get_session)],
    admin: Annotated[CurrentUser, Depends(require_admin)],
    patch: SettingsPatch = Body(...),
) -> dict[str, Any]:
    payload = patch.model_dump(exclude_none=True)
    if defaults := payload.get("alert_defaults_by_type"):
        unknown = set(defaults) - set(MONITORING_TYPES)
        if unknown:
            raise HTTPException(
                status_code=400, detail=f"tipos desconhecidos: {sorted(unknown)}"
            )
        current = await app_settings.get(session, "alert_defaults_by_type") or {}
        payload["alert_defaults_by_type"] = {**current, **defaults}
    await app_settings.set_many(session, payload, user_id=str(admin.id))
    await session.commit()
    return await app_settings.get_all(session)


@router.patch("/companies/{company_id}/alerts")
async def toggle_company_alerts(
    company_id: str,
    session: Annotated[AsyncSession, Depends(get_session)],
    _admin: Annotated[CurrentUser, Depends(require_admin)],
    enabled: bool = Query(...),
) -> dict[str, Any]:
    """Excepção por empresa, para uma empresa concreta fugir ao defeito do tipo."""
    row = (
        await session.execute(
            text(
                "UPDATE companies SET alerts_enabled = :en, updated_at = now()"
                " WHERE id = CAST(:cid AS UUID)"
                " RETURNING nif, legal_name, monitoring_type, alerts_enabled"
            ),
            {"en": enabled, "cid": company_id},
        )
    ).mappings().first()
    if not row:
        raise HTTPException(status_code=404, detail="empresa não encontrada")
    await session.commit()
    return dict(row)


@router.post("/test-email")
async def test_email(
    session: Annotated[AsyncSession, Depends(get_session)],
    _admin: Annotated[CurrentUser, Depends(require_admin)],
    payload: TestEmail = Body(...),
) -> dict[str, Any]:
    """Envia um email de teste para o endereço indicado.

    Respeita o interruptor geral: se os avisos estiverem desligados, isto também
    não sai — senão o teste diria que o SMTP funciona num estado em que nada
    funciona.
    """
    if not await app_settings.emails_enabled(session):
        return {
            "ok": False,
            "detalhe": "as notificações por email estão desligadas no interruptor geral",
        }
    ok, response = await send_email_raw(
        str(payload.to),
        "[Intel Grid] Email de teste",
        "Este é um email de teste do Intel Grid.\n\n"
        "Foi pedido manualmente nas Definições e não corresponde a nenhum aviso "
        "real. Nenhum prazo, insolvência ou empresa está envolvido.\n",
    )
    return {"ok": ok, "detalhe": response, "para": str(payload.to)}


@router.get("/alerts-log")
async def alerts_log(
    session: Annotated[AsyncSession, Depends(get_session)],
    _user: Annotated[CurrentUser, Depends(current_user)],
    limit: int = Query(50, le=200),
) -> list[dict[str, Any]]:
    rows = (
        await session.execute(
            text(
                """
                SELECT l.id::text, l.sent_at, l.status, l.response,
                       c.type, c.target, c.kind,
                       a.process_number, a.ato,
                       p.process_number AS processo
                  FROM alerts_log l
                  LEFT JOIN alerts_config c ON c.id = l.alert_config_id
                  LEFT JOIN cire_announcements a ON a.id = l.announcement_id
                  LEFT JOIN processes p ON p.id = l.process_id
                 ORDER BY l.sent_at DESC
                 LIMIT :lim
                """
            ),
            {"lim": limit},
        )
    ).mappings().all()
    return [dict(r) for r in rows]
