from __future__ import annotations

from fastapi import APIRouter, Depends, HTTPException, Query, status

from app.core.dependencies import require_superuser
from app.modules.sync import service
from app.modules.sync.schemas import (
    AckChangesRequest,
    AckChangesResponse,
    HeartbeatRequest,
    HeartbeatResponse,
    PairConfirmRequest,
    PairConfirmResponse,
    PairStartRequest,
    PullChangesRequest,
    PullChangesResponse,
    PushChangesRequest,
    PushChangesResponse,
    ResolveConflictRequest,
    SyncCapabilitiesResponse,
)
from app.modules.sync.service import SyncServiceError
from app.modules.sync.tenant import (
    HubPrincipal,
    TenantContext,
    get_hub_auth,
    get_tenant_context,
    require_enterprise_zootech,
)
from app.modules.users.models import User
from app.core.dependencies import get_current_user

router = APIRouter()


def _map_error(exc: SyncServiceError) -> HTTPException:
    code = str(exc)
    status_code = status.HTTP_400_BAD_REQUEST
    if code in {"ENTERPRISE_ADMIN_ONLY", "ENTERPRISE_FORBIDDEN", "ENTERPRISE_ZOOTECH_ONLY"}:
        status_code = status.HTTP_403_FORBIDDEN
    if code in {"INVALID_PAIRING_CODE", "PAIRING_CODE_EXPIRED"}:
        status_code = status.HTTP_400_BAD_REQUEST
    if code == "CONFLICT_NOT_FOUND":
        status_code = status.HTTP_404_NOT_FOUND
    return HTTPException(status_code=status_code, detail=code)


@router.post("/reports/push")
def reports_push(body: PushChangesRequest, hub: HubPrincipal = Depends(get_hub_auth)) -> PushChangesResponse:
    """Report domain ingest — same durability path as global push."""
    return service.push_changes(hub, body)


@router.post("/reports/refresh")
def reports_refresh(
    farm_hub_id: str = Query(...),
    enterprise_id: str = Query(...),
    tenant: TenantContext = Depends(require_enterprise_zootech),
):
    if tenant.farm_hub_ids is not None and farm_hub_id not in tenant.farm_hub_ids:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="FARM_ACCESS_FORBIDDEN")
    return {"status": "queued", "farm_hub_id": farm_hub_id, "enterprise_id": tenant.enterprise_id}


@router.get("/metrics")
def sync_metrics(
    enterprise_id: str = Query(...),
    tenant: TenantContext = Depends(require_enterprise_zootech),
):
    if tenant.enterprise_id != enterprise_id:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="ENTERPRISE_FORBIDDEN")
    return service.get_sync_metrics(enterprise_id)


@router.get("/capabilities", response_model=SyncCapabilitiesResponse)
def capabilities() -> SyncCapabilitiesResponse:
    return service.get_capabilities()


@router.post("/changes/push", response_model=PushChangesResponse)
def push_changes(body: PushChangesRequest, hub: HubPrincipal = Depends(get_hub_auth)) -> PushChangesResponse:
    return service.push_changes(hub, body)


@router.post("/changes/pull", response_model=PullChangesResponse)
def pull_changes(body: PullChangesRequest, hub: HubPrincipal = Depends(get_hub_auth)) -> PullChangesResponse:
    return service.pull_changes(hub, body)


@router.post("/changes/ack", response_model=AckChangesResponse)
def ack_changes(body: AckChangesRequest, hub: HubPrincipal = Depends(get_hub_auth)) -> AckChangesResponse:
    return service.ack_changes(hub, body)


@router.post("/hubs/heartbeat", response_model=HeartbeatResponse)
def heartbeat(body: HeartbeatRequest, hub: HubPrincipal = Depends(get_hub_auth)) -> HeartbeatResponse:
    return service.hub_heartbeat(hub, body)


@router.get("/hub/conflicts")
def list_hub_conflicts(hub: HubPrincipal = Depends(get_hub_auth)):
    return service.list_conflicts_for_enterprise(hub.enterprise_id)


@router.get("/hub/conflicts/{conflict_id}")
def get_hub_conflict(conflict_id: str, hub: HubPrincipal = Depends(get_hub_auth)):
    try:
        return service.get_conflict_detail(conflict_id, hub.enterprise_id)
    except SyncServiceError as exc:
        raise _map_error(exc) from exc


@router.post("/hub/conflicts/{conflict_id}/resolve")
def resolve_hub_conflict(
    conflict_id: str,
    body: ResolveConflictRequest,
    hub: HubPrincipal = Depends(get_hub_auth),
):
    try:
        service.resolve_conflict(conflict_id, hub.enterprise_id, f"hub:{hub.hub_site_id}", body)
        return {"status": "ok"}
    except SyncServiceError as exc:
        raise _map_error(exc) from exc


@router.get("/conflicts")
def list_conflicts(
    enterprise_id: str = Query(...),
    tenant: TenantContext = Depends(require_enterprise_zootech),
):
    if tenant.enterprise_id != enterprise_id and tenant.enterprise_role != "admin":
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="ENTERPRISE_FORBIDDEN")
    return service.list_conflicts_for_enterprise(enterprise_id)


@router.get("/conflicts/{conflict_id}")
def get_conflict(
    conflict_id: str,
    enterprise_id: str = Query(...),
    tenant: TenantContext = Depends(require_enterprise_zootech),
):
    try:
        return service.get_conflict_detail(conflict_id, enterprise_id)
    except SyncServiceError as exc:
        raise _map_error(exc) from exc


@router.post("/conflicts/{conflict_id}/resolve")
def resolve_conflict(
    conflict_id: str,
    body: ResolveConflictRequest,
    enterprise_id: str = Query(...),
    tenant: TenantContext = Depends(require_enterprise_zootech),
):
    try:
        service.resolve_conflict(conflict_id, enterprise_id, tenant.user_id or "", body)
        return {"status": "ok"}
    except SyncServiceError as exc:
        raise _map_error(exc) from exc


enterprise_router = APIRouter()


@enterprise_router.post("/pair/start")
def pair_start(body: PairStartRequest, user: User = Depends(get_current_user)):
    try:
        return service.start_pairing(user, body)
    except SyncServiceError as exc:
        raise _map_error(exc) from exc


@enterprise_router.post("/pair/confirm", response_model=PairConfirmResponse)
def pair_confirm(body: PairConfirmRequest) -> PairConfirmResponse:
    try:
        return service.confirm_pairing(body)
    except SyncServiceError as exc:
        raise _map_error(exc) from exc
