"""
routes_admin_surface.py
───────────────────────
Admin Control Plane API

Roles: Only 'admin' or 'superadmin' may access these endpoints.

Endpoints:
  GET  /admin-surface/health           — system health overview
  GET  /admin-surface/queues           — queue depth snapshot
  GET  /admin-surface/installs         — surface session / install overview
  POST /admin-surface/actions          — submit an admin action
  GET  /admin-surface/actions          — list admin action history
  GET  /admin-surface/actions/{id}     — get a specific action
  GET  /admin-surface/logs             — recent audit event log
  GET  /admin-surface/templates        — template catalog summary (admin view)
  POST /admin-surface/templates/{id}/publish  — publish a template
  POST /admin-surface/templates/{id}/archive  — archive a template
  GET  /admin-surface/synthetic-jobs   — list synthetic generation jobs
  POST /admin-surface/synthetic-jobs/{id}/cancel — cancel a synthetic job
"""

from __future__ import annotations

import json
import logging
import uuid
from datetime import datetime, timezone
from typing import Any, Optional

from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
from pydantic import BaseModel, Field

from backend.auth.dependencies import get_current_user

logger = logging.getLogger("velocity.admin_surface")

router = APIRouter()

# ── RBAC guard ────────────────────────────────────────────────────────────────

ADMIN_ROLES = {"admin", "superadmin", "ADMIN", "SUPERADMIN"}


def require_admin(user=Depends(get_current_user)):
    normalized_role = user.role.upper()
    if normalized_role not in {"ADMIN", "SUPERADMIN"}:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Admin access required.",
        )
    return user


# ── Helpers ───────────────────────────────────────────────────────────────────

def _pool(request: Request):
    pool = request.app.state.db_pool
    if pool is None:
        raise HTTPException(status_code=503, detail="Database unavailable.")
    return pool


# ── Pydantic Models ───────────────────────────────────────────────────────────

VALID_ACTION_TYPES = {
    "user_create", "user_deactivate", "user_role_change",
    "tenant_config_update", "inventory_batch_approve", "inventory_batch_reject",
    "template_publish", "template_archive",
    "synthetic_job_trigger", "synthetic_job_cancel",
    "system_health_check", "queue_drain", "debug_event_export",
    "install_register", "install_deregister",
}


class AdminActionRequest(BaseModel):
    action_type: str
    target_type: str
    target_id: str
    payload: dict = Field(default_factory=dict)
    idempotency_key: Optional[str] = None


# ── System Health ─────────────────────────────────────────────────────────────

@router.get("/health", summary="System health overview")
async def get_health(
    request: Request,
    admin=Depends(require_admin),
):
    """
    Returns an aggregated health snapshot covering DB pool, queue depths,
    and basic surface session counts.
    """
    pool = _pool(request)
    async with pool.acquire() as conn:
        # DB round-trip latency
        import time
        t0 = time.monotonic()
        await conn.fetchval("SELECT 1")
        db_latency_ms = round((time.monotonic() - t0) * 1000, 2)

        # Pending jobs
        pending_transcriptions = await conn.fetchval(
            "SELECT COUNT(*) FROM edge_transcription_jobs WHERE status='pending'"
        )
        pending_synthetic_jobs = await conn.fetchval(
            "SELECT COUNT(*) FROM oracle_synthetic_generation_jobs WHERE status IN ('pending','running')"
        )
        pending_admin_actions = await conn.fetchval(
            "SELECT COUNT(*) FROM admin_action_events WHERE status='pending'"
        )
        pending_inventory_batches = await conn.fetchval(
            "SELECT COUNT(*) FROM inventory_import_batches WHERE status IN ('pending','validating','processing')"
        )

        # Active surface sessions (last 30 min)
        active_sessions = await conn.fetchval(
            "SELECT COUNT(*) FROM surface_sessions WHERE last_active_at > NOW() - INTERVAL '30 minutes'"
        )

        # Surface breakdown
        surface_breakdown = await conn.fetch(
            """
            SELECT surface_type, COUNT(*) as count
            FROM surface_sessions
            WHERE last_active_at > NOW() - INTERVAL '30 minutes'
            GROUP BY surface_type
            """
        )

    return {
        "status": "ok",
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "database": {
            "connected": True,
            "latency_ms": db_latency_ms,
        },
        "queues": {
            "pending_transcriptions": pending_transcriptions,
            "pending_synthetic_jobs": pending_synthetic_jobs,
            "pending_admin_actions": pending_admin_actions,
            "pending_inventory_batches": pending_inventory_batches,
        },
        "active_sessions": {
            "total": active_sessions,
            "by_surface": {r["surface_type"]: r["count"] for r in surface_breakdown},
        },
    }


# ── Queue Visibility ──────────────────────────────────────────────────────────

@router.get("/queues", summary="Queue depth snapshot")
async def get_queues(
    request: Request,
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        transcription_queue = await conn.fetch(
            """
            SELECT status, COUNT(*) as count
            FROM edge_transcription_jobs
            GROUP BY status ORDER BY status
            """
        )
        synthetic_queue = await conn.fetch(
            """
            SELECT status, COUNT(*) as count
            FROM oracle_synthetic_generation_jobs
            GROUP BY status ORDER BY status
            """
        )
        inventory_queue = await conn.fetch(
            """
            SELECT status, COUNT(*) as count
            FROM inventory_import_batches
            GROUP BY status ORDER BY status
            """
        )
        admin_queue = await conn.fetch(
            """
            SELECT status, COUNT(*) as count
            FROM admin_action_events
            GROUP BY status ORDER BY status
            """
        )
    return {
        "transcription_jobs": {r["status"]: r["count"] for r in transcription_queue},
        "synthetic_jobs": {r["status"]: r["count"] for r in synthetic_queue},
        "inventory_batches": {r["status"]: r["count"] for r in inventory_queue},
        "admin_actions": {r["status"]: r["count"] for r in admin_queue},
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }


# ── Install / Surface Overview ────────────────────────────────────────────────

@router.get("/installs", summary="Surface session and install overview")
async def get_installs(
    request: Request,
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        rows = await conn.fetch(
            """
            SELECT surface_type, app_version, COUNT(*) as session_count,
                   MAX(last_active_at) as last_seen
            FROM surface_sessions
            GROUP BY surface_type, app_version
            ORDER BY surface_type, app_version
            """
        )
    return {
        "installs": [dict(r) for r in rows],
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }


# ── Admin Actions ─────────────────────────────────────────────────────────────

@router.post("/actions", status_code=status.HTTP_201_CREATED, summary="Submit an admin action")
async def submit_action(
    request: Request,
    body: AdminActionRequest,
    admin=Depends(require_admin),
):
    """
    Submit a bounded admin action. All actions are persisted with full audit trail.
    Supported action_types are enumerated in VALID_ACTION_TYPES.

    Actions are not auto-executed — they transition to 'pending' and must be
    processed by the appropriate backend job or confirmed by a second admin.
    (This prevents destructive mass-actions from running unreviewed.)
    """
    if body.action_type not in VALID_ACTION_TYPES:
        raise HTTPException(400, f"Invalid action_type. Valid: {sorted(VALID_ACTION_TYPES)}")

    action_id = body.idempotency_key or str(uuid.uuid4())
    pool = _pool(request)
    async with pool.acquire() as conn:
        try:
            row = await conn.fetchrow(
                """
                INSERT INTO admin_action_events (
                    tenant_id, action_id, action_type, target_type, target_id,
                    requested_by, payload
                ) VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb)
                RETURNING action_event_id, status, created_at
                """,
                admin.role, action_id, body.action_type, body.target_type,
                body.target_id, admin.user_id, json.dumps(body.payload),
            )
        except Exception as exc:
            if "unique" in str(exc).lower():
                raise HTTPException(409, "Action with this idempotency key already exists")
            raise

    logger.info(
        "Admin action submitted: %s by %s → %s/%s",
        body.action_type, admin.user_id, body.target_type, body.target_id,
    )
    return {
        "action_event_id": str(row["action_event_id"]),
        "action_id": action_id,
        "status": row["status"],
        "created_at": str(row["created_at"]),
    }


@router.get("/actions", summary="List admin action history")
async def list_actions(
    request: Request,
    action_type: Optional[str] = Query(None),
    status_filter: Optional[str] = Query(None, alias="status"),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    admin=Depends(require_admin),
):
    pool = _pool(request)
    where = "WHERE tenant_id = $1"
    params: list[Any] = [admin.role]
    idx = 2

    if action_type:
        where += f" AND action_type = ${idx}"; params.append(action_type); idx += 1
    if status_filter:
        where += f" AND status = ${idx}"; params.append(status_filter); idx += 1

    async with pool.acquire() as conn:
        rows = await conn.fetch(
            f"""
            SELECT action_event_id, action_id, action_type, target_type, target_id,
                   requested_by, status, result_message, executed_at, created_at
            FROM admin_action_events
            {where}
            ORDER BY created_at DESC
            LIMIT ${idx} OFFSET ${idx+1}
            """,
            *params, limit, offset,
        )
        total = await conn.fetchval(
            f"SELECT COUNT(*) FROM admin_action_events {where}", *params,
        )
    return {"total": total, "limit": limit, "offset": offset, "actions": [dict(r) for r in rows]}


@router.get("/actions/{action_event_id}", summary="Get a specific admin action")
async def get_action(
    action_event_id: str,
    request: Request,
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        row = await conn.fetchrow(
            "SELECT * FROM admin_action_events WHERE action_event_id=$1 AND tenant_id=$2",
            action_event_id, admin.role,
        )
    if not row:
        raise HTTPException(404, "Admin action not found")
    return dict(row)


# ── Audit Log ─────────────────────────────────────────────────────────────────

@router.get("/logs", summary="Recent Oracle audit events")
async def get_audit_logs(
    request: Request,
    entity_type: Optional[str] = Query(None),
    limit: int = Query(100, ge=1, le=500),
    offset: int = Query(0, ge=0),
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        if entity_type:
            rows = await conn.fetch(
                """
                SELECT audit_event_id, entity_type, entity_id, action, actor_id,
                       actor_type, correlation_id, details, created_at
                FROM oracle_audit_events
                WHERE tenant_id=$1 AND entity_type=$2
                ORDER BY created_at DESC
                LIMIT $3 OFFSET $4
                """,
                admin.role, entity_type, limit, offset,
            )
        else:
            rows = await conn.fetch(
                """
                SELECT audit_event_id, entity_type, entity_id, action, actor_id,
                       actor_type, correlation_id, details, created_at
                FROM oracle_audit_events
                WHERE tenant_id=$1
                ORDER BY created_at DESC
                LIMIT $2 OFFSET $3
                """,
                admin.role, limit, offset,
            )
    return {"logs": [dict(r) for r in rows]}


# ── Template Administration ───────────────────────────────────────────────────

@router.get("/templates", summary="Template catalog admin view")
async def get_templates_admin(
    request: Request,
    status_filter: Optional[str] = Query(None, alias="status"),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    admin=Depends(require_admin),
):
    pool = _pool(request)
    where = "WHERE tenant_id = $1"
    params: list[Any] = [admin.role]
    idx = 2

    if status_filter:
        where += f" AND status = ${idx}"; params.append(status_filter); idx += 1

    async with pool.acquire() as conn:
        rows = await conn.fetch(
            f"""
            SELECT t.template_id, t.name, t.category, t.status, t.origin,
                   t.version, t.use_count, t.chapter_id, t.subchapter_id,
                   ch.name as chapter_name, sub.name as subchapter_name,
                   t.created_at, t.updated_at
            FROM oracle_component_templates t
            LEFT JOIN oracle_template_chapters ch ON ch.chapter_id = t.chapter_id
            LEFT JOIN oracle_template_subchapters sub ON sub.subchapter_id = t.subchapter_id
            {where}
            ORDER BY t.updated_at DESC
            LIMIT ${idx} OFFSET ${idx+1}
            """,
            *params, limit, offset,
        )
        total = await conn.fetchval(
            f"SELECT COUNT(*) FROM oracle_component_templates {where}", *params,
        )
    return {"total": total, "limit": limit, "offset": offset, "templates": [dict(r) for r in rows]}


@router.post("/templates/{template_id}/publish", summary="Publish a template")
async def publish_template(
    template_id: str,
    request: Request,
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        result = await conn.execute(
            """
            UPDATE oracle_component_templates
            SET status='catalog_active', updated_at=NOW()
            WHERE template_id=$1 AND tenant_id=$2
            """,
            template_id, admin.role,
        )
    if result == "UPDATE 0":
        raise HTTPException(404, "Template not found")
    logger.info("Template %s published by admin %s", template_id, admin.user_id)
    return {"status": "published"}


@router.post("/templates/{template_id}/archive", summary="Archive a template")
async def archive_template(
    template_id: str,
    request: Request,
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        result = await conn.execute(
            """
            UPDATE oracle_component_templates
            SET status='archived', updated_at=NOW()
            WHERE template_id=$1 AND tenant_id=$2
            """,
            template_id, admin.role,
        )
    if result == "UPDATE 0":
        raise HTTPException(404, "Template not found")
    logger.info("Template %s archived by admin %s", template_id, admin.user_id)
    return {"status": "archived"}


# ── Template Chapter Admin ────────────────────────────────────────────────────

@router.get("/template-chapters", summary="List template chapters (admin view)")
async def list_chapters_admin(
    request: Request,
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        rows = await conn.fetch(
            """
            SELECT ch.chapter_id, ch.name, ch.description, ch.sort_order, ch.is_active,
                   COUNT(sub.subchapter_id) as subchapter_count
            FROM oracle_template_chapters ch
            LEFT JOIN oracle_template_subchapters sub ON sub.chapter_id = ch.chapter_id
            WHERE ch.tenant_id=$1
            GROUP BY ch.chapter_id
            ORDER BY ch.sort_order ASC
            """,
            admin.role,
        )
    return {"chapters": [dict(r) for r in rows]}


# ── Synthetic Jobs Admin ──────────────────────────────────────────────────────

@router.get("/synthetic-jobs", summary="List synthetic generation jobs")
async def list_synthetic_jobs(
    request: Request,
    status_filter: Optional[str] = Query(None, alias="status"),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        if status_filter:
            rows = await conn.fetch(
                """
                SELECT job_id, template_id, model, status, requested_count,
                       accepted_count, created_by, started_at, completed_at, created_at
                FROM oracle_synthetic_generation_jobs
                WHERE tenant_id=$1 AND status=$2
                ORDER BY created_at DESC LIMIT $3 OFFSET $4
                """,
                admin.role, status_filter, limit, offset,
            )
        else:
            rows = await conn.fetch(
                """
                SELECT job_id, template_id, model, status, requested_count,
                       accepted_count, created_by, started_at, completed_at, created_at
                FROM oracle_synthetic_generation_jobs
                WHERE tenant_id=$1
                ORDER BY created_at DESC LIMIT $2 OFFSET $3
                """,
                admin.role, limit, offset,
            )
    return {"jobs": [dict(r) for r in rows]}


@router.post("/synthetic-jobs/{job_id}/cancel", summary="Cancel a synthetic generation job")
async def cancel_synthetic_job(
    job_id: str,
    request: Request,
    admin=Depends(require_admin),
):
    pool = _pool(request)
    async with pool.acquire() as conn:
        result = await conn.execute(
            """
            UPDATE oracle_synthetic_generation_jobs
            SET status='cancelled', updated_at=NOW()
            WHERE job_id=$1 AND tenant_id=$2 AND status IN ('pending','running')
            """,
            job_id, admin.role,
        )
    if result == "UPDATE 0":
        raise HTTPException(404, "Job not found or already in terminal state")
    return {"status": "cancelled"}
