"""Norms API Routes"""
import uuid
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from normbase.db.connection import get_db
from normbase.models.norm import Norm, NormVersion
from normbase.models.schemas import (
    NormCreate,
    NormUpdate,
    NormResponse,
    NormListResponse,
    NormVersionResponse,
)
from normbase.services.cache_service import CacheService
from normbase.config import settings

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


async def _invalidate_retrieve_cache() -> None:
    cache = CacheService()
    try:
        await cache.delete_by_prefix(settings.retrieve_cache_prefix)
    except Exception as e:
        print(f"Retrieve cache invalidation error: {e}")


@router.post("", response_model=NormResponse, status_code=status.HTTP_201_CREATED)
async def create_norm(
    norm_data: NormCreate,
    db: AsyncSession = Depends(get_db)
):
    """Create a new norm"""
    norm = Norm(
        name=norm_data.name,
        norm_type=norm_data.norm_type,
        authority_level=norm_data.authority_level,
        domain=norm_data.domain,
        field_id=norm_data.field_id,  # Added field_id
        scope_roles=norm_data.scope_roles,
        scope_contexts=norm_data.scope_contexts,
        condition=norm_data.condition,
        action=norm_data.action,
        consequence=norm_data.consequence,
        formatted_prompt=norm_data.formatted_prompt,
        source=norm_data.source,
        source_details=norm_data.source_details,
        status=norm_data.status,
        case_refs=norm_data.case_refs,
        created_by=norm_data.created_by,
    )
    
    db.add(norm)
    await db.commit()
    await db.refresh(norm)
    await _invalidate_retrieve_cache()
    
    return norm


@router.get("", response_model=NormListResponse)
async def list_norms(
    page: int = Query(1, ge=1, description="Page number"),
    page_size: int = Query(20, ge=1, le=100, description="Page size"),
    norm_type: Optional[str] = Query(None, description="Filter by norm type"),
    status: Optional[str] = Query(None, description="Filter by status"),
    domain: Optional[str] = Query(None, description="Filter by domain"),
    db: AsyncSession = Depends(get_db)
):
    """List all norms with pagination"""
    # Build query
    query = select(Norm)
    count_query = select(func.count(Norm.id))
    
    # Apply filters
    if norm_type:
        query = query.where(Norm.norm_type == norm_type)
        count_query = count_query.where(Norm.norm_type == norm_type)
    
    if status:
        query = query.where(Norm.status == status)
        count_query = count_query.where(Norm.status == status)
    
    if domain:
        query = query.where(Norm.domain == domain)
        count_query = count_query.where(Norm.domain == domain)
    
    # Get total count
    total_result = await db.execute(count_query)
    total = total_result.scalar() or 0
    
    # Get paginated results
    query = query.offset((page - 1) * page_size).limit(page_size)
    result = await db.execute(query)
    norms = result.scalars().all()
    
    return NormListResponse(
        total=total,
        items=[NormResponse.model_validate(n) for n in norms],
        page=page,
        page_size=page_size
    )


@router.get("/{norm_id}", response_model=NormResponse)
async def get_norm(
    norm_id: uuid.UUID,
    db: AsyncSession = Depends(get_db)
):
    """Get a norm by ID"""
    result = await db.execute(select(Norm).where(Norm.id == norm_id))
    norm = result.scalar_one_or_none()
    
    if not norm:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Norm with id {norm_id} not found"
        )
    
    return NormResponse.model_validate(norm)


@router.put("/{norm_id}", response_model=NormResponse)
async def update_norm(
    norm_id: uuid.UUID,
    norm_data: NormUpdate,
    db: AsyncSession = Depends(get_db)
):
    """Update a norm"""
    result = await db.execute(select(Norm).where(Norm.id == norm_id))
    norm = result.scalar_one_or_none()
    
    if not norm:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Norm with id {norm_id} not found"
        )
    
    # Create version snapshot before update
    version_snapshot = NormVersion(
        norm_id=norm.id,
        version=norm.version,
        snapshot={
            "name": norm.name,
            "norm_type": norm.norm_type,
            "authority_level": norm.authority_level,
            "domain": norm.domain,
            "scope_roles": norm.scope_roles,
            "scope_contexts": norm.scope_contexts,
            "condition": norm.condition,
            "action": norm.action,
            "consequence": norm.consequence,
            "formatted_prompt": norm.formatted_prompt,
            "source": norm.source,
            "status": norm.status,
            "case_refs": norm.case_refs,
        }
    )
    db.add(version_snapshot)
    
    # Update fields
    update_data = norm_data.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(norm, field, value)
    
    # Increment version
    norm.version += 1
    
    await db.commit()
    await db.refresh(norm)
    await _invalidate_retrieve_cache()
    
    return NormResponse.model_validate(norm)


@router.delete("/{norm_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_norm(
    norm_id: uuid.UUID,
    db: AsyncSession = Depends(get_db)
):
    """Delete a norm"""
    result = await db.execute(select(Norm).where(Norm.id == norm_id))
    norm = result.scalar_one_or_none()
    
    if not norm:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Norm with id {norm_id} not found"
        )
    
    await db.delete(norm)
    await db.commit()
    await _invalidate_retrieve_cache()


@router.get("/{norm_id}/versions", response_model=List[NormVersionResponse])
async def list_norm_versions(
    norm_id: uuid.UUID,
    db: AsyncSession = Depends(get_db)
):
    """Get version history for a norm"""
    result = await db.execute(
        select(NormVersion)
        .where(NormVersion.norm_id == norm_id)
        .order_by(NormVersion.version.desc())
    )
    versions = result.scalars().all()
    
    return [NormVersionResponse.model_validate(v) for v in versions]


@router.post("/{norm_id}/versions", response_model=NormVersionResponse)
async def create_norm_version(
    norm_id: uuid.UUID,
    change_note: str,
    db: AsyncSession = Depends(get_db)
):
    """Create a new version snapshot for a norm"""
    result = await db.execute(select(Norm).where(Norm.id == norm_id))
    norm = result.scalar_one_or_none()
    
    if not norm:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Norm with id {norm_id} not found"
        )
    
    version = NormVersion(
        norm_id=norm.id,
        version=norm.version,
        snapshot={
            "name": norm.name,
            "norm_type": norm.norm_type,
            "authority_level": norm.authority_level,
            "domain": norm.domain,
            "scope_roles": norm.scope_roles,
            "scope_contexts": norm.scope_contexts,
            "condition": norm.condition,
            "action": norm.action,
            "consequence": norm.consequence,
            "formatted_prompt": norm.formatted_prompt,
            "source": norm.source,
            "status": norm.status,
            "case_refs": norm.case_refs,
        },
        change_note=change_note
    )
    
    db.add(version)
    await db.commit()
    await db.refresh(version)
    
    return NormVersionResponse.model_validate(version)
