import uuid
import logging

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from pydantic import BaseModel, field_validator
from typing import List, Optional

from app.core.deps import get_db, get_current_staff
from app.models.mstr_department import MstrDepartment
from app.constants import error_codes
from app.constants.role_keys import ADMIN_ROLE_KEYS

logger = logging.getLogger(__name__)

router = APIRouter()


# ── Request Models ─────────────────────────────────────────────────────────────

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

    @field_validator("name")
    @classmethod
    def name_must_not_be_empty(cls, v):
        v = v.strip()
        if not v:
            raise ValueError("Department name must not be empty")
        return v

    @field_validator("code")
    @classmethod
    def code_must_not_be_empty(cls, v):
        v = v.strip()
        if not v:
            raise ValueError("Department code must not be empty")
        return v


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

    @field_validator("name")
    @classmethod
    def name_must_not_be_empty(cls, v):
        if v is not None:
            v = v.strip()
            if not v:
                raise ValueError("Department name must not be empty")
        return v

    @field_validator("code")
    @classmethod
    def code_must_not_be_empty(cls, v):
        if v is not None:
            v = v.strip()
            if not v:
                raise ValueError("Department code must not be empty")
        return v


# ── Response Models ────────────────────────────────────────────────────────────

class DepartmentItem(BaseModel):
    id: str
    name: str
    code: str
    description: Optional[str] = None
    createdAt: str


class DepartmentListResponse(BaseModel):
    departments: List[DepartmentItem]


def dept_to_dict(d: MstrDepartment) -> dict:
    return {
        "id": str(d.id),
        "name": d.name,
        "code": d.code,
        "description": d.description,
        "createdAt": d.created_at.isoformat(),
    }


# ── Routes ─────────────────────────────────────────────────────────────────────

@router.get("", response_model=DepartmentListResponse)
async def list_departments(
    db: AsyncSession = Depends(get_db),
    current_staff: dict = Depends(get_current_staff),
):
    result = await db.execute(
        select(MstrDepartment).where(MstrDepartment.is_active == True).order_by(MstrDepartment.name)
    )
    departments = result.scalars().all()
    return DepartmentListResponse(departments=[DepartmentItem(**dept_to_dict(d)) for d in departments])


@router.get("/{dept_id}")
async def get_department(
    dept_id: str,
    db: AsyncSession = Depends(get_db),
    current_staff: dict = Depends(get_current_staff),
):
    result = await db.execute(
        select(MstrDepartment).where(MstrDepartment.id == uuid.UUID(dept_id), MstrDepartment.is_active == True)
    )
    dept = result.scalar_one_or_none()
    if not dept:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail={"error": error_codes.DEPARTMENT_NOT_FOUND, "message": "Department not found"},
        )
    return dept_to_dict(dept)


@router.post("", status_code=status.HTTP_201_CREATED)
async def create_department(
    body: CreateDepartmentRequest,
    db: AsyncSession = Depends(get_db),
    current_staff: dict = Depends(get_current_staff),
):
    if current_staff.get("role") not in ADMIN_ROLE_KEYS:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail={"error": error_codes.FORBIDDEN, "message": "Only admins can create departments"},
        )

    existing = await db.execute(
        select(MstrDepartment).where(
            MstrDepartment.name == body.name,
            MstrDepartment.code == body.code,
        )
    )
    if existing.scalar_one_or_none():
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail={"error": error_codes.DEPARTMENT_ALREADY_EXISTS, "message": "Department with this name and code already exists"},
        )

    dept = MstrDepartment(
        id=uuid.uuid4(),
        name=body.name,
        code=body.code,
        description=body.description,
        llm_system_prompt="",
        llm_summary_prompt="",
        is_active=True,
    )
    db.add(dept)
    await db.commit()
    await db.refresh(dept)
    logger.info("Department created: %s", dept.name)
    return dept_to_dict(dept)


@router.put("/{dept_id}")
async def update_department(
    dept_id: str,
    body: UpdateDepartmentRequest,
    db: AsyncSession = Depends(get_db),
    current_staff: dict = Depends(get_current_staff),
):
    if current_staff.get("role") not in ADMIN_ROLE_KEYS:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail={"error": error_codes.FORBIDDEN, "message": "Only admins can update departments"},
        )

    result = await db.execute(select(MstrDepartment).where(MstrDepartment.id == uuid.UUID(dept_id)))
    dept = result.scalar_one_or_none()
    if not dept:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail={"error": error_codes.DEPARTMENT_NOT_FOUND, "message": "Department not found"},
        )

    new_name = body.name if body.name is not None else dept.name
    new_code = body.code if body.code is not None else dept.code

    if (body.name is not None or body.code is not None) and (new_name != dept.name or new_code != dept.code):
        conflict = await db.execute(
            select(MstrDepartment).where(
                MstrDepartment.name == new_name,
                MstrDepartment.code == new_code,
                MstrDepartment.id != uuid.UUID(dept_id),
            )
        )
        if conflict.scalar_one_or_none():
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail={"error": error_codes.DEPARTMENT_ALREADY_EXISTS, "message": "Department with this name and code already exists"},
            )

    dept.name = new_name
    dept.code = new_code
    if body.description is not None:
        dept.description = body.description

    await db.commit()
    await db.refresh(dept)
    logger.info("Department updated: %s", dept.name)
    return dept_to_dict(dept)


@router.delete("/{dept_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_department(
    dept_id: str,
    db: AsyncSession = Depends(get_db),
    current_staff: dict = Depends(get_current_staff),
):
    if current_staff.get("role") not in ADMIN_ROLE_KEYS:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail={"error": error_codes.FORBIDDEN, "message": "Only admins can delete departments"},
        )

    result = await db.execute(select(MstrDepartment).where(MstrDepartment.id == uuid.UUID(dept_id)))
    dept = result.scalar_one_or_none()
    if not dept:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail={"error": error_codes.DEPARTMENT_NOT_FOUND, "message": "Department not found"},
        )

    dept.is_active = False
    await db.commit()
    logger.info("Department deactivated: %s", dept_id)
