"""
FastAPI routes for Whistleblower (HinSchG) — Hinweisgeberschutz.

Admin endpoints for managing reports + public endpoint for anonymous submissions.
Deadlines: 7 days acknowledgment (§ 17 Abs. 1), 3 months feedback (§ 17 Abs. 2).

Endpoints:
  GET    /whistleblower/reports              — list with filters
  GET    /whistleblower/reports/stats        — counts by status/category
  POST   /whistleblower/reports              — create report (admin)
  GET    /whistleblower/reports/{id}         — single report with messages
  PUT    /whistleblower/reports/{id}         — update status/priority/assignment
  POST   /whistleblower/reports/{id}/acknowledge — send acknowledgment
  POST   /whistleblower/reports/{id}/close   — close report
  POST   /whistleblower/reports/{id}/messages — add message
  GET    /whistleblower/reports/{id}/measures — list measures
  POST   /whistleblower/reports/{id}/measures — add measure
  POST   /whistleblower/submit              — public anonymous submission
  GET    /whistleblower/check/{access_key}  — reporter checks status
"""

import logging
import secrets
import string
from datetime import datetime, timedelta, timezone
from typing import Optional, List

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import text
from sqlalchemy.orm import Session

from classroom_engine.database import get_db
from .tenant_utils import get_tenant_id as _get_tenant_id
from .db_utils import row_to_dict as _row_to_dict

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

VALID_CATEGORIES = {"corruption", "fraud", "data_protection", "discrimination",
                    "environment", "competition", "product_safety", "tax_evasion", "other"}
VALID_STATUSES = {"new", "acknowledged", "under_review", "investigation",
                  "measures_taken", "closed", "rejected"}


def _gen_ref(tenant_id: str, db: Session) -> str:
    year = datetime.now().year
    q = text("SELECT COUNT(*) FROM compliance_whistleblower_reports WHERE tenant_id = :tid")
    count = db.execute(q, {"tid": tenant_id}).scalar() or 0
    return f"WB-{year}-{count + 1:06d}"


def _gen_access_key() -> str:
    chars = string.ascii_uppercase + string.digits
    parts = [''.join(secrets.choice(chars) for _ in range(4)) for _ in range(3)]
    return '-'.join(parts)


# =============================================================================
# Schemas
# =============================================================================

class ReportCreate(BaseModel):
    category: str = "other"
    title: str
    description: str
    is_anonymous: bool = True
    reporter_name: Optional[str] = None
    reporter_email: Optional[str] = None
    reporter_phone: Optional[str] = None
    priority: str = "normal"

class ReportUpdate(BaseModel):
    status: Optional[str] = None
    priority: Optional[str] = None
    assigned_to: Optional[str] = None
    category: Optional[str] = None

class MessageCreate(BaseModel):
    message: str
    sender_type: str = "admin"
    is_internal: bool = False

class MeasureCreate(BaseModel):
    title: str
    description: Optional[str] = None
    responsible: Optional[str] = None
    due_date: Optional[str] = None


# =============================================================================
# Admin Routes
# =============================================================================

@router.get("/reports")
def list_reports(
    status: Optional[str] = Query(None),
    category: Optional[str] = Query(None),
    limit: int = Query(50, le=200),
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    where = ["tenant_id = :tid"]
    params = {"tid": tenant_id, "lim": limit}
    if status:
        where.append("status = :st")
        params["st"] = status
    if category:
        where.append("category = :cat")
        params["cat"] = category
    q = text(f"SELECT * FROM compliance_whistleblower_reports WHERE {' AND '.join(where)} ORDER BY received_at DESC LIMIT :lim")
    return [_row_to_dict(r) for r in db.execute(q, params).fetchall()]


@router.get("/reports/stats")
def report_stats(
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    now = datetime.now(timezone.utc)
    q = text("SELECT status, COUNT(*) as cnt FROM compliance_whistleblower_reports WHERE tenant_id = :tid GROUP BY status")
    by_status = {r.status: r.cnt for r in db.execute(q, {"tid": tenant_id}).fetchall()}
    q2 = text("SELECT category, COUNT(*) as cnt FROM compliance_whistleblower_reports WHERE tenant_id = :tid GROUP BY category")
    by_category = {r.category: r.cnt for r in db.execute(q2, {"tid": tenant_id}).fetchall()}
    q3 = text("SELECT COUNT(*) FROM compliance_whistleblower_reports WHERE tenant_id = :tid AND deadline_acknowledgment < :now AND acknowledged_at IS NULL AND status = 'new'")
    overdue_ack = db.execute(q3, {"tid": tenant_id, "now": now}).scalar() or 0
    q4 = text("SELECT COUNT(*) FROM compliance_whistleblower_reports WHERE tenant_id = :tid AND deadline_feedback < :now AND status NOT IN ('closed', 'rejected')")
    overdue_fb = db.execute(q4, {"tid": tenant_id, "now": now}).scalar() or 0
    total = sum(by_status.values())
    return {"total": total, "by_status": by_status, "by_category": by_category, "overdue_acknowledgment": overdue_ack, "overdue_feedback": overdue_fb}


@router.post("/reports")
def create_report(
    body: ReportCreate,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    now = datetime.now(timezone.utc)
    ref = _gen_ref(tenant_id, db)
    ak = _gen_access_key()
    q = text("""
        INSERT INTO compliance_whistleblower_reports
        (tenant_id, reference_number, access_key, category, title, description,
         is_anonymous, reporter_name, reporter_email, reporter_phone, priority,
         received_at, deadline_acknowledgment, deadline_feedback)
        VALUES (:tid, :ref, :ak, :cat, :title, :desc,
                :anon, :rn, :re, :rp, :pri,
                :now, :dl_ack, :dl_fb)
        RETURNING *
    """)
    row = db.execute(q, {
        "tid": tenant_id, "ref": ref, "ak": ak,
        "cat": body.category, "title": body.title, "desc": body.description,
        "anon": body.is_anonymous, "rn": body.reporter_name,
        "re": body.reporter_email, "rp": body.reporter_phone,
        "pri": body.priority, "now": now,
        "dl_ack": now + timedelta(days=7),
        "dl_fb": now + timedelta(days=90),
    }).fetchone()
    db.commit()
    return _row_to_dict(row)


@router.get("/reports/{report_id}")
def get_report(
    report_id: str,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    row = db.execute(text("SELECT * FROM compliance_whistleblower_reports WHERE id = :rid AND tenant_id = :tid"),
                     {"rid": report_id, "tid": tenant_id}).fetchone()
    if not row:
        raise HTTPException(404, "Report not found")
    result = _row_to_dict(row)
    msgs = db.execute(text("SELECT * FROM compliance_whistleblower_messages WHERE report_id = :rid ORDER BY created_at"),
                      {"rid": report_id}).fetchall()
    result["messages"] = [_row_to_dict(m) for m in msgs]
    measures = db.execute(text("SELECT * FROM compliance_whistleblower_measures WHERE report_id = :rid ORDER BY created_at"),
                          {"rid": report_id}).fetchall()
    result["measures"] = [_row_to_dict(m) for m in measures]
    return result


@router.put("/reports/{report_id}")
def update_report(
    report_id: str,
    body: ReportUpdate,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    sets, params = [], {"rid": report_id, "tid": tenant_id}
    for field in ["status", "priority", "assigned_to", "category"]:
        val = getattr(body, field, None)
        if val is not None:
            sets.append(f"{field} = :{field}")
            params[field] = val
    if not sets:
        raise HTTPException(400, "No fields to update")
    sets.append("updated_at = NOW()")
    q = text(f"UPDATE compliance_whistleblower_reports SET {', '.join(sets)} WHERE id = :rid AND tenant_id = :tid RETURNING *")
    row = db.execute(q, params).fetchone()
    if not row:
        raise HTTPException(404, "Report not found")
    db.commit()
    return _row_to_dict(row)


@router.post("/reports/{report_id}/acknowledge")
def acknowledge_report(
    report_id: str,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    q = text("""
        UPDATE compliance_whistleblower_reports
        SET status = 'acknowledged', acknowledged_at = NOW(), updated_at = NOW()
        WHERE id = :rid AND tenant_id = :tid RETURNING *
    """)
    row = db.execute(q, {"rid": report_id, "tid": tenant_id}).fetchone()
    if not row:
        raise HTTPException(404, "Report not found")
    db.commit()
    return _row_to_dict(row)


@router.post("/reports/{report_id}/close")
def close_report(
    report_id: str,
    reason: str = Query(""),
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    q = text("""
        UPDATE compliance_whistleblower_reports
        SET status = 'closed', closed_at = NOW(), closure_reason = :reason, updated_at = NOW()
        WHERE id = :rid AND tenant_id = :tid RETURNING *
    """)
    row = db.execute(q, {"rid": report_id, "tid": tenant_id, "reason": reason}).fetchone()
    if not row:
        raise HTTPException(404, "Report not found")
    db.commit()
    return _row_to_dict(row)


@router.post("/reports/{report_id}/messages")
def add_message(
    report_id: str,
    body: MessageCreate,
    db: Session = Depends(get_db),
    tenant_id: str = Depends(_get_tenant_id),
):
    q = text("""
        INSERT INTO compliance_whistleblower_messages (report_id, sender_type, message, is_internal)
        VALUES (:rid, :st, :msg, :internal) RETURNING *
    """)
    row = db.execute(q, {"rid": report_id, "st": body.sender_type, "msg": body.message, "internal": body.is_internal}).fetchone()
    db.commit()
    return _row_to_dict(row)


@router.get("/reports/{report_id}/measures")
def list_measures(report_id: str, db: Session = Depends(get_db)):
    return [_row_to_dict(r) for r in db.execute(text(
        "SELECT * FROM compliance_whistleblower_measures WHERE report_id = :rid ORDER BY created_at"
    ), {"rid": report_id}).fetchall()]


@router.post("/reports/{report_id}/measures")
def add_measure(
    report_id: str, body: MeasureCreate,
    db: Session = Depends(get_db),
):
    q = text("""
        INSERT INTO compliance_whistleblower_measures (report_id, title, description, responsible, due_date)
        VALUES (:rid, :title, :desc, :resp, :due) RETURNING *
    """)
    row = db.execute(q, {"rid": report_id, "title": body.title, "desc": body.description,
                         "resp": body.responsible, "due": body.due_date}).fetchone()
    db.commit()
    return _row_to_dict(row)


# =============================================================================
# Public Routes (Anonymous)
# =============================================================================

@router.post("/submit")
def submit_report(body: ReportCreate, db: Session = Depends(get_db), tenant_id: str = Depends(_get_tenant_id)):
    """Public anonymous submission — same as create but returns only access_key."""
    body.is_anonymous = True
    result = create_report(body, db, tenant_id)
    return {"access_key": result["access_key"], "reference_number": result["reference_number"],
            "message": "Ihre Meldung wurde erfolgreich eingereicht. Nutzen Sie den Zugangscode um den Status zu pruefen."}


@router.get("/check/{access_key}")
def check_status(access_key: str, db: Session = Depends(get_db), tenant_id: str = Depends(_get_tenant_id)):
    """Reporter checks status anonymously via access key."""
    row = db.execute(text(
        "SELECT id, reference_number, status, category, received_at, acknowledged_at FROM compliance_whistleblower_reports WHERE access_key = :ak AND tenant_id = :tid"
    ), {"ak": access_key, "tid": tenant_id}).fetchone()
    if not row:
        raise HTTPException(404, "Meldung nicht gefunden")
    result = _row_to_dict(row)
    msgs = db.execute(text(
        "SELECT message, sender_type, created_at FROM compliance_whistleblower_messages WHERE report_id = :rid AND is_internal = FALSE ORDER BY created_at"
    ), {"rid": result["id"]}).fetchall()
    result["messages"] = [_row_to_dict(m) for m in msgs]
    return result
