"""
FastAPI routes for Compliance Projects (Multi-Projekt-Architektur).

Endpoints:
- GET    /v1/projects              → List all projects for a tenant
- POST   /v1/projects              → Create a new project
- GET    /v1/projects/{project_id} → Get a single project
- PATCH  /v1/projects/{project_id} → Update project (name, description)
- DELETE /v1/projects/{project_id} → Archive project (soft delete)
"""

import json
import logging
from typing import Optional
from uuid import UUID

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

from database import SessionLocal
from .tenant_utils import get_tenant_id

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


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

class CreateProjectRequest(BaseModel):
    name: str
    description: str = ""
    customer_type: str = "new"  # 'new' | 'existing'
    copy_from_project_id: Optional[str] = None  # Copy company profile from existing project


class UpdateProjectRequest(BaseModel):
    name: Optional[str] = None
    description: Optional[str] = None


class ProjectResponse(BaseModel):
    id: str
    tenant_id: str
    name: str
    description: str
    customer_type: str
    status: str
    project_version: int
    completion_percentage: int
    created_at: str
    updated_at: str


# =============================================================================
# HELPERS
# =============================================================================

def _row_to_response(row) -> dict:
    """Convert a DB row to ProjectResponse dict."""
    return {
        "id": str(row.id),
        "tenant_id": row.tenant_id,
        "name": row.name,
        "description": row.description or "",
        "customer_type": row.customer_type or "new",
        "status": row.status or "active",
        "project_version": row.project_version or 1,
        "completion_percentage": row.completion_percentage or 0,
        "created_at": row.created_at.isoformat() if row.created_at else "",
        "updated_at": row.updated_at.isoformat() if row.updated_at else "",
    }


# =============================================================================
# ENDPOINTS
# =============================================================================

@router.get("")
async def list_projects(
    tenant_id: str = Depends(get_tenant_id),
    include_archived: bool = False,
):
    """List all projects for the tenant."""
    db = SessionLocal()
    try:
        if include_archived:
            query = text("""
                SELECT id, tenant_id, name, description, customer_type, status,
                       project_version, completion_percentage, created_at, updated_at
                FROM compliance_projects
                WHERE tenant_id = :tenant_id AND status != 'deleted'
                ORDER BY created_at DESC
            """)
        else:
            query = text("""
                SELECT id, tenant_id, name, description, customer_type, status,
                       project_version, completion_percentage, created_at, updated_at
                FROM compliance_projects
                WHERE tenant_id = :tenant_id AND status = 'active'
                ORDER BY created_at DESC
            """)
        result = db.execute(query, {"tenant_id": tenant_id})
        rows = result.fetchall()
        return {
            "projects": [_row_to_response(row) for row in rows],
            "total": len(rows),
        }
    finally:
        db.close()


@router.post("", status_code=201)
async def create_project(
    body: CreateProjectRequest,
    tenant_id: str = Depends(get_tenant_id),
):
    """Create a new compliance project.

    Optionally copies the company profile (companyProfile) from an existing
    project's sdk_states into the new project's state. This allows a tenant
    to start a new project for a subsidiary with the same base data.
    """
    db = SessionLocal()
    try:
        # Create the project row
        result = db.execute(
            text("""
                INSERT INTO compliance_projects
                    (tenant_id, name, description, customer_type, status)
                VALUES
                    (:tenant_id, :name, :description, :customer_type, 'active')
                RETURNING id, tenant_id, name, description, customer_type, status,
                          project_version, completion_percentage, created_at, updated_at
            """),
            {
                "tenant_id": tenant_id,
                "name": body.name,
                "description": body.description,
                "customer_type": body.customer_type,
            },
        )
        project_row = result.fetchone()
        project_id = str(project_row.id)

        # Build initial SDK state
        initial_state = {
            "version": "1.0.0",
            "projectVersion": 1,
            "tenantId": tenant_id,
            "projectId": project_id,
            "customerType": body.customer_type,
            "companyProfile": None,
        }

        # If copy_from_project_id is provided, copy company profile
        if body.copy_from_project_id:
            source = db.execute(
                text("""
                    SELECT state FROM sdk_states
                    WHERE tenant_id = :tenant_id AND project_id = :project_id
                """),
                {
                    "tenant_id": tenant_id,
                    "project_id": body.copy_from_project_id,
                },
            ).fetchone()

            if source and source.state:
                source_state = source.state if isinstance(source.state, dict) else json.loads(source.state)
                if "companyProfile" in source_state:
                    initial_state["companyProfile"] = source_state["companyProfile"]
                if "customerType" in source_state:
                    initial_state["customerType"] = source_state["customerType"]

        # Create the sdk_states row for this project
        db.execute(
            text("""
                INSERT INTO sdk_states (tenant_id, project_id, state, version, created_at, updated_at)
                VALUES (:tenant_id, :project_id, CAST(:state AS jsonb), 1, NOW(), NOW())
            """),
            {
                "tenant_id": tenant_id,
                "project_id": project_id,
                "state": json.dumps(initial_state),
            },
        )

        db.commit()
        logger.info("Created project %s for tenant %s", project_id, tenant_id)
        return _row_to_response(project_row)
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()


@router.get("/{project_id}")
async def get_project(
    project_id: str,
    tenant_id: str = Depends(get_tenant_id),
):
    """Get a single project by ID (tenant-scoped)."""
    db = SessionLocal()
    try:
        result = db.execute(
            text("""
                SELECT id, tenant_id, name, description, customer_type, status,
                       project_version, completion_percentage, created_at, updated_at
                FROM compliance_projects
                WHERE id = :project_id AND tenant_id = :tenant_id AND status != 'deleted'
            """),
            {"project_id": project_id, "tenant_id": tenant_id},
        )
        row = result.fetchone()
        if not row:
            raise HTTPException(status_code=404, detail="Project not found")
        return _row_to_response(row)
    finally:
        db.close()


@router.patch("/{project_id}")
async def update_project(
    project_id: str,
    body: UpdateProjectRequest,
    tenant_id: str = Depends(get_tenant_id),
):
    """Update project name/description."""
    db = SessionLocal()
    try:
        # Build SET clause dynamically
        updates = {}
        set_parts = ["updated_at = NOW()"]
        if body.name is not None:
            set_parts.append("name = :name")
            updates["name"] = body.name
        if body.description is not None:
            set_parts.append("description = :description")
            updates["description"] = body.description

        updates["project_id"] = project_id
        updates["tenant_id"] = tenant_id

        result = db.execute(
            text(f"""
                UPDATE compliance_projects
                SET {', '.join(set_parts)}
                WHERE id = :project_id AND tenant_id = :tenant_id AND status != 'deleted'
                RETURNING id, tenant_id, name, description, customer_type, status,
                          project_version, completion_percentage, created_at, updated_at
            """),
            updates,
        )
        row = result.fetchone()
        if not row:
            raise HTTPException(status_code=404, detail="Project not found")
        db.commit()
        return _row_to_response(row)
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()


@router.delete("/{project_id}")
async def archive_project(
    project_id: str,
    tenant_id: str = Depends(get_tenant_id),
):
    """Soft-delete (archive) a project."""
    db = SessionLocal()
    try:
        result = db.execute(
            text("""
                UPDATE compliance_projects
                SET status = 'archived', archived_at = NOW(), updated_at = NOW()
                WHERE id = :project_id AND tenant_id = :tenant_id AND status = 'active'
                RETURNING id
            """),
            {"project_id": project_id, "tenant_id": tenant_id},
        )
        row = result.fetchone()
        if not row:
            raise HTTPException(status_code=404, detail="Project not found or already archived")
        db.commit()
        return {"success": True, "id": str(row.id), "status": "archived"}
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()


@router.post("/{project_id}/restore")
async def restore_project(
    project_id: str,
    tenant_id: str = Depends(get_tenant_id),
):
    """Restore an archived project back to active."""
    db = SessionLocal()
    try:
        result = db.execute(
            text("""
                UPDATE compliance_projects
                SET status = 'active', archived_at = NULL, updated_at = NOW()
                WHERE id = :project_id AND tenant_id = :tenant_id AND status = 'archived'
                RETURNING id, tenant_id, name, description, customer_type, status,
                          project_version, completion_percentage, created_at, updated_at
            """),
            {"project_id": project_id, "tenant_id": tenant_id},
        )
        row = result.fetchone()
        if not row:
            raise HTTPException(status_code=404, detail="Project not found or not archived")
        db.commit()
        logger.info("Restored project %s for tenant %s", project_id, tenant_id)
        return _row_to_response(row)
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()


@router.delete("/{project_id}/permanent")
async def permanently_delete_project(
    project_id: str,
    tenant_id: str = Depends(get_tenant_id),
):
    """Permanently delete a project and all associated data."""
    db = SessionLocal()
    try:
        # Verify project exists and belongs to tenant
        check = db.execute(
            text("""
                SELECT id FROM compliance_projects
                WHERE id = :project_id AND tenant_id = :tenant_id
            """),
            {"project_id": project_id, "tenant_id": tenant_id},
        ).fetchone()
        if not check:
            raise HTTPException(status_code=404, detail="Project not found")

        # Delete sdk_states (CASCADE should handle this, but be explicit)
        db.execute(
            text("DELETE FROM sdk_states WHERE project_id = :project_id AND tenant_id = :tenant_id"),
            {"project_id": project_id, "tenant_id": tenant_id},
        )

        # Delete the project itself
        db.execute(
            text("DELETE FROM compliance_projects WHERE id = :project_id AND tenant_id = :tenant_id"),
            {"project_id": project_id, "tenant_id": tenant_id},
        )

        db.commit()
        logger.info("Permanently deleted project %s for tenant %s", project_id, tenant_id)
        return {"success": True, "id": project_id, "status": "deleted"}
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()
