import json
from datetime import date
from typing import Annotated
from uuid import UUID

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

from app.auth.deps import CurrentUser, current_user
from app.db import get_session
from app.schemas.process import ProcessDetail, ProcessEventOut, ProcessList, ProcessOut

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


class FlagFalsePositive(BaseModel):
    reason: str | None = None


@router.get("", response_model=ProcessList)
async def list_processes(
    session: Annotated[AsyncSession, Depends(get_session)],
    _user: Annotated[CurrentUser, Depends(current_user)],
    company_id: UUID | None = None,
    source: str | None = Query(default=None, max_length=40),
    monitoring_type: str | None = Query(
        default=None, pattern="^(internal|competitor|analysis|client|related)$"
    ),
    date_from: date | None = None,
    date_to: date | None = None,
    q: str | None = Query(default=None, max_length=200),
    role: str | None = Query(default=None, max_length=100),
    tribunal: str | None = Query(default=None, max_length=200),
    active: bool | None = None,
    page: int = Query(default=1, ge=1),
    page_size: int = Query(default=50, ge=1, le=200),
) -> ProcessList:
    where: list[str] = []
    params: dict[str, object] = {}
    if company_id:
        # When browsing a specific company (incl. archived), don't hide anything.
        where.append("p.company_id = :cid")
        params["cid"] = str(company_id)
    elif active is not None:
        where.append("c.active = :act")
        params["act"] = active
    if source:
        # Accept either a single source or a comma-separated list
        # (e.g. "distribuicao,cire"). Each token is validated against
        # the known sources below.
        tokens = [t.strip() for t in source.split(",") if t.strip()]
        valid = [t for t in tokens if t in ("distribuicao", "cire")]
        if valid:
            where.append("p.source = ANY(:srcs)")
            params["srcs"] = valid
    if monitoring_type:
        where.append("c.monitoring_type = :mt")
        params["mt"] = monitoring_type
    if date_from:
        where.append("p.date_filed >= :df")
        params["df"] = date_from
    if date_to:
        where.append("p.date_filed <= :dt")
        params["dt"] = date_to
    if q:
        where.append("(p.process_number ILIKE :q OR p.tribunal ILIKE :q OR c.legal_name ILIKE :q)")
        params["q"] = f"%{q}%"
    if role:
        where.append("p.role_in_process ILIKE :role")
        params["role"] = role
    if tribunal:
        where.append("p.tribunal ILIKE :trib")
        params["trib"] = f"%{tribunal}%"
    where_sql = ("WHERE " + " AND ".join(where)) if where else ""

    total = (
        await session.execute(
            text(
                f"""
                SELECT count(*) FROM processes p
                JOIN companies c ON c.id = p.company_id
                {where_sql}
                """
            ),
            params,
        )
    ).scalar_one()

    params["limit"] = page_size
    params["offset"] = (page - 1) * page_size
    rows = (
        await session.execute(
            text(
                f"""
                SELECT p.id::text, p.company_id::text, c.legal_name, c.nif,
                       p.source, p.process_number, p.tribunal, p.juizo, p.species,
                       p.role_in_process, p.date_filed, p.first_seen_at, p.last_seen_at
                FROM processes p
                JOIN companies c ON c.id = p.company_id
                {where_sql}
                ORDER BY p.date_filed DESC, p.first_seen_at DESC
                LIMIT :limit OFFSET :offset
                """
            ),
            params,
        )
    ).all()
    items = [
        ProcessOut(
            id=r[0], company_id=r[1], company_name=r[2], company_nif=r[3],
            source=r[4], process_number=r[5], tribunal=r[6], juizo=r[7], species=r[8],
            role_in_process=r[9], date_filed=r[10], first_seen_at=r[11], last_seen_at=r[12],
        )
        for r in rows
    ]
    return ProcessList(items=items, total=total, page=page, page_size=page_size)


@router.get("/{process_id}", response_model=ProcessDetail)
async def get_process(
    process_id: UUID,
    session: Annotated[AsyncSession, Depends(get_session)],
    _user: Annotated[CurrentUser, Depends(current_user)],
) -> ProcessDetail:
    row = (
        await session.execute(
            text(
                """
                SELECT p.id::text, p.company_id::text, c.legal_name, c.nif,
                       p.source, p.process_number, p.tribunal, p.juizo, p.species,
                       p.role_in_process, p.date_filed, p.first_seen_at, p.last_seen_at,
                       p.raw
                FROM processes p
                JOIN companies c ON c.id = p.company_id
                WHERE p.id = :id
                """
            ),
            {"id": str(process_id)},
        )
    ).first()
    if not row:
        raise HTTPException(status.HTTP_404_NOT_FOUND)
    events = (
        await session.execute(
            text(
                """
                SELECT id::text, event_type, payload, occurred_at
                FROM process_events WHERE process_id = :id
                ORDER BY occurred_at DESC
                """
            ),
            {"id": str(process_id)},
        )
    ).all()
    return ProcessDetail(
        id=row[0], company_id=row[1], company_name=row[2], company_nif=row[3],
        source=row[4], process_number=row[5], tribunal=row[6], juizo=row[7], species=row[8],
        role_in_process=row[9], date_filed=row[10], first_seen_at=row[11], last_seen_at=row[12],
        raw=row[13],
        events=[
            ProcessEventOut(id=e[0], event_type=e[1], payload=e[2], occurred_at=e[3])
            for e in events
        ],
    )


@router.post("/{process_id}/flag-false-positive", status_code=status.HTTP_204_NO_CONTENT)
async def flag_false_positive(
    process_id: UUID,
    session: Annotated[AsyncSession, Depends(get_session)],
    user: Annotated[CurrentUser, Depends(current_user)],
    payload: FlagFalsePositive | None = Body(default=None),
) -> None:
    """Move a process to `false_positive_reports` and delete it. The dedup_hash
    is kept UNIQUE in the report table, so the next scrape's ingest will skip
    this exact (company,process,tribunal,date) combo instead of re-inserting."""
    row = (
        await session.execute(
            text(
                """
                SELECT p.id::text, p.dedup_hash, p.company_id::text, c.legal_name,
                       p.process_number, p.tribunal, p.source, p.raw
                FROM processes p
                JOIN companies c ON c.id = p.company_id
                WHERE p.id = :id
                """
            ),
            {"id": str(process_id)},
        )
    ).first()
    if not row:
        raise HTTPException(status.HTTP_404_NOT_FOUND)
    parties = (
        await session.execute(
            text(
                """
                SELECT json_agg(json_build_object('name', name, 'nif', nif, 'role', role))
                FROM process_parties WHERE process_id = :id
                """
            ),
            {"id": str(process_id)},
        )
    ).scalar_one_or_none()
    await session.execute(
        text(
            """
            INSERT INTO false_positive_reports (
                dedup_hash, company_id, company_legal_name, process_number,
                tribunal, source, parties_snapshot, raw_snapshot,
                flagged_by, flagged_by_email, reason
            )
            VALUES (:h, :cid, :cname, :pn, :trib, :src,
                    CAST(:parties AS JSONB), CAST(:raw AS JSONB),
                    :uid, :email, :reason)
            ON CONFLICT (dedup_hash) DO NOTHING
            """
        ),
        {
            "h": row[1],
            "cid": row[2],
            "cname": row[3],
            "pn": row[4],
            "trib": row[5],
            "src": row[6],
            "parties": None if parties is None else json.dumps(parties),
            "raw": None if row[7] is None else json.dumps(row[7]),
            "uid": str(user.id),
            "email": user.email,
            "reason": (payload.reason if payload else None),
        },
    )
    await session.execute(
        text("DELETE FROM processes WHERE id = :id"), {"id": str(process_id)}
    )
    await session.commit()
