"""
FastAPI routes for Audit Sessions & Sign-off functionality.

Sprint 3 Phase 3: Auditor-Verbesserungen

Endpoints:
- /audit/sessions: Manage audit sessions
- /audit/checklist: Audit checklist with sign-off
"""

import logging
from datetime import datetime
from typing import Optional, List
from uuid import uuid4
import hashlib

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import func

from classroom_engine.database import get_db

from ..db.models import (
    AuditSessionDB, AuditSignOffDB, AuditResultEnum, AuditSessionStatusEnum,
    RequirementDB, RegulationDB, ControlMappingDB
)
from .schemas import (
    CreateAuditSessionRequest, AuditSessionResponse, AuditSessionSummary, AuditSessionDetailResponse,
    AuditSessionListResponse, SignOffRequest, SignOffResponse,
    AuditChecklistItem, AuditChecklistResponse, AuditStatistics,
    PaginationMeta,
)

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/audit", tags=["compliance-audit"])


# ============================================================================
# Audit Sessions
# ============================================================================

@router.post("/sessions", response_model=AuditSessionResponse)
async def create_audit_session(
    request: CreateAuditSessionRequest,
    db: Session = Depends(get_db),
):
    """
    Create a new audit session for structured compliance reviews.

    An audit session groups requirements for systematic review by an auditor.
    """
    # Get total requirements count based on filters
    query = db.query(RequirementDB)
    if request.regulation_codes:
        reg_ids = db.query(RegulationDB.id).filter(
            RegulationDB.code.in_(request.regulation_codes)
        ).all()
        reg_ids = [r[0] for r in reg_ids]
        query = query.filter(RequirementDB.regulation_id.in_(reg_ids))

    total_items = query.count()

    # Create the session
    session = AuditSessionDB(
        id=str(uuid4()),
        name=request.name,
        description=request.description,
        auditor_name=request.auditor_name,
        auditor_email=request.auditor_email,
        auditor_organization=request.auditor_organization,
        status=AuditSessionStatusEnum.DRAFT,
        regulation_ids=request.regulation_codes,
        total_items=total_items,
        completed_items=0,
        compliant_count=0,
        non_compliant_count=0,
    )

    db.add(session)
    db.commit()
    db.refresh(session)

    return AuditSessionResponse(
        id=session.id,
        name=session.name,
        description=session.description,
        auditor_name=session.auditor_name,
        auditor_email=session.auditor_email,
        auditor_organization=session.auditor_organization,
        status=session.status.value,
        regulation_ids=session.regulation_ids,
        total_items=session.total_items,
        completed_items=session.completed_items,
        compliant_count=session.compliant_count,
        non_compliant_count=session.non_compliant_count,
        completion_percentage=session.completion_percentage,
        created_at=session.created_at,
        started_at=session.started_at,
        completed_at=session.completed_at,
    )


@router.get("/sessions", response_model=List[AuditSessionSummary])
async def list_audit_sessions(
    status: Optional[str] = None,
    db: Session = Depends(get_db),
):
    """
    List all audit sessions, optionally filtered by status.
    """
    query = db.query(AuditSessionDB)

    if status:
        try:
            status_enum = AuditSessionStatusEnum(status)
            query = query.filter(AuditSessionDB.status == status_enum)
        except ValueError:
            raise HTTPException(
                status_code=400,
                detail=f"Invalid status: {status}. Valid values: draft, in_progress, completed, archived"
            )

    sessions = query.order_by(AuditSessionDB.created_at.desc()).all()

    return [
        AuditSessionSummary(
            id=s.id,
            name=s.name,
            auditor_name=s.auditor_name,
            status=s.status.value,
            total_items=s.total_items,
            completed_items=s.completed_items,
            completion_percentage=s.completion_percentage,
            created_at=s.created_at,
            started_at=s.started_at,
            completed_at=s.completed_at,
        )
        for s in sessions
    ]


@router.get("/sessions/{session_id}", response_model=AuditSessionDetailResponse)
async def get_audit_session(
    session_id: str,
    db: Session = Depends(get_db),
):
    """
    Get detailed information about a specific audit session.
    """
    session = db.query(AuditSessionDB).filter(AuditSessionDB.id == session_id).first()
    if not session:
        raise HTTPException(status_code=404, detail=f"Audit session {session_id} not found")

    # Get sign-off statistics
    signoffs = db.query(AuditSignOffDB).filter(AuditSignOffDB.session_id == session_id).all()

    stats = AuditStatistics(
        total=session.total_items,
        compliant=sum(1 for s in signoffs if s.result == AuditResultEnum.COMPLIANT),
        compliant_with_notes=sum(1 for s in signoffs if s.result == AuditResultEnum.COMPLIANT_WITH_NOTES),
        non_compliant=sum(1 for s in signoffs if s.result == AuditResultEnum.NON_COMPLIANT),
        not_applicable=sum(1 for s in signoffs if s.result == AuditResultEnum.NOT_APPLICABLE),
        pending=session.total_items - len(signoffs),
        completion_percentage=session.completion_percentage,
    )

    return AuditSessionDetail(
        id=session.id,
        name=session.name,
        description=session.description,
        auditor_name=session.auditor_name,
        auditor_email=session.auditor_email,
        auditor_organization=session.auditor_organization,
        status=session.status.value,
        regulation_ids=session.regulation_ids,
        total_items=session.total_items,
        completed_items=session.completed_items,
        compliant_count=session.compliant_count,
        non_compliant_count=session.non_compliant_count,
        completion_percentage=session.completion_percentage,
        created_at=session.created_at,
        started_at=session.started_at,
        completed_at=session.completed_at,
        statistics=stats,
    )


@router.put("/sessions/{session_id}/start")
async def start_audit_session(
    session_id: str,
    db: Session = Depends(get_db),
):
    """
    Start an audit session (change status from draft to in_progress).
    """
    session = db.query(AuditSessionDB).filter(AuditSessionDB.id == session_id).first()
    if not session:
        raise HTTPException(status_code=404, detail=f"Audit session {session_id} not found")

    if session.status != AuditSessionStatusEnum.DRAFT:
        raise HTTPException(
            status_code=400,
            detail=f"Session cannot be started. Current status: {session.status.value}"
        )

    session.status = AuditSessionStatusEnum.IN_PROGRESS
    session.started_at = datetime.utcnow()
    db.commit()

    return {"success": True, "message": "Audit session started", "status": "in_progress"}


@router.put("/sessions/{session_id}/complete")
async def complete_audit_session(
    session_id: str,
    db: Session = Depends(get_db),
):
    """
    Complete an audit session (change status from in_progress to completed).
    """
    session = db.query(AuditSessionDB).filter(AuditSessionDB.id == session_id).first()
    if not session:
        raise HTTPException(status_code=404, detail=f"Audit session {session_id} not found")

    if session.status != AuditSessionStatusEnum.IN_PROGRESS:
        raise HTTPException(
            status_code=400,
            detail=f"Session cannot be completed. Current status: {session.status.value}"
        )

    session.status = AuditSessionStatusEnum.COMPLETED
    session.completed_at = datetime.utcnow()
    db.commit()

    return {"success": True, "message": "Audit session completed", "status": "completed"}


@router.put("/sessions/{session_id}/archive")
async def archive_audit_session(
    session_id: str,
    db: Session = Depends(get_db),
):
    """
    Archive a completed audit session.
    """
    session = db.query(AuditSessionDB).filter(AuditSessionDB.id == session_id).first()
    if not session:
        raise HTTPException(status_code=404, detail=f"Audit session {session_id} not found")

    if session.status != AuditSessionStatusEnum.COMPLETED:
        raise HTTPException(
            status_code=400,
            detail=f"Only completed sessions can be archived. Current status: {session.status.value}"
        )

    session.status = AuditSessionStatusEnum.ARCHIVED
    db.commit()

    return {"success": True, "message": "Audit session archived", "status": "archived"}


@router.delete("/sessions/{session_id}")
async def delete_audit_session(
    session_id: str,
    db: Session = Depends(get_db),
):
    """
    Delete an audit session and all its sign-offs.

    Only draft sessions can be deleted.
    """
    session = db.query(AuditSessionDB).filter(AuditSessionDB.id == session_id).first()
    if not session:
        raise HTTPException(status_code=404, detail=f"Audit session {session_id} not found")

    if session.status not in [AuditSessionStatusEnum.DRAFT, AuditSessionStatusEnum.ARCHIVED]:
        raise HTTPException(
            status_code=400,
            detail=f"Cannot delete session with status: {session.status.value}. Archive it first."
        )

    # Delete all sign-offs first (cascade should handle this, but be explicit)
    db.query(AuditSignOffDB).filter(AuditSignOffDB.session_id == session_id).delete()

    # Delete the session
    db.delete(session)
    db.commit()

    return {"success": True, "message": f"Audit session {session_id} deleted"}


# ============================================================================
# Audit Checklist & Sign-off
# ============================================================================

@router.get("/checklist/{session_id}", response_model=AuditChecklistResponse)
async def get_audit_checklist(
    session_id: str,
    page: int = Query(1, ge=1),
    page_size: int = Query(50, ge=1, le=200),
    status_filter: Optional[str] = None,
    regulation_filter: Optional[str] = None,
    search: Optional[str] = None,
    db: Session = Depends(get_db),
):
    """
    Get the audit checklist for a session with pagination.

    Returns requirements with their current sign-off status.
    """
    # Get the session
    session = db.query(AuditSessionDB).filter(AuditSessionDB.id == session_id).first()
    if not session:
        raise HTTPException(status_code=404, detail=f"Audit session {session_id} not found")

    # Build base query for requirements
    query = db.query(RequirementDB).join(RegulationDB)

    # Apply session's regulation filter
    if session.regulation_ids:
        query = query.filter(RegulationDB.code.in_(session.regulation_ids))

    # Apply additional filters
    if regulation_filter:
        query = query.filter(RegulationDB.code == regulation_filter)

    if search:
        search_term = f"%{search}%"
        query = query.filter(
            (RequirementDB.title.ilike(search_term)) |
            (RequirementDB.article.ilike(search_term)) |
            (RequirementDB.description.ilike(search_term))
        )

    # Get total count before pagination
    total_count = query.count()

    # Apply pagination
    requirements = (
        query
        .order_by(RegulationDB.code, RequirementDB.article)
        .offset((page - 1) * page_size)
        .limit(page_size)
        .all()
    )

    # Get existing sign-offs for these requirements
    req_ids = [r.id for r in requirements]
    signoffs = (
        db.query(AuditSignOffDB)
        .filter(AuditSignOffDB.session_id == session_id)
        .filter(AuditSignOffDB.requirement_id.in_(req_ids))
        .all()
    )
    signoff_map = {s.requirement_id: s for s in signoffs}

    # Get control mappings counts
    mapping_counts = (
        db.query(ControlMappingDB.requirement_id, func.count(ControlMappingDB.id))
        .filter(ControlMappingDB.requirement_id.in_(req_ids))
        .group_by(ControlMappingDB.requirement_id)
        .all()
    )
    mapping_count_map = dict(mapping_counts)

    # Build checklist items
    items = []
    for req in requirements:
        signoff = signoff_map.get(req.id)

        # Apply status filter if specified
        if status_filter:
            if status_filter == "pending" and signoff is not None:
                continue
            elif status_filter != "pending" and (signoff is None or signoff.result.value != status_filter):
                continue

        item = AuditChecklistItem(
            requirement_id=req.id,
            regulation_code=req.regulation.code,
            article=req.article,
            paragraph=req.paragraph,
            title=req.title,
            description=req.description,
            current_result=signoff.result.value if signoff else "pending",
            notes=signoff.notes if signoff else None,
            is_signed=signoff.signature_hash is not None if signoff else False,
            signed_at=signoff.signed_at if signoff else None,
            signed_by=signoff.signed_by if signoff else None,
            evidence_count=0,  # TODO: Add evidence count
            controls_mapped=mapping_count_map.get(req.id, 0),
            implementation_status=req.implementation_status,
            priority=req.priority,
        )
        items.append(item)

    # Calculate statistics
    all_signoffs = db.query(AuditSignOffDB).filter(AuditSignOffDB.session_id == session_id).all()
    stats = AuditStatistics(
        total=session.total_items,
        compliant=sum(1 for s in all_signoffs if s.result == AuditResultEnum.COMPLIANT),
        compliant_with_notes=sum(1 for s in all_signoffs if s.result == AuditResultEnum.COMPLIANT_WITH_NOTES),
        non_compliant=sum(1 for s in all_signoffs if s.result == AuditResultEnum.NON_COMPLIANT),
        not_applicable=sum(1 for s in all_signoffs if s.result == AuditResultEnum.NOT_APPLICABLE),
        pending=session.total_items - len(all_signoffs),
        completion_percentage=session.completion_percentage,
    )

    return AuditChecklistResponse(
        session=AuditSessionSummary(
            id=session.id,
            name=session.name,
            auditor_name=session.auditor_name,
            status=session.status.value,
            total_items=session.total_items,
            completed_items=session.completed_items,
            completion_percentage=session.completion_percentage,
            created_at=session.created_at,
            started_at=session.started_at,
            completed_at=session.completed_at,
        ),
        items=items,
        pagination=PaginationMeta(
            page=page,
            page_size=page_size,
            total=total_count,
            total_pages=(total_count + page_size - 1) // page_size,
        ),
        statistics=stats,
    )


@router.put("/checklist/{session_id}/items/{requirement_id}/sign-off", response_model=SignOffResponse)
async def sign_off_item(
    session_id: str,
    requirement_id: str,
    request: SignOffRequest,
    db: Session = Depends(get_db),
):
    """
    Sign off on a specific requirement in an audit session.

    If sign=True, creates a digital signature (SHA-256 hash).
    """
    # Validate session exists and is in progress
    session = db.query(AuditSessionDB).filter(AuditSessionDB.id == session_id).first()
    if not session:
        raise HTTPException(status_code=404, detail=f"Audit session {session_id} not found")

    if session.status not in [AuditSessionStatusEnum.DRAFT, AuditSessionStatusEnum.IN_PROGRESS]:
        raise HTTPException(
            status_code=400,
            detail=f"Cannot sign off items in session with status: {session.status.value}"
        )

    # Validate requirement exists
    requirement = db.query(RequirementDB).filter(RequirementDB.id == requirement_id).first()
    if not requirement:
        raise HTTPException(status_code=404, detail=f"Requirement {requirement_id} not found")

    # Map string result to enum
    try:
        result_enum = AuditResultEnum(request.result)
    except ValueError:
        raise HTTPException(
            status_code=400,
            detail=f"Invalid result: {request.result}. Valid values: compliant, compliant_notes, non_compliant, not_applicable, pending"
        )

    # Check if sign-off already exists
    signoff = (
        db.query(AuditSignOffDB)
        .filter(AuditSignOffDB.session_id == session_id)
        .filter(AuditSignOffDB.requirement_id == requirement_id)
        .first()
    )

    was_new = signoff is None
    old_result = signoff.result if signoff else None

    if signoff:
        # Update existing sign-off
        signoff.result = result_enum
        signoff.notes = request.notes
        signoff.updated_at = datetime.utcnow()
    else:
        # Create new sign-off
        signoff = AuditSignOffDB(
            id=str(uuid4()),
            session_id=session_id,
            requirement_id=requirement_id,
            result=result_enum,
            notes=request.notes,
        )
        db.add(signoff)

    # Create digital signature if requested
    signature = None
    if request.sign:
        timestamp = datetime.utcnow().isoformat()
        data = f"{result_enum.value}|{requirement_id}|{session.auditor_name}|{timestamp}"
        signature = hashlib.sha256(data.encode()).hexdigest()
        signoff.signature_hash = signature
        signoff.signed_at = datetime.utcnow()
        signoff.signed_by = session.auditor_name

    # Update session statistics
    if was_new:
        session.completed_items += 1

    # Update compliant/non-compliant counts
    if old_result != result_enum:
        if old_result == AuditResultEnum.COMPLIANT or old_result == AuditResultEnum.COMPLIANT_WITH_NOTES:
            session.compliant_count = max(0, session.compliant_count - 1)
        elif old_result == AuditResultEnum.NON_COMPLIANT:
            session.non_compliant_count = max(0, session.non_compliant_count - 1)

        if result_enum == AuditResultEnum.COMPLIANT or result_enum == AuditResultEnum.COMPLIANT_WITH_NOTES:
            session.compliant_count += 1
        elif result_enum == AuditResultEnum.NON_COMPLIANT:
            session.non_compliant_count += 1

    # Auto-start session if this is the first sign-off
    if session.status == AuditSessionStatusEnum.DRAFT:
        session.status = AuditSessionStatusEnum.IN_PROGRESS
        session.started_at = datetime.utcnow()

    db.commit()
    db.refresh(signoff)

    return SignOffResponse(
        id=signoff.id,
        session_id=signoff.session_id,
        requirement_id=signoff.requirement_id,
        result=signoff.result.value,
        notes=signoff.notes,
        is_signed=signoff.signature_hash is not None,
        signature_hash=signoff.signature_hash,
        signed_at=signoff.signed_at,
        signed_by=signoff.signed_by,
        created_at=signoff.created_at,
        updated_at=signoff.updated_at,
    )


@router.get("/checklist/{session_id}/items/{requirement_id}", response_model=SignOffResponse)
async def get_sign_off(
    session_id: str,
    requirement_id: str,
    db: Session = Depends(get_db),
):
    """
    Get the current sign-off status for a specific requirement.
    """
    signoff = (
        db.query(AuditSignOffDB)
        .filter(AuditSignOffDB.session_id == session_id)
        .filter(AuditSignOffDB.requirement_id == requirement_id)
        .first()
    )

    if not signoff:
        raise HTTPException(
            status_code=404,
            detail=f"No sign-off found for requirement {requirement_id} in session {session_id}"
        )

    return SignOffResponse(
        id=signoff.id,
        session_id=signoff.session_id,
        requirement_id=signoff.requirement_id,
        result=signoff.result.value,
        notes=signoff.notes,
        is_signed=signoff.signature_hash is not None,
        signature_hash=signoff.signature_hash,
        signed_at=signoff.signed_at,
        signed_by=signoff.signed_by,
        created_at=signoff.created_at,
        updated_at=signoff.updated_at,
    )


# ============================================================================
# PDF Report Generation
# ============================================================================

@router.get("/sessions/{session_id}/report/pdf")
async def generate_audit_pdf_report(
    session_id: str,
    language: str = Query("de", regex="^(de|en)$"),
    include_signatures: bool = Query(True),
    db: Session = Depends(get_db),
):
    """
    Generate a PDF report for an audit session.

    Parameters:
    - session_id: The audit session ID
    - language: Output language ('de' or 'en'), default 'de'
    - include_signatures: Include digital signature verification section

    Returns:
    - PDF file as streaming response
    """
    from fastapi.responses import StreamingResponse
    import io
    from ..services.audit_pdf_generator import AuditPDFGenerator

    # Validate session exists
    session = db.query(AuditSessionDB).filter(AuditSessionDB.id == session_id).first()
    if not session:
        raise HTTPException(
            status_code=404,
            detail=f"Audit session {session_id} not found"
        )

    try:
        generator = AuditPDFGenerator(db)
        pdf_bytes, filename = generator.generate(
            session_id=session_id,
            language=language,
            include_signatures=include_signatures,
        )

        return StreamingResponse(
            io.BytesIO(pdf_bytes),
            media_type="application/pdf",
            headers={
                "Content-Disposition": f"attachment; filename={filename}",
            }
        )
    except Exception as e:
        logger.error(f"Failed to generate PDF report: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to generate PDF report: {str(e)}"
        )
