import math

from fastapi import APIRouter, HTTPException, Query
from pydantic import EmailStr

from app.services.ledger_auth import require_firm, require_authenticated
from app.services.notifications import resolve_client_id
from app.services import users as user_service
from app.services.invoice_workflow import WorkflowError
from app.services.snelstart.exceptions import SnelStartError, http_status_for
from app.store import (
    AnswerQueryRequest,
    ApproveInvoiceRequest,
    BulkInvoiceRequest,
    ClientRuleRequest,
    CreateClientRequest,
    CreateQueryRequest,
    InvoiceStatusRequest,
    LedgerState,
    RejectInvoiceRequest,
    SettingsPatchRequest,
    SyncInvoiceRequest,
    TxStatusRequest,
    UpdateInvoiceRequest,
    UpdateSuggestionRequest,
    store,
)

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


def _workflow_http(exc: WorkflowError) -> HTTPException:
    return HTTPException(status_code=400, detail=str(exc))


@router.get("/state", response_model=LedgerState)
def get_state(
    platform: str = Query(pattern="^(firm|client)$"),
    email: EmailStr = Query(...),
) -> LedgerState:
    email_n = str(email).lower()
    if platform == "client":
        user_service.provision_client_account("client", email_n)
    client_id = resolve_client_id(platform, email_n)
    return store.snapshot_for(platform, client_id)


@router.get("/invoices")
def list_invoices(
    platform: str = Query(pattern="^(firm|client)$"),
    email: EmailStr = Query(...),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=50),
    search: str | None = None,
    status: str | None = None,
    client: str | None = None,
    clientId: str | None = None,
    confidence: str | None = None,
    date: str | None = None,
):
    require_firm(platform=platform, email=str(email))
    result = store.list_invoices_page(
        client_id=clientId or client,
        search=search,
        status=status,
        confidence=confidence,
        uploaded_at=date,
        page=page,
        page_size=page_size,
    )
    total = result["total"]
    total_pages = math.ceil(total / page_size) if total else 0
    return {
        "items": result["items"],
        "page": page,
        "page_size": page_size,
        "total": total,
        "total_pages": total_pages,
        "has_any": result["has_any"],
    }


@router.patch("/invoices/{invoice_id}")
def update_invoice(invoice_id: str, payload: UpdateInvoiceRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.update_invoice(invoice_id, payload.patch, actor=actor)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Document not found") from exc
    except WorkflowError as exc:
        raise _workflow_http(exc) from exc


@router.patch("/invoices/{invoice_id}/suggestion")
def update_suggestion(invoice_id: str, payload: UpdateSuggestionRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor or payload.editedBy)
    try:
        return store.update_suggestion(invoice_id, payload.patch, actor)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Document not found") from exc
    except WorkflowError as exc:
        raise _workflow_http(exc) from exc


@router.post("/invoices/{invoice_id}/status")
def set_invoice_status(invoice_id: str, payload: InvoiceStatusRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.set_invoice_status(invoice_id, payload.status, actor=actor)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Document not found") from exc
    except WorkflowError as exc:
        raise _workflow_http(exc) from exc


@router.post("/invoices/{invoice_id}/approve")
def approve_invoice(invoice_id: str, payload: ApproveInvoiceRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.approve_invoice(invoice_id, actor=actor)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Document not found") from exc
    except WorkflowError as exc:
        raise _workflow_http(exc) from exc


@router.post("/invoices/{invoice_id}/reject")
def reject_invoice(invoice_id: str, payload: RejectInvoiceRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.reject_invoice(invoice_id, actor=actor, reason=payload.reason)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Document not found") from exc
    except WorkflowError as exc:
        raise _workflow_http(exc) from exc


@router.post("/invoices/{invoice_id}/sync")
def sync_invoice(invoice_id: str, payload: SyncInvoiceRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.sync_invoice(invoice_id, actor=actor)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Document not found") from exc
    except SnelStartError as exc:
        raise HTTPException(status_code=http_status_for(exc), detail=exc.user_message) from exc
    except WorkflowError as exc:
        raise _workflow_http(exc) from exc


@router.post("/invoices/bulk/approve")
def bulk_approve(payload: BulkInvoiceRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.bulk_approve(payload.ids, actor=actor)
    except WorkflowError as exc:
        raise _workflow_http(exc) from exc


@router.post("/invoices/bulk/needs-review")
def bulk_needs_review(payload: BulkInvoiceRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    return store.bulk_needs_review(payload.ids, actor=actor)


@router.post("/queries")
def create_query(payload: CreateQueryRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    if not payload.question.strip():
        raise HTTPException(status_code=400, detail="Please enter a question")
    if not any(c.id == payload.clientId for c in store.snapshot().clients):
        raise HTTPException(status_code=404, detail="Client not found")
    try:
        return store.create_query(payload, actor=actor)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post("/queries/{query_id}/answer")
def answer_query_legacy(query_id: str, payload: AnswerQueryRequest):
    platform, email = require_authenticated(platform=payload.platform, email=payload.email)
    if platform != "client":
        raise HTTPException(status_code=403, detail="Only clients can answer questions")
    client_id = resolve_client_id(platform, email)
    try:
        query = store._find_query(query_id)  # noqa: SLF001
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Query not found") from exc
    if not client_id or query.clientId != client_id:
        raise HTTPException(status_code=403, detail="You do not have access to this question")
    try:
        return store.answer_query(query_id, payload)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Query not found") from exc
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post("/queries/{query_id}/approve")
def approve_query_legacy(query_id: str, payload: ApproveInvoiceRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.resolve_query(query_id, actor=actor)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Query not found") from exc
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post("/transactions/{tx_id}/status")
def set_tx_status(tx_id: str, payload: TxStatusRequest):
    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.set_tx_status(tx_id, payload.status, actor=actor)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Transaction not found") from exc


@router.patch("/settings")
def update_settings(payload: SettingsPatchRequest):
    return store.update_settings(payload.patch)


@router.patch("/settings/client-rules/{client_id}")
def set_client_rule(client_id: str, payload: ClientRuleRequest):
    return store.set_client_rule(client_id, payload.rule)


@router.get("/clients")
def list_clients(
    platform: str = Query(pattern="^(firm|client)$"),
    email: EmailStr = Query(...),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=50),
    search: str | None = None,
    connected: bool = False,
):
    require_firm(platform=platform, email=str(email))
    result = store.list_clients(
        search=search,
        connected_only=connected,
        page=page,
        page_size=page_size,
    )
    total = result["total"]
    total_pages = math.ceil(total / page_size) if total else 0
    return {
        "items": result["items"],
        "page": page,
        "page_size": page_size,
        "total": total,
        "total_pages": total_pages,
    }


@router.get("/clients/{client_id}")
def get_client_overview(
    client_id: str,
    platform: str = Query(pattern="^(firm|client)$"),
    email: EmailStr = Query(...),
):
    require_firm(platform=platform, email=str(email))
    from app.services.client_overview import get_firm_client_overview

    overview = get_firm_client_overview(client_id)
    if not overview:
        raise HTTPException(status_code=404, detail="Client not found")
    return overview


@router.post("/clients")
def create_client(
    payload: CreateClientRequest,
    platform: str = Query(pattern="^(firm|client)$"),
    email: EmailStr = Query(...),
):
    require_firm(platform=platform, email=str(email))
    from app.services.users import create_firm_managed_client

    try:
        created = create_firm_managed_client(
            full_name=payload.name,
            company_name=payload.company_name,
            portal_email=str(payload.email),
            password=payload.password,
            kvk=payload.kvk,
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    return created
