"""OntoRefactor four-level ontology and data-governance REST API."""
from __future__ import annotations

from typing import Any, Dict, List, Optional

from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, Field

from agentpaas.api.deps import get_tenant
from agentpaas.api.errors import api_error
from agentpaas.api.middleware.auth import TenantContext
from agentpaas.db.session import get_db
from agentpaas.governance import service
from agentpaas.governance.agents import execute_governance_agent, list_agent_runs


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


def _call(fn, *args, **kwargs):
    try:
        return fn(*args, **kwargs)
    except service.GovernanceError as exc:
        api_error(exc.status, exc.code, str(exc))


class ProjectCreate(BaseModel):
    name: str = Field(..., min_length=1, max_length=256)
    description: str = Field(default="", max_length=4096)
    namespace: str = Field(..., min_length=3, max_length=1024)
    version: str = Field(default="1.0.0", max_length=64)
    status: str = Field(default="active", max_length=32)


class ElementUpsert(BaseModel):
    uri: str = Field(default="", max_length=2048)
    layer: str
    profile: str
    kind: str = Field(..., min_length=1, max_length=128)
    name: str = Field(..., min_length=1, max_length=256)
    description: str = Field(default="", max_length=8192)
    status: str = "candidate"
    version: str = "1.0.0"
    attributes: Dict[str, Any] = Field(default_factory=dict)
    source: str = Field(default="", max_length=2048)


class EvidenceCreate(BaseModel):
    kind: str = Field(default="SourceArtifact", max_length=128)
    uri: str = Field(default="", max_length=2048)
    title: str = Field(..., min_length=1, max_length=512)
    content: str = Field(default="", max_length=524288)
    content_hash: str = Field(default="", max_length=128)
    source: str = Field(default="", max_length=2048)
    observed_at: Optional[str] = None
    valid_from: Optional[str] = None
    valid_to: Optional[str] = None
    metadata: Dict[str, Any] = Field(default_factory=dict)


class AssertionCreate(BaseModel):
    subject_id: str
    predicate: str = Field(..., min_length=1, max_length=128)
    object_id: Optional[str] = None
    object_value: str = Field(default="", max_length=8192)
    assertion_type: str = "inferred"
    confidence: float = Field(default=0.0, ge=0.0, le=1.0)
    evidence_ids: List[str] = Field(default_factory=list)
    generated_by: str = Field(default="", max_length=1024)
    valid_from: Optional[str] = None
    valid_to: Optional[str] = None
    review_status: str = "pending"
    reviewer: str = Field(default="", max_length=256)
    review_note: str = Field(default="", max_length=4096)


class AssertionReview(BaseModel):
    status: str
    reviewer: str = Field(..., min_length=1, max_length=256)
    note: str = Field(default="", max_length=4096)


class RuleUpsert(BaseModel):
    name: str = Field(..., min_length=1, max_length=256)
    description: str = Field(default="", max_length=4096)
    rule_type: str = Field(default="integrity", max_length=128)
    target_layer: str = ""
    target_profile: str = ""
    target_kind: str = ""
    expression: Dict[str, Any] = Field(default_factory=dict)
    severity: str = "medium"
    enabled: bool = True
    version: str = "1.0.0"


class IssueUpdate(BaseModel):
    status: str
    assigned_to: str = Field(default="", max_length=256)


class AgentAnalyzeRequest(BaseModel):
    source_type: str = Field(..., description="ddl | openapi | inventory")
    content: str = Field(..., min_length=1, max_length=524288)
    source_name: str = Field(default="", max_length=1024)
    environment: str = Field(default="development", max_length=128)
    persist: bool = True


@router.get("/projects")
async def projects(tenant: TenantContext = Depends(get_tenant)):
    return {"projects": service.list_projects(get_db(), tenant.tenant_id)}


@router.post("/projects", status_code=201)
async def create_project(body: ProjectCreate, tenant: TenantContext = Depends(get_tenant)):
    return _call(service.create_project, get_db(), tenant.tenant_id, body.model_dump())


@router.post("/bootstrap/tenant-isolation")
async def bootstrap_tenant_isolation(tenant: TenantContext = Depends(get_tenant)):
    """Install the idempotent M3->M0 tenant-isolation reference slice."""
    return _call(service.seed_tenant_isolation, get_db(), tenant.tenant_id)


@router.get("/projects/{project_id}/overview")
async def project_overview(project_id: str, tenant: TenantContext = Depends(get_tenant)):
    return _call(service.overview, get_db(), tenant.tenant_id, project_id)


@router.get("/projects/{project_id}/elements")
async def elements(
    project_id: str,
    layer: str = "",
    profile: str = "",
    status: str = "",
    q: str = "",
    limit: int = Query(default=200, ge=1, le=500),
    offset: int = Query(default=0, ge=0),
    tenant: TenantContext = Depends(get_tenant),
):
    return _call(
        service.list_elements, get_db(), tenant.tenant_id, project_id,
        layer=layer, profile=profile, status=status, query=q, limit=limit, offset=offset,
    )


@router.post("/projects/{project_id}/elements", status_code=201)
async def upsert_element(
    project_id: str, body: ElementUpsert, tenant: TenantContext = Depends(get_tenant),
):
    return _call(service.upsert_element, get_db(), tenant.tenant_id, project_id, body.model_dump())


@router.get("/elements/{element_id}/impact")
async def element_impact(
    element_id: str, max_depth: int = Query(default=4, ge=1, le=8),
    tenant: TenantContext = Depends(get_tenant),
):
    return _call(service.impact, get_db(), tenant.tenant_id, element_id, max_depth)


@router.get("/projects/{project_id}/graph")
async def project_graph(
    project_id: str, include_pending: bool = True,
    limit: int = Query(default=500, ge=1, le=500),
    tenant: TenantContext = Depends(get_tenant),
):
    return _call(
        service.graph, get_db(), tenant.tenant_id, project_id,
        include_pending=include_pending, limit=limit,
    )


@router.get("/projects/{project_id}/evidence")
async def evidence(
    project_id: str, limit: int = Query(default=200, ge=1, le=500),
    tenant: TenantContext = Depends(get_tenant),
):
    return {"evidence": _call(service.list_evidence, get_db(), tenant.tenant_id, project_id, limit)}


@router.post("/projects/{project_id}/evidence", status_code=201)
async def add_evidence(
    project_id: str, body: EvidenceCreate, tenant: TenantContext = Depends(get_tenant),
):
    return _call(service.create_evidence, get_db(), tenant.tenant_id, project_id, body.model_dump())


@router.get("/projects/{project_id}/assertions")
async def assertions(
    project_id: str, review_status: str = "", assertion_type: str = "",
    limit: int = Query(default=300, ge=1, le=500),
    tenant: TenantContext = Depends(get_tenant),
):
    return {"assertions": _call(
        service.list_assertions, get_db(), tenant.tenant_id, project_id,
        review_status=review_status, assertion_type=assertion_type, limit=limit,
    )}


@router.post("/projects/{project_id}/assertions", status_code=201)
async def add_assertion(
    project_id: str, body: AssertionCreate, tenant: TenantContext = Depends(get_tenant),
):
    return _call(service.create_assertion, get_db(), tenant.tenant_id, project_id, body.model_dump())


@router.post("/assertions/{assertion_id}/review")
async def review_assertion(
    assertion_id: str, body: AssertionReview, tenant: TenantContext = Depends(get_tenant),
):
    return _call(
        service.review_assertion, get_db(), tenant.tenant_id, assertion_id,
        body.status, body.reviewer, body.note,
    )


@router.get("/projects/{project_id}/rules")
async def rules(project_id: str, tenant: TenantContext = Depends(get_tenant)):
    return {"rules": _call(service.list_rules, get_db(), tenant.tenant_id, project_id)}


@router.post("/projects/{project_id}/rules", status_code=201)
async def add_rule(
    project_id: str, body: RuleUpsert, tenant: TenantContext = Depends(get_tenant),
):
    return _call(service.upsert_rule, get_db(), tenant.tenant_id, project_id, body.model_dump())


@router.post("/projects/{project_id}/validate")
async def validate(project_id: str, tenant: TenantContext = Depends(get_tenant)):
    return _call(service.validate_project, get_db(), tenant.tenant_id, project_id, persist=True)


@router.get("/projects/{project_id}/issues")
async def issues(
    project_id: str, status: str = "open", tenant: TenantContext = Depends(get_tenant),
):
    return {"issues": _call(service.list_issues, get_db(), tenant.tenant_id, project_id, status)}


@router.put("/issues/{issue_id}")
async def change_issue(
    issue_id: str, body: IssueUpdate, tenant: TenantContext = Depends(get_tenant),
):
    return _call(
        service.update_issue, get_db(), tenant.tenant_id, issue_id,
        body.status, body.assigned_to,
    )


@router.post("/projects/{project_id}/agent-runs/analyze")
async def analyze_with_agents(
    project_id: str, body: AgentAnalyzeRequest, tenant: TenantContext = Depends(get_tenant),
):
    _call(service.require_project, get_db(), tenant.tenant_id, project_id)
    try:
        return execute_governance_agent(
            get_db(), tenant.tenant_id, project_id, body.model_dump(), persist=body.persist,
        )
    except service.GovernanceError as exc:
        api_error(exc.status, exc.code, str(exc))


@router.get("/projects/{project_id}/agent-runs")
async def agent_runs(
    project_id: str, limit: int = Query(default=50, ge=1, le=200),
    tenant: TenantContext = Depends(get_tenant),
):
    _call(service.require_project, get_db(), tenant.tenant_id, project_id)
    return {"runs": list_agent_runs(get_db(), tenant.tenant_id, project_id, limit)}

