"""
DSR (Data Subject Request) Routes — Betroffenenanfragen nach DSGVO Art. 15-21.

Phase 1 Step 4 refactor: thin handlers delegate to DSRService (CRUD/stats/
export/deadlines) and DSRWorkflowService (status/identity/assign/complete/
reject/communications/exception-checks/templates).
"""

from typing import Optional

from fastapi import APIRouter, Depends, Query, Header
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session

from classroom_engine.database import get_db
from compliance.api._http_errors import translate_domain_errors
from compliance.schemas.dsr import (
    AssignRequest,
    CompleteDSR,
    CreateTemplateVersion,
    DSRCreate,
    DSRUpdate,
    ExtendDeadline,
    RejectDSR,
    SendCommunication,
    StatusChange,
    UpdateExceptionCheck,
    VerifyIdentity,
)
from compliance.services.dsr_service import (
    ART17_EXCEPTIONS,
    DEFAULT_TENANT,
    DEADLINE_DAYS,
    DSRService,
    VALID_PRIORITIES,
    VALID_REQUEST_TYPES,
    VALID_SOURCES,
    VALID_STATUSES,
    _dsr_to_dict,
    _generate_request_number,
    _get_dsr_or_404,
    _record_history,
)
from compliance.services.dsr_workflow_service import DSRWorkflowService

router = APIRouter(prefix="/dsr", tags=["compliance-dsr"])


# ---------------------------------------------------------------------------
# DI helpers
# ---------------------------------------------------------------------------

def _get_tenant(x_tenant_id: Optional[str] = Header(None, alias='X-Tenant-ID')) -> str:
    return x_tenant_id or DEFAULT_TENANT


def _dsr_svc(db: Session = Depends(get_db)) -> DSRService:
    return DSRService(db)


def _wf_svc(db: Session = Depends(get_db)) -> DSRWorkflowService:
    return DSRWorkflowService(db)


# ---------------------------------------------------------------------------
# CRUD
# ---------------------------------------------------------------------------

@router.post("")
async def create_dsr(
    body: DSRCreate,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
):
    with translate_domain_errors():
        return svc.create(body, tenant_id)


@router.get("")
async def list_dsrs(
    status: Optional[str] = Query(None),
    request_type: Optional[str] = Query(None),
    assigned_to: Optional[str] = Query(None),
    priority: Optional[str] = Query(None),
    overdue_only: bool = Query(False),
    search: Optional[str] = Query(None),
    from_date: Optional[str] = Query(None),
    to_date: Optional[str] = Query(None),
    limit: int = Query(20, ge=1, le=100),
    offset: int = Query(0, ge=0),
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
):
    with translate_domain_errors():
        return svc.list(
            tenant_id, status=status, request_type=request_type,
            assigned_to=assigned_to, priority=priority,
            overdue_only=overdue_only, search=search,
            from_date=from_date, to_date=to_date,
            limit=limit, offset=offset,
        )


@router.get("/stats")
async def get_dsr_stats(
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
):
    with translate_domain_errors():
        return svc.stats(tenant_id)


@router.get("/export")
async def export_dsrs(
    format: str = Query("csv", pattern="^(csv|json)$"),
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
):
    with translate_domain_errors():
        result = svc.export(tenant_id, fmt=format)
    if format == "json":
        return result
    return StreamingResponse(
        result,
        media_type="text/csv; charset=utf-8",
        headers={"Content-Disposition": "attachment; filename=dsr_export.csv"},
    )


@router.post("/deadlines/process")
async def process_deadlines(
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
):
    with translate_domain_errors():
        return svc.process_deadlines(tenant_id)


# ---------------------------------------------------------------------------
# Templates (static paths before /{dsr_id})
# ---------------------------------------------------------------------------

@router.get("/templates")
async def get_templates(
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.get_templates(tenant_id)


@router.get("/templates/published")
async def get_published_templates(
    request_type: Optional[str] = Query(None),
    language: str = Query("de"),
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.get_published_templates(
            tenant_id, request_type=request_type, language=language,
        )


@router.get("/templates/{template_id}/versions")
async def get_template_versions(
    template_id: str,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.get_template_versions(template_id, tenant_id)


@router.post("/templates/{template_id}/versions")
async def create_template_version(
    template_id: str,
    body: CreateTemplateVersion,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.create_template_version(template_id, body, tenant_id)


@router.put("/template-versions/{version_id}/publish")
async def publish_template_version(
    version_id: str,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.publish_template_version(version_id, tenant_id)


# ---------------------------------------------------------------------------
# Single DSR (parameterized — after static paths)
# ---------------------------------------------------------------------------

@router.get("/{dsr_id}")
async def get_dsr(
    dsr_id: str,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
):
    with translate_domain_errors():
        return svc.get(dsr_id, tenant_id)


@router.put("/{dsr_id}")
async def update_dsr(
    dsr_id: str,
    body: DSRUpdate,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
):
    with translate_domain_errors():
        return svc.update(dsr_id, body, tenant_id)


@router.delete("/{dsr_id}")
async def delete_dsr(
    dsr_id: str,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
):
    with translate_domain_errors():
        return svc.delete(dsr_id, tenant_id)


# ---------------------------------------------------------------------------
# Workflow actions
# ---------------------------------------------------------------------------

@router.post("/{dsr_id}/status")
async def change_status(
    dsr_id: str,
    body: StatusChange,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.change_status(dsr_id, body, tenant_id)


@router.post("/{dsr_id}/reject-art11")
async def reject_art11(
    dsr_id: str,
    notes: str = Query(""),
    tenant_id: str = Depends(_get_tenant),
    db: Session = Depends(get_db),
):
    """Reject DSR under Art. 11 DSGVO — data subject not identifiable."""
    from compliance.services.dsr_art11_service import DSRArt11Service
    with translate_domain_errors():
        return DSRArt11Service(db).reject_not_identifiable(dsr_id, tenant_id, notes)


@router.post("/{dsr_id}/verify-identity")
async def verify_identity(
    dsr_id: str,
    body: VerifyIdentity,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.verify_identity(dsr_id, body, tenant_id)


@router.post("/{dsr_id}/assign")
async def assign_dsr(
    dsr_id: str,
    body: AssignRequest,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.assign(dsr_id, body, tenant_id)


@router.post("/{dsr_id}/extend")
async def extend_deadline(
    dsr_id: str,
    body: ExtendDeadline,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.extend_deadline(dsr_id, body, tenant_id)


@router.post("/{dsr_id}/complete")
async def complete_dsr(
    dsr_id: str,
    body: CompleteDSR,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.complete(dsr_id, body, tenant_id)


@router.post("/{dsr_id}/reject")
async def reject_dsr(
    dsr_id: str,
    body: RejectDSR,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.reject(dsr_id, body, tenant_id)


# ---------------------------------------------------------------------------
# History & Communications
# ---------------------------------------------------------------------------

@router.get("/{dsr_id}/history")
async def get_history(
    dsr_id: str,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.get_history(dsr_id, tenant_id)


@router.get("/{dsr_id}/communications")
async def get_communications(
    dsr_id: str,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.get_communications(dsr_id, tenant_id)


@router.post("/{dsr_id}/communicate")
async def send_communication(
    dsr_id: str,
    body: SendCommunication,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.send_communication(dsr_id, body, tenant_id)


# ---------------------------------------------------------------------------
# Exception Checks (Art. 17)
# ---------------------------------------------------------------------------

@router.get("/{dsr_id}/exception-checks")
async def get_exception_checks(
    dsr_id: str,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.get_exception_checks(dsr_id, tenant_id)


@router.post("/{dsr_id}/exception-checks/init")
async def init_exception_checks(
    dsr_id: str,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.init_exception_checks(dsr_id, tenant_id)


@router.put("/{dsr_id}/exception-checks/{check_id}")
async def update_exception_check(
    dsr_id: str,
    check_id: str,
    body: UpdateExceptionCheck,
    tenant_id: str = Depends(_get_tenant),
    svc: DSRWorkflowService = Depends(_wf_svc),
):
    with translate_domain_errors():
        return svc.update_exception_check(dsr_id, check_id, body, tenant_id)


# =============================================================================
# User Data Export (Art. 15 / Art. 20)
# =============================================================================


@router.get("/{dsr_id}/export-user-data")
async def export_user_data(
    dsr_id: str,
    format: str = Query("json"),
    tenant_id: str = Depends(_get_tenant),
    svc: DSRService = Depends(_dsr_svc),
    db: Session = Depends(get_db),
):
    """Export all CMP data about the data subject as JSON, CSV, or PDF."""
    import io
    from compliance.services.dsr_export_service import DSRExportService

    with translate_domain_errors():
        dsr = svc.get(dsr_id, tenant_id)
    email = dsr.get("requester_email")
    if not email:
        from fastapi import HTTPException
        raise HTTPException(400, "DSR has no requester email")

    export_svc = DSRExportService(db)
    if format == "pdf":
        content, filename = export_svc.export_pdf(tenant_id, email)
        return StreamingResponse(io.BytesIO(content), media_type="application/pdf",
                                 headers={"Content-Disposition": f'attachment; filename="{filename}"'})
    elif format == "csv":
        content, filename = export_svc.export_csv(tenant_id, email)
        return StreamingResponse(io.BytesIO(content), media_type="text/csv",
                                 headers={"Content-Disposition": f'attachment; filename="{filename}"'})
    else:
        content, filename = export_svc.export_json(tenant_id, email)
        return StreamingResponse(io.BytesIO(content), media_type="application/json",
                                 headers={"Content-Disposition": f'attachment; filename="{filename}"'})
