from __future__ import annotations

from dataclasses import dataclass

from fastapi import Depends, Header, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials

from app.core.dependencies import bearer, get_current_user
from app.core.security import decode_access_token
from app.modules.sync import repository as sync_repo
from app.modules.sync.schemas import HubAuthContext, TenantContext
from app.modules.users.models import User


@dataclass
class HubPrincipal:
    farm_hub_id: str
    hub_site_id: str
    enterprise_id: str


def get_hub_auth(authorization: str | None = Header(default=None)) -> HubPrincipal:
    if not authorization or not authorization.startswith("Hub "):
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="HUB_UNAUTHORIZED")
    token = authorization[4:].strip()
    if ":" not in token:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="HUB_UNAUTHORIZED")
    hub_site_id, api_key = token.split(":", 1)
    hub = sync_repo.verify_hub_credential(hub_site_id.strip(), api_key.strip())
    if not hub:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="HUB_UNAUTHORIZED")
    return HubPrincipal(farm_hub_id=hub.id, hub_site_id=hub.hub_site_id, enterprise_id=hub.enterprise_id)


def get_tenant_context(
    enterprise_id: str,
    user: User = Depends(get_current_user),
    credentials: HTTPAuthorizationCredentials | None = Depends(bearer),
) -> TenantContext:
    if user.is_superuser:
        return TenantContext(
            enterprise_id=enterprise_id,
            enterprise_role="admin",
            user_id=user.id,
            farm_hub_ids=None,
        )
    if credentials:
        try:
            payload = decode_access_token(credentials.credentials)
            if payload.get("enterprise_id") == enterprise_id:
                farm_ids = payload.get("farm_ids")
                return TenantContext(
                    enterprise_id=enterprise_id,
                    enterprise_role=payload.get("enterprise_role", "viewer"),  # type: ignore[arg-type]
                    user_id=user.id,
                    farm_hub_ids=farm_ids,
                )
        except Exception:
            pass
    member = sync_repo.get_member(user.id, enterprise_id)
    if not member:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="ENTERPRISE_FORBIDDEN")
    farm_ids = None if member.role == "admin" else sync_repo.list_farm_access(user.id, enterprise_id)
    return TenantContext(
        enterprise_id=enterprise_id,
        enterprise_role=member.role,  # type: ignore[arg-type]
        user_id=user.id,
        farm_hub_ids=farm_ids,
    )


def require_farm_access(
    farm_hub_id: str,
    enterprise_id: str,
    tenant: TenantContext,
) -> None:
    if tenant.enterprise_role == "admin" or tenant.farm_hub_ids is None:
        return
    if farm_hub_id not in (tenant.farm_hub_ids or []):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="FARM_ACCESS_FORBIDDEN")


def require_enterprise_admin(tenant: TenantContext = Depends(get_tenant_context)) -> TenantContext:
    if tenant.enterprise_role != "admin":
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="ENTERPRISE_ADMIN_ONLY")
    return tenant


def require_enterprise_zootech(tenant: TenantContext = Depends(get_tenant_context)) -> TenantContext:
    if tenant.enterprise_role not in ("admin", "zootech"):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="ENTERPRISE_ZOOTECH_ONLY")
    return tenant
