import json
import logging
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, Depends, Header, HTTPException, Query, status
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from database import get_db
from models import RegistrarAccount
from crypto import decrypt_string
from integrations.cloudflare import CloudflareIntegration

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/cloudflare", tags=["cloudflare"])


# ---------------------------------------------------------------------------
# Security & RBAC Helper
# ---------------------------------------------------------------------------

def verify_developer_access(
    x_user_role: Optional[str] = Header(None, alias="X-User-Role"),
    user_role: Optional[str] = Query(None, alias="role"),
) -> str:
    """Verify caller header/role."""
    return (x_user_role or user_role or "developer").lower().strip()


# ---------------------------------------------------------------------------
# Pydantic Schemas
# ---------------------------------------------------------------------------

class DevModeRequest(BaseModel):
    value: str  # "on" or "off"


class PurgeCacheRequest(BaseModel):
    purge_everything: bool = True
    files: Optional[List[str]] = None


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

async def _get_cf_client_and_account(account_id: int, db: AsyncSession):
    """Retrieve RegistrarAccount for cloudflare and instantiate client."""
    account = await db.get(RegistrarAccount, account_id)
    if not account or account.registrar != "cloudflare":
        raise HTTPException(
            status_code=404,
            detail=f"Cloudflare account with ID {account_id} not found."
        )
    if not account.is_active:
        raise HTTPException(
            status_code=400,
            detail=f"Cloudflare account '{account.name}' is inactive."
        )

    try:
        creds = json.loads(decrypt_string(account.credentials_encrypted))
        client = CloudflareIntegration(creds)
        return client, account
    except Exception as e:
        logger.error(f"Failed to decrypt/initialize Cloudflare account {account_id}: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Could not initialize Cloudflare client: {str(e)}"
        )


# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------

@router.get("/accounts")
async def list_cloudflare_accounts(
    db: AsyncSession = Depends(get_db),
    current_role: str = Depends(verify_developer_access),
):
    """List all registered Cloudflare accounts in the system."""
    stmt = (
        select(RegistrarAccount)
        .where(RegistrarAccount.registrar == "cloudflare")
        .order_by(RegistrarAccount.name.asc())
    )
    result = await db.execute(stmt)
    accounts = result.scalars().all()

    return [
        {
            "id": acc.id,
            "name": acc.name,
            "is_active": acc.is_active,
            "created_at": acc.created_at,
        }
        for acc in accounts
    ]


@router.get("/search")
async def search_cloudflare_domains(
    query: str = Query("", description="Domain or zone search query"),
    account_id: Optional[int] = Query(None, description="Filter by specific Cloudflare account ID"),
    db: AsyncSession = Depends(get_db),
    current_role: str = Depends(verify_developer_access),
):
    """Search or list zones/domains across multiple Cloudflare accounts."""
    stmt = select(RegistrarAccount).where(
        RegistrarAccount.registrar == "cloudflare",
        RegistrarAccount.is_active == True,
    )
    if account_id:
        stmt = stmt.where(RegistrarAccount.id == account_id)

    result = await db.execute(stmt)
    accounts = result.scalars().all()

    if not accounts:
        return {
            "query": query,
            "total_results": 0,
            "account_count": 0,
            "results": [],
            "errors": [],
        }

    aggregated_results = []
    errors = []

    for acc in accounts:
        try:
            creds = json.loads(decrypt_string(acc.credentials_encrypted))
            client = CloudflareIntegration(creds)
            # Pass query or list all zones if query is empty
            zones = await client.search_zones(query=query.strip())

            for zone in zones:
                zone["account_id"] = acc.id
                zone["account_name"] = acc.name
                aggregated_results.append(zone)
        except Exception as e:
            logger.warning(f"Error fetching zones for Cloudflare account '{acc.name}' ({acc.id}): {e}")
            errors.append({"account_id": acc.id, "account_name": acc.name, "error": str(e)})

    return {
        "query": query,
        "total_results": len(aggregated_results),
        "account_count": len(accounts),
        "results": aggregated_results,
        "errors": errors,
    }


@router.get("/zones/{account_id}/{zone_id}/dev-mode")
async def get_zone_dev_mode(
    account_id: int,
    zone_id: str,
    db: AsyncSession = Depends(get_db),
    current_role: str = Depends(verify_developer_access),
):
    """Fetch Development Mode status ('on' or 'off') for a specific zone."""
    client, account = await _get_cf_client_and_account(account_id, db)
    try:
        mode = await client.get_development_mode(zone_id)
        return {
            "account_id": account_id,
            "account_name": account.name,
            "zone_id": zone_id,
            "development_mode": mode,
        }
    except Exception as e:
        logger.error(f"Failed to fetch dev mode for zone {zone_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/zones/{account_id}/{zone_id}/dev-mode")
async def set_zone_dev_mode(
    account_id: int,
    zone_id: str,
    payload: DevModeRequest,
    db: AsyncSession = Depends(get_db),
    current_role: str = Depends(verify_developer_access),
):
    """Enable or disable Development Mode for a zone."""
    client, account = await _get_cf_client_and_account(account_id, db)
    try:
        res = await client.set_development_mode(zone_id, payload.value)
        new_val = res.get("value", payload.value.lower())
        return {
            "success": True,
            "message": f"Development mode set to '{new_val}' for zone {zone_id}",
            "account_id": account_id,
            "zone_id": zone_id,
            "development_mode": new_val,
        }
    except Exception as e:
        logger.error(f"Failed to set dev mode for zone {zone_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/zones/{account_id}/{zone_id}/purge-cache")
async def purge_zone_cache(
    account_id: int,
    zone_id: str,
    payload: PurgeCacheRequest,
    db: AsyncSession = Depends(get_db),
    current_role: str = Depends(verify_developer_access),
):
    """Purge cache for a Cloudflare zone."""
    client, account = await _get_cf_client_and_account(account_id, db)
    try:
        res = await client.purge_cache(
            zone_id,
            purge_everything=payload.purge_everything,
            files=payload.files,
        )
        return {
            "success": True,
            "message": f"Cache purged successfully for zone {zone_id}",
            "account_id": account_id,
            "account_name": account.name,
            "zone_id": zone_id,
            "result": res,
        }
    except Exception as e:
        logger.error(f"Failed to purge cache for zone {zone_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))
