from dataclasses import dataclass

import httpx
from fastapi import HTTPException, status
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.core.config import get_settings
from app.models.room import Room
from app.models.venue import Venue
from app.services.seed_service import enforce_layout_policy_for_venue, ensure_room_for_venue, ensure_venue_setup


@dataclass
class PortalTenantContext:
    tenant_id: str
    tenant_slug: str
    venue_name: str
    user_role: str
    permissions: tuple[str, ...]


def _extract_portal_context(payload: object) -> PortalTenantContext | None:
    if not isinstance(payload, dict):
        return None

    tenant_context = payload.get("tenant_context")
    current_user = payload.get("current_user")
    if not isinstance(tenant_context, dict) or not isinstance(current_user, dict):
        return None

    tenant = tenant_context.get("tenant")
    if not isinstance(tenant, dict):
        return None

    tenant_id = str(tenant.get("id") or "").strip()
    tenant_slug = str(tenant.get("slug") or "").strip()
    venue_name = ""

    venues = tenant_context.get("venues")
    if isinstance(venues, list):
        for venue in venues:
            if not isinstance(venue, dict):
                continue
            venue_name = str(venue.get("name") or "").strip()
            if venue_name:
                break

    if not venue_name:
        venue_name = str(tenant.get("name") or "").strip()

    user_role = str(current_user.get("role") or "").strip().lower()
    permissions_raw = current_user.get("permissions")
    permissions: list[str] = []
    if isinstance(permissions_raw, list):
        for item in permissions_raw:
            value = str(item or "").strip().lower()
            if value and value not in permissions:
                permissions.append(value)

    if not tenant_id or not tenant_slug or not venue_name or not user_role:
        return None

    return PortalTenantContext(
        tenant_id=tenant_id,
        tenant_slug=tenant_slug,
        venue_name=venue_name,
        user_role=user_role,
        permissions=tuple(permissions),
    )


def fetch_portal_tenant_context(session_token: str) -> PortalTenantContext:
    cleaned_token = session_token.strip()
    if not cleaned_token:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Sessione portale mancante")

    settings = get_settings()
    endpoint = f"{settings.assistant_api_base_url.rstrip('/')}/auth/me"

    try:
        with httpx.Client(timeout=settings.assistant_timeout_seconds) as client:
            response = client.get(endpoint, headers={"Authorization": f"Bearer {cleaned_token}"})
    except httpx.HTTPError as exc:
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Impossibile contattare il backend del portale per sincronizzare il locale",
        ) from exc

    if response.status_code == status.HTTP_401_UNAUTHORIZED:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Sessione portale non valida o scaduta")
    if response.status_code == status.HTTP_403_FORBIDDEN:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Questo account non puo accedere a Prenotazioni")
    if not response.is_success:
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Sincronizzazione del locale non riuscita dal backend del portale",
        )

    portal_context = _extract_portal_context(response.json())
    if portal_context is None:
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Il backend del portale non ha restituito un contesto tenant valido",
        )

    return portal_context


def ensure_portal_tenant_venue(*, portal_context: PortalTenantContext, db: Session) -> Venue:
    venue = db.scalar(select(Venue).where(Venue.portal_tenant_id == portal_context.tenant_id))
    if venue is not None:
        updates_applied = False
        policy_before = venue.layout_policy_applied
        room_existed = db.scalar(select(Room.id).where(Room.venue_id == venue.id).limit(1)) is not None

        if venue.name != portal_context.venue_name:
            venue.name = portal_context.venue_name
            updates_applied = True
        if venue.portal_tenant_slug != portal_context.tenant_slug:
            venue.portal_tenant_slug = portal_context.tenant_slug
            updates_applied = True

        room = ensure_room_for_venue(venue.id, db)
        enforce_layout_policy_for_venue(venue, room, db)

        if updates_applied or not policy_before or not room_existed:
            db.commit()
            db.refresh(venue)
        return venue

    venue, _, _, _ = ensure_venue_setup(
        db,
        venue_name=portal_context.venue_name,
        portal_tenant_id=portal_context.tenant_id,
        portal_tenant_slug=portal_context.tenant_slug,
    )
    return venue


def resolve_current_venue(*, session_token: str | None, db: Session) -> Venue:
    cleaned_token = (session_token or "").strip()
    if not cleaned_token:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Sessione portale richiesta")

    portal_context = fetch_portal_tenant_context(cleaned_token)
    if portal_context.user_role == "staff" and "prenotazioni" not in portal_context.permissions:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Questo account non puo accedere a Prenotazioni",
        )
    return ensure_portal_tenant_venue(portal_context=portal_context, db=db)


def sync_venue_name_from_portal(*, session_token: str, db: Session) -> Venue:
    return resolve_current_venue(session_token=session_token, db=db)
