from pathlib import Path
import re

from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from fastapi.responses import FileResponse

from app.api.deps import require_session
from app.models.fiscal_documents import (
    FiscalDocumentInboxItemRead,
    FiscalDocumentInboxListResponse,
    FiscalDocumentInboxStatusRead,
    FiscalDocumentRead,
    FiscalDocumentsListResponse,
    FiscalDocumentSettingsRead,
    FiscalDocumentSettingsUpdatePayload,
)
from app.services.fiscal_document_inbox_service import sync_fiscal_document_inbox
from app.services.google_workspace_client import is_google_workspace_configured
from app.services.google_workspace_store import get_google_workspace_store
from app.services.fiscal_document_service import ingest_fiscal_document
from app.services.tenant_store import FiscalDocumentInboxItemRecord, FiscalDocumentRecord, SessionIdentity, get_tenant_store


router = APIRouter()
_SIMPLE_EMAIL_PATTERN = re.compile(r"^[^\s@]+@[^\s@]+\.[^\s@]+$")
_GMAIL_READONLY_SCOPE = "https://www.googleapis.com/auth/gmail.readonly"


def _require_tenant_admin(session: SessionIdentity) -> SessionIdentity:
    if not get_tenant_store().session_has_permission(session, "fiscal_documents"):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Questo account non puo accedere a Documenti fiscali.",
        )
    return session


def _serialize_fiscal_document(record: FiscalDocumentRecord) -> FiscalDocumentRead:
    return FiscalDocumentRead(
        id=record.id,
        original_name=record.original_name,
        display_name=record.display_name,
        mime_type=record.mime_type,
        kind=record.kind,
        file_size_bytes=record.file_size_bytes,
        document_type=record.document_type,  # type: ignore[arg-type]
        document_number=record.document_number,
        document_date=record.document_date,
        supplier_name=record.supplier_name,
        total_amount=record.total_amount,
        currency=record.currency,
        summary_text=record.summary_text,
        extracted_text=record.extracted_text,
        status=record.status,  # type: ignore[arg-type]
        matching_status=record.matching_status,  # type: ignore[arg-type]
        review_status=record.review_status,  # type: ignore[arg-type]
        error_detail=record.error_detail,
        created_at=record.created_at,
        updated_at=record.updated_at,
    )


def _build_list_response(records: list[FiscalDocumentRecord]) -> FiscalDocumentsListResponse:
    items = [_serialize_fiscal_document(record) for record in records]
    return FiscalDocumentsListResponse(
        items=items,
        total_count=len(items),
        invoice_count=sum(1 for item in items if item.document_type == "invoice"),
        delivery_note_count=sum(1 for item in items if item.document_type == "delivery_note"),
        to_review_count=sum(1 for item in items if item.review_status == "to_review"),
    )


def _serialize_inbox_item(record: FiscalDocumentInboxItemRecord) -> FiscalDocumentInboxItemRead:
    return FiscalDocumentInboxItemRead(
        id=record.id,
        message_id=record.message_id,
        attachment_id=record.attachment_id,
        subject=record.subject,
        sender=record.sender,
        received_at=record.received_at,
        attachment_name=record.attachment_name,
        mime_type=record.mime_type,
        sync_status=record.sync_status,  # type: ignore[arg-type]
        document_id=record.document_id,
        error_detail=record.error_detail,
        created_at=record.created_at,
        updated_at=record.updated_at,
    )


def _build_inbox_list_response(records: list[FiscalDocumentInboxItemRecord]) -> FiscalDocumentInboxListResponse:
    items = [_serialize_inbox_item(record) for record in records]
    return FiscalDocumentInboxListResponse(
        items=items,
        total_count=len(items),
        imported_count=sum(1 for item in items if item.sync_status == "imported"),
        unsupported_count=sum(1 for item in items if item.sync_status == "unsupported"),
        error_count=sum(1 for item in items if item.sync_status == "error"),
    )


def _serialize_settings(session: SessionIdentity) -> FiscalDocumentSettingsRead:
    settings = get_tenant_store().get_fiscal_document_settings(session.tenant_id)
    return FiscalDocumentSettingsRead(
        inbound_email=settings.inbound_email,
        mailbox_configured=bool(settings.inbound_email),
        updated_at=settings.updated_at,
    )


def _normalize_inbound_email(value: str | None) -> str | None:
    normalized = (value or "").strip().lower()
    if not normalized:
        return None
    if not _SIMPLE_EMAIL_PATTERN.match(normalized):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Inserisci una casella email valida per ricevere bolle e fatture.",
        )
    return normalized


def _build_inbox_status(session: SessionIdentity) -> FiscalDocumentInboxStatusRead:
    settings = get_tenant_store().get_fiscal_document_settings(session.tenant_id)
    connection = get_google_workspace_store().get_connection(session.tenant_id, adopt_legacy_if_needed=True)
    google_configured = is_google_workspace_configured()
    granted_scopes = [item.strip() for item in (connection.scope.split() if connection and connection.scope else []) if item.strip()]
    gmail_scope_granted = _GMAIL_READONLY_SCOPE in granted_scopes
    inbound_email = settings.inbound_email
    google_account_email = connection.account_email if connection else None
    mailbox_matches_google_account = bool(
        inbound_email
        and google_account_email
        and inbound_email.strip().lower() == google_account_email.strip().lower()
    )
    ready_for_sync = bool(
        inbound_email
        and google_configured
        and connection is not None
        and gmail_scope_granted
        and mailbox_matches_google_account
    )

    if not inbound_email:
        next_step = "Configura prima la mailbox del locale che ricevera bolle e fatture."
    elif not google_configured:
        next_step = "Configura l'app Google Workspace nel backend per poter leggere la mailbox del locale."
    elif connection is None:
        next_step = "Collega l'account Google della mailbox documenti per abilitare la lettura delle email in arrivo."
    elif not gmail_scope_granted:
        next_step = "Ricollega Google e accetta anche il permesso Gmail in sola lettura per leggere le email in arrivo."
    elif not mailbox_matches_google_account:
        next_step = "La mailbox salvata non coincide con l'account Google collegato. Usa lo stesso indirizzo o collega l'account corretto."
    else:
        next_step = "Mailbox pronta. Nel prossimo step attiveremo la lettura automatica delle email in arrivo."

    return FiscalDocumentInboxStatusRead(
        mailbox_configured=bool(inbound_email),
        inbound_email=inbound_email,
        google_configured=google_configured,
        google_connected=connection is not None,
        google_account_email=google_account_email,
        gmail_scope_granted=gmail_scope_granted,
        mailbox_matches_google_account=mailbox_matches_google_account,
        ready_for_sync=ready_for_sync,
        next_step=next_step,
    )


@router.get("/settings", response_model=FiscalDocumentSettingsRead)
def get_fiscal_document_settings(session: SessionIdentity = Depends(require_session)) -> FiscalDocumentSettingsRead:
    admin_session = _require_tenant_admin(session)
    return _serialize_settings(admin_session)


@router.put("/settings", response_model=FiscalDocumentSettingsRead)
def update_fiscal_document_settings(
    payload: FiscalDocumentSettingsUpdatePayload,
    session: SessionIdentity = Depends(require_session),
) -> FiscalDocumentSettingsRead:
    admin_session = _require_tenant_admin(session)
    inbound_email = _normalize_inbound_email(payload.inbound_email)
    get_tenant_store().upsert_fiscal_document_settings(admin_session.tenant_id, inbound_email=inbound_email)
    return _serialize_settings(admin_session)


@router.get("/inbox-status", response_model=FiscalDocumentInboxStatusRead)
def get_fiscal_document_inbox_status(session: SessionIdentity = Depends(require_session)) -> FiscalDocumentInboxStatusRead:
    admin_session = _require_tenant_admin(session)
    return _build_inbox_status(admin_session)


@router.get("/inbox", response_model=FiscalDocumentInboxListResponse)
def list_fiscal_document_inbox(session: SessionIdentity = Depends(require_session)) -> FiscalDocumentInboxListResponse:
    admin_session = _require_tenant_admin(session)
    records = get_tenant_store().list_fiscal_document_inbox_items(admin_session.tenant_id)
    return _build_inbox_list_response(records)


@router.post("/inbox/sync", response_model=FiscalDocumentInboxListResponse)
async def sync_fiscal_document_inbox_route(session: SessionIdentity = Depends(require_session)) -> FiscalDocumentInboxListResponse:
    admin_session = _require_tenant_admin(session)
    await sync_fiscal_document_inbox(admin_session)
    records = get_tenant_store().list_fiscal_document_inbox_items(admin_session.tenant_id)
    return _build_inbox_list_response(records)


@router.get("", response_model=FiscalDocumentsListResponse)
def list_fiscal_documents(session: SessionIdentity = Depends(require_session)) -> FiscalDocumentsListResponse:
    admin_session = _require_tenant_admin(session)
    records = get_tenant_store().list_fiscal_documents(admin_session.tenant_id)
    return _build_list_response(records)


@router.post("/upload", response_model=FiscalDocumentsListResponse)
async def upload_fiscal_documents(
    files: list[UploadFile] = File(...),
    session: SessionIdentity = Depends(require_session),
) -> FiscalDocumentsListResponse:
    admin_session = _require_tenant_admin(session)
    if not files:
        raise HTTPException(status_code=400, detail="Seleziona almeno un documento fiscale da caricare.")

    for upload in files:
        await ingest_fiscal_document(admin_session, upload)

    records = get_tenant_store().list_fiscal_documents(admin_session.tenant_id)
    return _build_list_response(records)


@router.get("/{document_id}/download")
def download_fiscal_document(
    document_id: str,
    session: SessionIdentity = Depends(require_session),
):
    admin_session = _require_tenant_admin(session)
    record = get_tenant_store().get_fiscal_document(admin_session.tenant_id, document_id)
    if record is None:
        raise HTTPException(status_code=404, detail="Documento fiscale non trovato.")

    storage_path = Path(record.storage_path)
    if not storage_path.exists():
        raise HTTPException(status_code=404, detail="Il file non e piu disponibile sul server.")

    return FileResponse(storage_path, media_type=record.mime_type, filename=record.display_name)
