"""
FastAPI routes for the Control Dependency Engine.

Endpoints:
  GET    /v1/dependencies                     — List dependencies
  POST   /v1/dependencies                     — Create a dependency
  DELETE /v1/dependencies/{dep_id}            — Deactivate a dependency
  POST   /v1/dependencies/generate            — Auto-generate dependencies
  POST   /v1/dependencies/evaluate            — Evaluate controls with dependencies
  GET    /v1/dependencies/evaluate/{run_id}   — Get evaluation results
  POST   /v1/dependencies/validate            — Validate graph (cycle check)
  GET    /v1/dependencies/graph               — Dependency graph for visualization
"""

import json
import logging
import uuid
from typing import Optional

from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import text

from db.session import SessionLocal
from services.dependency_engine import (
    Dependency,
    ControlState,
    EvaluationResult,
    evaluate_controls,
    detect_cycles,
    load_all_active_dependencies,
    load_dependencies_for_controls,
    store_dependency,
    store_evaluation_results,
)

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/v1/dependencies", tags=["dependencies"])


# =============================================================================
# REQUEST / RESPONSE MODELS
# =============================================================================

class DependencyCreateRequest(BaseModel):
    source_control_id: str
    target_control_id: str
    dependency_type: str
    condition: dict = {}
    effect: dict = {}
    priority: int = 100


class EvaluateRequest(BaseModel):
    control_ids: Optional[list] = None
    company_profile: dict = {}
    control_statuses: dict = {}


class GenerateRequest(BaseModel):
    enable_ontology: bool = True
    enable_patterns: bool = True
    enable_domain_packs: bool = True
    dry_run: bool = True
    limit: int = 0


# =============================================================================
# LIST DEPENDENCIES
# =============================================================================

@router.get("/")
async def list_dependencies(
    dependency_type: Optional[str] = Query(default=None),
    source_id: Optional[str] = Query(default=None),
    target_id: Optional[str] = Query(default=None),
    active_only: bool = Query(default=True),
    limit: int = Query(default=100, le=1000),
    offset: int = Query(default=0),
):
    """List dependencies with optional filters."""
    db = SessionLocal()
    try:
        conditions = []
        params: dict = {"lim": limit, "off": offset}

        if active_only:
            conditions.append("is_active = TRUE")
        if dependency_type:
            conditions.append("dependency_type = :dtype")
            params["dtype"] = dependency_type
        if source_id:
            conditions.append("source_control_id = CAST(:src AS uuid)")
            params["src"] = source_id
        if target_id:
            conditions.append("target_control_id = CAST(:tgt AS uuid)")
            params["tgt"] = target_id

        where = "WHERE " + " AND ".join(conditions) if conditions else ""

        rows = db.execute(text(f"""
            SELECT d.id, d.source_control_id, d.target_control_id,
                   d.dependency_type, d.condition, d.effect, d.priority,
                   d.generation_method, d.is_active, d.created_at,
                   s.control_id AS source_cid, s.title AS source_title,
                   t.control_id AS target_cid, t.title AS target_title
            FROM control_dependencies d
            LEFT JOIN canonical_controls s ON s.id = d.source_control_id
            LEFT JOIN canonical_controls t ON t.id = d.target_control_id
            {where}
            ORDER BY d.priority, d.created_at
            LIMIT :lim OFFSET :off
        """), params).fetchall()

        total = db.execute(text(f"""
            SELECT COUNT(*) FROM control_dependencies d {where}
        """), params).scalar()

        return {
            "total": total,
            "dependencies": [
                {
                    "id": str(r[0]),
                    "source_control_id": str(r[1]),
                    "target_control_id": str(r[2]),
                    "dependency_type": r[3],
                    "condition": r[4],
                    "effect": r[5],
                    "priority": r[6],
                    "generation_method": r[7],
                    "is_active": r[8],
                    "created_at": str(r[9]) if r[9] else None,
                    "source_control_id_short": r[10] or "",
                    "source_title": r[11] or "",
                    "target_control_id_short": r[12] or "",
                    "target_title": r[13] or "",
                }
                for r in rows
            ],
        }
    finally:
        db.close()


# =============================================================================
# CREATE DEPENDENCY
# =============================================================================

@router.post("/")
async def create_dependency(req: DependencyCreateRequest):
    """Create a manual dependency."""
    valid_types = {"prerequisite", "conditional_requirement", "supersedes",
                   "compensating_control", "scope_exclusion"}
    if req.dependency_type not in valid_types:
        raise HTTPException(400, f"Invalid type. Must be one of: {valid_types}")

    if req.source_control_id == req.target_control_id:
        raise HTTPException(400, "Source and target must be different")

    db = SessionLocal()
    try:
        dep = Dependency(
            source_control_id=req.source_control_id,
            target_control_id=req.target_control_id,
            dependency_type=req.dependency_type,
            condition=req.condition,
            effect=req.effect,
            priority=req.priority,
            generation_method="manual",
        )
        dep_id = store_dependency(db, dep)
        db.commit()
        return {"id": dep_id, "status": "created"}
    except Exception as e:
        db.rollback()
        raise HTTPException(500, str(e))
    finally:
        db.close()


# =============================================================================
# DELETE (DEACTIVATE) DEPENDENCY
# =============================================================================

@router.delete("/{dep_id}")
async def deactivate_dependency(dep_id: str):
    """Deactivate a dependency (soft delete)."""
    db = SessionLocal()
    try:
        result = db.execute(
            text("UPDATE control_dependencies SET is_active = FALSE, updated_at = NOW() WHERE id = CAST(:did AS uuid)"),
            {"did": dep_id},
        )
        db.commit()
        if result.rowcount == 0:
            raise HTTPException(404, "Dependency not found")
        return {"status": "deactivated"}
    finally:
        db.close()


# =============================================================================
# AUTO-GENERATE DEPENDENCIES
# =============================================================================

@router.post("/generate")
async def generate_dependencies(req: GenerateRequest):
    """Auto-generate dependencies from ontology, patterns, and domain packs."""
    from services.dependency_generator import generate_all_dependencies

    db = SessionLocal()
    try:
        query = """
            SELECT id::text, control_id, title, generation_metadata
            FROM canonical_controls
            WHERE release_state = 'draft'
              AND generation_metadata->>'decomposition_method' = 'pass0b'
        """
        if req.limit > 0:
            query += f" LIMIT {req.limit}"

        rows = db.execute(text(query)).fetchall()
        controls = []
        for r in rows:
            meta = r[3] if isinstance(r[3], dict) else {}
            controls.append({
                "id": r[0],
                "control_id": r[1],
                "title": r[2],
                "generation_metadata": meta,
            })

        deps, stats = generate_all_dependencies(
            controls,
            enable_ontology=req.enable_ontology,
            enable_patterns=req.enable_patterns,
            enable_domain_packs=req.enable_domain_packs,
        )

        if not req.dry_run:
            stored = 0
            for dep in deps:
                store_dependency(db, dep)
                stored += 1
            db.commit()
            stats["stored"] = stored

        return {
            "dry_run": req.dry_run,
            "controls_analyzed": len(controls),
            "stats": stats,
            "sample_dependencies": [
                {
                    "source": d.source_control_id[:8],
                    "target": d.target_control_id[:8],
                    "type": d.dependency_type,
                    "method": d.generation_method,
                    "priority": d.priority,
                }
                for d in deps[:20]
            ],
        }
    finally:
        db.close()


# =============================================================================
# EVALUATE CONTROLS
# =============================================================================

@router.post("/evaluate")
async def evaluate(req: EvaluateRequest):
    """Evaluate controls with dependency resolution."""
    db = SessionLocal()
    try:
        # Load control statuses
        if req.control_statuses:
            control_ids = list(req.control_statuses.keys())
            states = {
                cid: ControlState(control_id=cid, raw_status=status)
                for cid, status in req.control_statuses.items()
            }
        elif req.control_ids:
            control_ids = req.control_ids
            states = {
                cid: ControlState(control_id=cid, raw_status="fail")
                for cid in control_ids
            }
        else:
            raise HTTPException(400, "Provide control_ids or control_statuses")

        # Load dependencies
        deps = load_dependencies_for_controls(db, control_ids)

        # Evaluate
        results = evaluate_controls(states, deps, req.company_profile)

        # Store results
        store_evaluation_results(db, results, req.company_profile)
        db.commit()

        # Format response
        run_id = next(iter(results.values())).evaluation_run_id if results else ""

        return {
            "evaluation_run_id": run_id,
            "total_controls": len(results),
            "dependencies_evaluated": len(deps),
            "results": [
                {
                    "control_id": r.control_id,
                    "raw_status": r.raw_status,
                    "resolved_status": r.resolved_status,
                    "dependency_resolution": r.dependency_resolution,
                    "confidence": r.confidence,
                }
                for r in results.values()
            ],
            "summary": {
                "pass": sum(1 for r in results.values() if r.resolved_status == "pass"),
                "fail": sum(1 for r in results.values() if r.resolved_status == "fail"),
                "not_applicable": sum(1 for r in results.values() if r.resolved_status == "not_applicable"),
                "compensated_fail": sum(1 for r in results.values() if r.resolved_status == "compensated_fail"),
                "review_required": sum(1 for r in results.values() if r.resolved_status == "review_required"),
            },
        }
    finally:
        db.close()


# =============================================================================
# GET EVALUATION RESULTS
# =============================================================================

@router.get("/evaluate/{run_id}")
async def get_evaluation_results(run_id: str):
    """Get stored evaluation results for a run."""
    db = SessionLocal()
    try:
        rows = db.execute(text("""
            SELECT er.control_id::text, cc.control_id, cc.title,
                   er.raw_status, er.resolved_status,
                   er.dependency_resolution, er.confidence, er.reasoning
            FROM control_evaluation_results er
            JOIN canonical_controls cc ON cc.id = er.control_id
            WHERE er.evaluation_run_id = CAST(:rid AS uuid)
            ORDER BY er.resolved_status, cc.control_id
        """), {"rid": run_id}).fetchall()

        if not rows:
            raise HTTPException(404, "Evaluation run not found")

        return {
            "evaluation_run_id": run_id,
            "total": len(rows),
            "results": [
                {
                    "control_uuid": r[0],
                    "control_id": r[1],
                    "title": r[2],
                    "raw_status": r[3],
                    "resolved_status": r[4],
                    "dependency_resolution": r[5],
                    "confidence": r[6],
                    "reasoning": r[7],
                }
                for r in rows
            ],
        }
    finally:
        db.close()


# =============================================================================
# VALIDATE GRAPH (CYCLE CHECK)
# =============================================================================

@router.post("/validate")
async def validate_graph():
    """Validate the dependency graph for cycles."""
    db = SessionLocal()
    try:
        deps = load_all_active_dependencies(db)
        cycles = detect_cycles(deps)

        return {
            "total_dependencies": len(deps),
            "cycles_found": len(cycles),
            "cycles": cycles[:20],
            "is_valid": len(cycles) == 0,
        }
    finally:
        db.close()


# =============================================================================
# DEPENDENCY GRAPH (FOR VISUALIZATION)
# =============================================================================

@router.get("/graph")
async def get_graph(limit: int = Query(default=200, le=1000)):
    """Get dependency graph as nodes + edges."""
    db = SessionLocal()
    try:
        deps = load_all_active_dependencies(db)[:limit]

        node_ids = set()
        for d in deps:
            node_ids.add(d.source_control_id)
            node_ids.add(d.target_control_id)

        nodes = []
        if node_ids:
            id_list = list(node_ids)
            rows = db.execute(text("""
                SELECT id::text, control_id, title, release_state, category
                FROM canonical_controls
                WHERE id = ANY(CAST(:ids AS uuid[]))
            """), {"ids": id_list}).fetchall()

            for r in rows:
                nodes.append({
                    "id": r[0],
                    "control_id": r[1],
                    "title": r[2],
                    "release_state": r[3],
                    "category": r[4],
                })

        edges = [
            {
                "source": d.source_control_id,
                "target": d.target_control_id,
                "type": d.dependency_type,
                "priority": d.priority,
                "generation_method": d.generation_method,
            }
            for d in deps
        ]

        return {
            "nodes": nodes,
            "edges": edges,
            "total_nodes": len(nodes),
            "total_edges": len(edges),
        }
    finally:
        db.close()
