"""Billing API routes — Spec 023 + Spec 043."""

from __future__ import annotations

import os
from typing import Any

from fastapi import APIRouter, Depends, Header, HTTPException, Query, Request
from pydantic import BaseModel, Field

from app import auth, billing_store
from app.client_activation import ActivationError, activate_billing_account
from app.permissions import can_manage_billing, can_read_billing, should_mask_sensitive

router = APIRouter(prefix="/api/v1/billing", tags=["billing"])

FOSS_WEBHOOK_SECRET = os.getenv("FOSS_WEBHOOK_SECRET", os.getenv("WEBHOOK_SECRET", ""))


class PatchBillingBody(BaseModel):
    billing_state: str | None = None
    recurrence_active: bool | None = None
    external_customer_id: str | None = None
    plan_code: str | None = None
    activated_by: str | None = None


class ActivateBillingBody(BaseModel):
    plan_code: str = Field(..., min_length=3)
    config: dict[str, Any] | None = None
    provision_mail: bool = True
    notes: str | None = None


class FossOrderActivatedBody(BaseModel):
    order_id: int | str | None = None
    product_slug: str | None = None
    client_email: str | None = None
    domain: str | None = None
    config: dict[str, Any] | None = None


def _reader(user: auth.DeskUser = Depends(auth.get_current_user)) -> auth.DeskUser:
    if not can_read_billing(user.role):
        raise HTTPException(403, "permissão insuficiente")
    return user


def _manager(user: auth.DeskUser = Depends(auth.get_current_user)) -> auth.DeskUser:
    if not can_manage_billing(user.role):
        raise HTTPException(403, "permissão insuficiente")
    return user


@router.get("/summary")
def billing_summary(user: auth.DeskUser = Depends(_reader)):
    conn = auth.db()
    try:
        data = billing_store.summary(conn)
        if should_mask_sensitive(user.role):
            data["recent_validations"] = [
                billing_store._row_dict(r, mask=True)
                for r in conn.execute(
                    "SELECT * FROM billing_accounts ORDER BY updated_at DESC LIMIT 5"
                ).fetchall()
            ]
        return data
    finally:
        conn.close()


@router.get("/accounts")
def list_billing_accounts(
    billing_state: str = "",
    domain: str = "",
    limit: int = Query(100, ge=1, le=500),
    user: auth.DeskUser = Depends(_reader),
):
    conn = auth.db()
    try:
        mask = should_mask_sensitive(user.role)
        return billing_store.list_accounts(
            conn,
            billing_state=billing_state.strip() or None,
            domain=domain.strip() or None,
            limit=limit,
            mask=mask,
        )
    finally:
        conn.close()


@router.get("/accounts/by-domain/{domain}")
def billing_by_domain(domain: str, user: auth.DeskUser = Depends(_reader)):
    conn = auth.db()
    try:
        acc = billing_store.get_by_domain(conn, domain, mask=should_mask_sensitive(user.role))
    finally:
        conn.close()
    if not acc:
        raise HTTPException(404, "conta não encontrada")
    return acc


@router.get("/accounts/{account_id}")
def get_billing_account(account_id: int, user: auth.DeskUser = Depends(_reader)):
    conn = auth.db()
    try:
        acc = billing_store.get_account(conn, account_id, mask=should_mask_sensitive(user.role))
    finally:
        conn.close()
    if not acc:
        raise HTTPException(404, "conta não encontrada")
    return acc


@router.get("/accounts/{account_id}/activation-preview")
def billing_activation_preview(
    account_id: int,
    plan_code: str = "",
    user: auth.DeskUser = Depends(_manager),
):
    """Preview read-only — Spec 043 Fase 1."""
    from app.activation_mapper import build_activation_preview

    conn = auth.db()
    try:
        acc = billing_store.get_account(conn, account_id, mask=False)
    finally:
        conn.close()
    if not acc:
        raise HTTPException(404, "conta não encontrada")
    preview = build_activation_preview(
        acc,
        plan_code=plan_code.strip() or None,
    )
    if should_mask_sensitive(user.role):
        preview["company_profile"] = billing_store._mask_profile(preview.get("company_profile") or {})
        for key in ("email", "company_vat", "phone"):
            if preview["mapped"]["foss_client"].get(key):
                if "email" in key:
                    preview["mapped"]["foss_client"][key] = billing_store._mask_email(
                        preview["mapped"]["foss_client"][key]
                    )
                elif key == "company_vat":
                    preview["mapped"]["foss_client"][key] = billing_store._mask_tax_id(
                        preview["mapped"]["foss_client"][key]
                    )
        if preview["mapped"]["odoo_partner"].get("email"):
            preview["mapped"]["odoo_partner"]["email"] = billing_store._mask_email(
                preview["mapped"]["odoo_partner"]["email"]
            )
        if preview["mapped"]["odoo_partner"].get("vat"):
            preview["mapped"]["odoo_partner"]["vat"] = billing_store._mask_tax_id(
                preview["mapped"]["odoo_partner"]["vat"]
            )
    return preview


@router.post("/accounts/{account_id}/activate")
def billing_activate_account(
    account_id: int,
    body: ActivateBillingBody,
    user: auth.DeskUser = Depends(_manager),
):
    """Dispara FOSS → OpenPanel → Odoo → wizard — Spec 043."""
    conn = auth.db()
    try:
        billing_store.init_schema(conn)
        try:
            result = activate_billing_account(
                conn,
                account_id,
                plan_code=body.plan_code,
                config=body.config,
                provision_mail=body.provision_mail,
                notes=body.notes,
                activated_by=user.username,
            )
        except ActivationError as exc:
            msg = str(exc)
            if "já activa" in msg.lower():
                raise HTTPException(409, msg) from exc
            if "não encontrada" in msg.lower():
                raise HTTPException(404, msg) from exc
            raise HTTPException(400, msg) from exc
    finally:
        conn.close()
    status = 200 if result.get("ok") else 207
    from fastapi.responses import JSONResponse

    return JSONResponse(status_code=status, content=result)


@router.post("/webhook/foss/order-activated")
def foss_order_activated(
    body: FossOrderActivatedBody,
    request: Request,
    x_webhook_secret: str | None = Header(default=None),
):
    """Webhook FOSS pós-activação — provisiona mail-bundle (Spec 035)."""
    if FOSS_WEBHOOK_SECRET and x_webhook_secret != FOSS_WEBHOOK_SECRET:
        raise HTTPException(401, "webhook secret inválido")
    domain = (body.domain or "").strip().lower()
    if not domain:
        raise HTTPException(400, "domain obrigatório")
    conn = auth.db()
    try:
        billing_store.init_schema(conn)
        acc = billing_store.get_by_domain(conn, domain, mask=False)
        if not acc:
            raise HTTPException(404, "conta não encontrada para domínio")
        from app.vm123 import wizard_client
        from app.activation_mapper import map_wizard_mail_bundle, normalize_company_profile, resolve_plan_config

        profile = normalize_company_profile(acc.get("company_profile"), domain=domain)
        code, cfg = resolve_plan_config(body.product_slug or acc.get("plan_code"), body.config)
        mail_payload = map_wizard_mail_bundle(
            domain=domain,
            profile=profile,
            config=cfg,
            foss_order_id=body.order_id,
        )
        wizard_step = wizard_client.provision_mail_bundle(mail_payload)
        billing_store.save_provision_log(
            conn,
            int(acc["id"]),
            {
                "webhook": "order-activated",
                "order_id": body.order_id,
                "wizard": wizard_step,
            },
        )
        if body.order_id and not acc.get("external_subscription_id"):
            billing_store.patch_account(
                conn,
                int(acc["id"]),
                external_subscription_id=str(body.order_id),
            )
        return {"ok": True, "domain": domain, "wizard": wizard_step}
    finally:
        conn.close()


@router.patch("/accounts/{account_id}")
def patch_billing_account(
    account_id: int,
    body: PatchBillingBody,
    user: auth.DeskUser = Depends(_manager),
):
    conn = auth.db()
    try:
        fields = body.model_dump(exclude_none=True)
        if body.recurrence_active and not fields.get("activated_by"):
            from datetime import datetime, timezone

            fields["activated_by"] = user.username
            fields["activated_at"] = datetime.now(timezone.utc).isoformat()
        acc = billing_store.patch_account(conn, account_id, **fields)
    finally:
        conn.close()
    if not acc:
        raise HTTPException(404, "conta não encontrada")
    return acc
