"""System-level endpoints (admin only).

Provides manual triggers for background operations such as the MITRE
ATT&CK synchronisation, intel scanning, Atomic Red Team import, and
scheduler health introspection.

Also exposes email configuration CRUD (admin only) that writes to the
system_configs table so settings survive container restarts.
"""

# Import logging
import logging
from typing import Optional

from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request, status
from pydantic import BaseModel
from sqlalchemy.orm import Session

from app.database import SessionLocal, get_db
from app.dependencies.auth import require_role
from app.models.user import User
from app.services.mitre_sync_service import sync_mitre
from app.services.intel_service import scan_intel
from app.services.atomic_import_service import import_atomic_red_team
from app.jobs.mitre_sync_job import scheduler

# Import limiter from app.limiter
from app.limiter import limiter

# Assign logger = logging.getLogger(__name__)
logger = logging.getLogger(__name__)

# Assign router = APIRouter(prefix="/system", tags=["system"])
router = APIRouter(prefix="/system", tags=["system"])


# ---------------------------------------------------------------------------
# Pydantic schemas for email config
# ---------------------------------------------------------------------------


class EmailConfigOut(BaseModel):
    enabled: bool
    host: str
    port: int
    username: str
    from_email: str
    use_tls: bool
    # password is never returned


class EmailConfigUpdate(BaseModel):
    enabled: Optional[bool] = None
    host: Optional[str] = None
    port: Optional[int] = None
    username: Optional[str] = None
    password: Optional[str] = None
    from_email: Optional[str] = None
    use_tls: Optional[bool] = None


class EmailTestRequest(BaseModel):
    to: str


# ---------------------------------------------------------------------------
# Helpers for system_configs CRUD
# ---------------------------------------------------------------------------

_SMTP_KEYS = {
    "enabled": "smtp.enabled",
    "host": "smtp.host",
    "port": "smtp.port",
    "username": "smtp.username",
    "password": "smtp.password",  # nosec B105
    "from_email": "smtp.from_email",
    "use_tls": "smtp.use_tls",
}


def _upsert_config(db: Session, key: str, value: str) -> None:
    from app.models.system_config import SystemConfig  # lazy import avoids circular

    row = db.query(SystemConfig).filter(SystemConfig.key == key).first()
    if row:
        row.value = value
    else:
        row = SystemConfig(key=key, value=value)
        db.add(row)


def _read_email_config_from_db(db: Session) -> dict:
    """Return a dict with resolved email settings (DB overrides env)."""
    from app.services.email_service import _get_smtp_config

    return _get_smtp_config(db)


def _bg_mitre_sync() -> None:
    """Run MITRE sync in a background task with its own DB session."""
    logger.info("Background MITRE sync task starting...")
    db = SessionLocal()
    try:
        summary = sync_mitre(db)
        logger.info("Background MITRE sync task finished — %s", summary)
    except Exception:
        logger.exception("Background MITRE sync task failed")
    finally:
        db.close()


@router.post("/sync-mitre")
# Apply the @limiter.limit decorator
@limiter.limit("2/hour")
# Define function trigger_mitre_sync
def trigger_mitre_sync(
    # Entry: request
    request: Request,
    background_tasks: BackgroundTasks,
    current_user: User = Depends(require_role("admin")),
):
    """Manually trigger a MITRE ATT&CK synchronisation in the background.

    **Requires** the ``admin`` role.

    Returns immediately — the sync runs asynchronously. Poll
    ``/system/scheduler-status`` for progress, or check server logs.
    """
    background_tasks.add_task(_bg_mitre_sync)
    return {
        "message": "MITRE sync started in background",
        "status": "started",
        "new": 0,
        "updated": 0,
    }


# Apply the @router.post decorator
@router.post("/run-intel-scan")
# Define function trigger_intel_scan
def trigger_intel_scan(
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(require_role("admin")),
) -> dict:
    """Manually trigger a threat-intelligence scan.

    **Requires** the ``admin`` role.

    Returns a JSON object with the scan summary including the count of
    new intel items found.
    """
    # Assign summary = scan_intel(db)
    summary = scan_intel(db)
    # Return {
    return {
        # Literal argument value
        "message": "Intel scan completed",
        # Literal argument value
        "new_items": summary["new_items"],
    }


# Apply the @router.post decorator
@router.post("/import-atomic-tests")
# Apply the @limiter.limit decorator
@limiter.limit("2/hour")
# Define function trigger_atomic_import
def trigger_atomic_import(
    # Entry: request
    request: Request,
    # Entry: db
    db: Session = Depends(get_db),
    # Entry: current_user
    current_user: User = Depends(require_role("admin")),
) -> dict:
    """Trigger an import of Atomic Red Team tests as TestTemplates.

    **Requires** the ``admin`` role.

    Downloads the Atomic Red Team repository ZIP from GitHub, parses the
    YAML files, and creates/updates TestTemplate records.  Running this
    endpoint multiple times is idempotent — duplicates are skipped.

    Returns a JSON object with import statistics.
    """
    # Attempt the following; catch errors below
    try:
        # Assign summary = import_atomic_red_team(db)
        summary = import_atomic_red_team(db)
    # Handle Exception
    except Exception as exc:
        # Log error: "Atomic Red Team import failed: %s", exc, exc_info
        logger.error("Atomic Red Team import failed: %s", exc, exc_info=True)
        # Return {
        return {
            # Literal argument value
            "message": "Import failed. Check server logs for details.",
        }

    # Return {
    return {
        # Literal argument value
        "message": "Import completed",
        # Literal argument value
        "imported": summary["created"],
        # Literal argument value
        "skipped": summary["skipped_existing"],
        # Literal argument value
        "total_parsed": summary["total_tests_parsed"],
    }


# Apply the @router.get decorator
@router.get("/scheduler-status")
# Define function scheduler_status
def scheduler_status(
    # Entry: current_user
    current_user: User = Depends(require_role("admin")),
) -> dict:
    """Return the current state of the background scheduler.

    **Requires** the ``admin`` role.
    """
    # Assign jobs = scheduler.get_jobs()
    jobs = scheduler.get_jobs()
    # Return {
    return {
        # Literal argument value
        "running": scheduler.running,
        # Literal argument value
        "jobs": [
            {
                # Literal argument value
                "id": job.id,
                # Literal argument value
                "name": job.name,
                # Literal argument value
                "next_run_time": str(job.next_run_time) if job.next_run_time else None,
            }
            for job in jobs
        ],
    }


# ---------------------------------------------------------------------------
# Jira config endpoints (admin only)
# ---------------------------------------------------------------------------


class JiraConfigOut(BaseModel):
    enabled: bool
    url: str
    project_key: str
    parent_ticket: str
    parent_ticket_standalone: str
    admin_email_set: bool          # whether admin email is configured (value never returned)
    tempo_admin_token_set: bool    # whether admin Tempo token is configured


class JiraConfigUpdate(BaseModel):
    enabled: Optional[bool] = None
    url: Optional[str] = None
    project_key: Optional[str] = None
    parent_ticket: Optional[str] = None
    parent_ticket_standalone: Optional[str] = None
    admin_email: Optional[str] = None
    admin_api_token: Optional[str] = None
    tempo_admin_token: Optional[str] = None


_JIRA_KEYS = {
    "enabled": "jira.enabled",
    "url": "jira.url",
    "project_key": "jira.project_key",
    "parent_ticket": "jira.parent_ticket",
    "parent_ticket_standalone": "jira.parent_ticket_standalone",
    "admin_email": "jira.admin_email",
    "admin_api_token": "jira.admin_api_token",
    "tempo_admin_token": "tempo.admin_token",
}


@router.get("/jira-config", response_model=JiraConfigOut)
def get_jira_config(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Return current Jira configuration (merged DB + env).

    **Requires** the ``admin`` role. Credential values are never returned.
    """
    from app.services.jira_service import (
        get_jira_url, get_jira_project_key, is_jira_enabled,
        get_jira_parent_ticket, get_jira_parent_ticket_standalone,
        get_admin_jira_email, _read_system_config,
    )

    return JiraConfigOut(
        enabled=is_jira_enabled(db),
        url=get_jira_url(db) or "",
        project_key=get_jira_project_key(db) or "",
        parent_ticket=get_jira_parent_ticket(db) or "",
        parent_ticket_standalone=get_jira_parent_ticket_standalone(db) or "",
        admin_email_set=bool(get_admin_jira_email(db)),
        tempo_admin_token_set=bool(_read_system_config(db, "tempo.admin_token")),
    )


@router.patch("/jira-config", response_model=JiraConfigOut)
def update_jira_config(
    payload: JiraConfigUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Update Jira configuration and persist to DB.

    **Requires** the ``admin`` role. Only provided fields are updated.
    """
    from app.services.jira_service import (
        upsert_jira_config, get_jira_url, get_jira_project_key, is_jira_enabled,
        get_jira_parent_ticket, get_jira_parent_ticket_standalone,
        get_admin_jira_email, _read_system_config,
    )

    update_data = payload.model_dump(exclude_unset=True)
    for field, val in update_data.items():
        db_key = _JIRA_KEYS.get(field)
        if db_key:
            upsert_jira_config(db, db_key, str(val))
    db.commit()

    return JiraConfigOut(
        enabled=is_jira_enabled(db),
        url=get_jira_url(db) or "",
        project_key=get_jira_project_key(db) or "",
        parent_ticket=get_jira_parent_ticket(db) or "",
        parent_ticket_standalone=get_jira_parent_ticket_standalone(db) or "",
        admin_email_set=bool(get_admin_jira_email(db)),
        tempo_admin_token_set=bool(_read_system_config(db, "tempo.admin_token")),
    )


@router.post("/jira-test")
def test_jira_connection(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Test the Jira connection using the admin account credentials.

    Always returns HTTP 200 with a ``status`` field so Cloudflare never
    replaces the response with its own error page.
    """
    from app.services.jira_service import (
        get_admin_jira_client, get_jira_url, has_admin_jira_configured,
        get_admin_jira_email,
    )

    jira_url = get_jira_url(db)
    if not jira_url:
        return {"status": "error", "message": "Jira URL is not configured.", "jira_url": ""}

    if not has_admin_jira_configured(db):
        return {
            "status": "error",
            "message": (
                "Admin Jira credentials not configured. "
                "Set the Admin Email and Admin API Token in the fields above."
            ),
            "jira_url": jira_url,
        }

    admin_email = get_admin_jira_email(db)

    try:
        jira = get_admin_jira_client(db)
        try:
            jira._session.timeout = 10  # type: ignore[attr-defined]
        except Exception:  # nosec B110
            pass
        myself = jira.myself()
        connected_as = (
            (myself.get("displayName") if isinstance(myself, dict) else None)
            or (myself.get("emailAddress") if isinstance(myself, dict) else None)
            or admin_email
            or "authenticated"
        )
        return {"status": "ok", "connected_as": connected_as, "jira_url": jira_url}
    except Exception as exc:
        err = str(exc)
        if "Expecting value" in err or "line 1 column 1" in err:
            msg = (
                "Jira returned a non-JSON response. "
                "Verify the URL (e.g. https://company.atlassian.net), "
                "admin email and API token."
            )
        elif "401" in err or "Unauthorized" in err:
            msg = (
                "Authentication failed (401). "
                f"Check that the admin email ({admin_email or 'not set'}) "
                "and API token are correct."
            )
        elif "403" in err or "Forbidden" in err:
            msg = "Access denied (403). The admin token may not have permission for this Jira project."
        elif "timed out" in err.lower() or "timeout" in err.lower():
            msg = "Connection timed out. Check that the Jira URL is reachable from the server."
        else:
            msg = f"Jira connection failed: {err}"
        logger.warning("Admin Jira test connection failed: %s", err)
        return {"status": "error", "message": msg, "jira_url": jira_url}


# ---------------------------------------------------------------------------
# POST /system/tempo-test
# ---------------------------------------------------------------------------


@router.post("/tempo-test")
def test_tempo_connection(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Test the admin Tempo token connectivity.

    Always returns HTTP 200 with a ``status`` field so Cloudflare never
    intercepts the response.
    """
    from app.services.tempo_service import get_admin_tempo_token, get_admin_tempo_client

    admin_token = get_admin_tempo_token(db)
    if not admin_token:
        return {
            "status": "error",
            "message": (
                "Admin Tempo token not configured. "
                "Set it in System Settings → Jira Configuration → Admin Tempo Token."
            ),
        }

    try:
        tempo = get_admin_tempo_client(db)
        worklogs = tempo.search_worklogs(dateFrom="2024-01-01", dateTo="2024-01-02")
        count = len(worklogs) if isinstance(worklogs, list) else "n/a"
        return {
            "status": "ok",
            "message": "Admin Tempo token connected successfully.",
            "worklogs_found": count,
        }
    except Exception as exc:
        err = str(exc)
        if "401" in err or "Unauthorized" in err:
            msg = (
                "Authentication failed (401). "
                "Check the admin Tempo API token — obtain it at "
                "Jira → Apps → Tempo → Settings → API Integration."
            )
        elif "403" in err or "Forbidden" in err:
            msg = "Access denied (403). The admin Tempo token lacks the required permissions."
        else:
            msg = f"Tempo connection failed: {err}"
        logger.warning("Admin Tempo test connection failed: %s", err)
        return {"status": "error", "message": msg}


# ---------------------------------------------------------------------------
# GET /system/email-config
# ---------------------------------------------------------------------------


@router.get("/email-config", response_model=EmailConfigOut)
def get_email_config(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Return current SMTP email configuration (merged DB + env).

    **Requires** the ``admin`` role. Password is never returned.
    """
    cfg = _read_email_config_from_db(db)
    return EmailConfigOut(
        enabled=cfg["enabled"],
        host=cfg["host"],
        port=cfg["port"],
        username=cfg["username"],
        from_email=cfg["from_email"],
        use_tls=cfg["use_tls"],
    )


# ---------------------------------------------------------------------------
# PATCH /system/email-config
# ---------------------------------------------------------------------------


@router.patch("/email-config", response_model=EmailConfigOut)
def update_email_config(
    payload: EmailConfigUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Update SMTP email configuration and persist to DB.

    **Requires** the ``admin`` role.
    Only provided fields are updated (partial update).
    """
    update_data = payload.model_dump(exclude_unset=True)
    for field, val in update_data.items():
        db_key = _SMTP_KEYS.get(field)
        if db_key:
            _upsert_config(db, db_key, str(val))
    db.commit()

    cfg = _read_email_config_from_db(db)
    return EmailConfigOut(
        enabled=cfg["enabled"],
        host=cfg["host"],
        port=cfg["port"],
        username=cfg["username"],
        from_email=cfg["from_email"],
        use_tls=cfg["use_tls"],
    )


# ---------------------------------------------------------------------------
# POST /system/email-test
# ---------------------------------------------------------------------------


# ---------------------------------------------------------------------------
# ATT&CK Evaluations endpoints (admin only)
# ---------------------------------------------------------------------------


@router.get("/attck-evaluations/rounds")
def list_evaluation_rounds(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Return all public CrowdStrike ENTERPRISE evaluation rounds with import status.

    Each entry includes whether it has already been imported into this platform.
    """
    from app.services.attck_evaluations_service import fetch_rounds_with_status
    from app.models.evaluation_import import EvaluationImport

    status_info = fetch_rounds_with_status()
    rounds = status_info["rounds"]

    imported = {
        row.adversary_name.lower(): row
        for row in db.query(EvaluationImport).filter(EvaluationImport.status == "completed").all()
    }

    round_list = [
        {
            "name": r["name"],
            "display_name": r.get("display_name", r["name"]),
            "eval_round": r["eval_round"],
            "imported": r["name"].lower() in imported,
            "imported_at": imported[r["name"].lower()].imported_at.isoformat()
                if r["name"].lower() in imported else None,
            "tests_created": imported[r["name"].lower()].tests_created
                if r["name"].lower() in imported else None,
            "techniques_covered": imported[r["name"].lower()].techniques_covered
                if r["name"].lower() in imported else None,
        }
        for r in rounds
    ]

    return {
        "rounds": round_list,
        "api_reachable": status_info["api_reachable"],
        "api_error": status_info.get("api_error"),
    }


@router.post("/attck-evaluations/import")
def import_evaluation_round(
    payload: dict,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Import a specific ATT&CK Evaluation round for CrowdStrike.

    Body: { "adversary_name": "apt29", "adversary_display": "APT29", "eval_round": 2 }

    Creates tests in ``in_review`` state — Blue Leads must validate each
    result before it counts as real coverage.
    """
    from app.services.attck_evaluations_service import import_evaluation_round as _import

    adversary_name = payload.get("adversary_name", "")
    adversary_display = payload.get("adversary_display", adversary_name)
    eval_round = payload.get("eval_round", 0)

    if not adversary_name or not eval_round:
        raise HTTPException(status_code=400, detail="adversary_name and eval_round are required")

    try:
        summary = _import(db, adversary_name, adversary_display, eval_round, current_user)
    except ValueError as exc:
        raise HTTPException(status_code=409, detail=str(exc))
    except Exception as exc:
        logger.error("ATT&CK Evaluation import failed: %s", exc, exc_info=True)
        raise HTTPException(status_code=502, detail=f"Import failed: {exc}")

    return {
        "message": f"Import complete — {summary['created']} tests created",
        **summary,
    }


@router.post("/attck-evaluations/import-latest")
def import_latest_evaluation(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Import the latest available CrowdStrike evaluation round automatically.

    Returns 409 if the latest round was already imported.
    """
    from app.services.attck_evaluations_service import get_latest_round, import_evaluation_round as _import

    try:
        latest = get_latest_round()
    except Exception as exc:
        raise HTTPException(status_code=502, detail=f"Could not reach MITRE Evaluations API: {exc}")

    try:
        summary = _import(
            db,
            latest["name"],
            latest.get("display_name", latest["name"]),
            latest["eval_round"],
            current_user,
        )
    except ValueError as exc:
        raise HTTPException(status_code=409, detail=str(exc))
    except Exception as exc:
        logger.error("ATT&CK Evaluation import failed: %s", exc, exc_info=True)
        raise HTTPException(status_code=502, detail=f"Import failed: {exc}")

    return {
        "message": f"Import complete — {summary['created']} tests created",
        **summary,
    }


@router.get("/attck-evaluations/check-new")
def check_new_evaluation_round(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Check if a new ATT&CK Evaluation round is available that hasn't been imported yet."""
    from app.services.attck_evaluations_service import check_for_new_round

    return check_for_new_round(db)


@router.post("/attck-evaluations/bulk-approve")
def bulk_approve_evaluation_tests(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Bulk-approve all Blue Team validation for ATT&CK Evaluation imported tests.

    Finds every test in ``in_review`` state whose name starts with ``[EVAL R``
    and approves the Blue Team side.  Because all evaluation imports pre-approve
    the Red Team side, this moves every matched test to ``validated`` state.

    **Important caveats** (enforced by UI warnings before this is called):
    - Results come from a controlled MITRE lab, NOT the organisation's env.
    - Validated tests will immediately affect coverage metrics and dashboards.
    - Blue Leads should still spot-check high-priority techniques individually.
    """
    from datetime import datetime
    from app.models.test import Test
    from app.models.enums import TestState
    from app.models.technique import Technique
    from app.services.status_service import recalculate_technique_status
    from app.services.audit_service import log_action

    # Find all pending evaluation tests
    pending = (
        db.query(Test)
        .filter(
            Test.state == TestState.in_review,
            Test.name.like("[EVAL R%"),
        )
        .all()
    )

    if not pending:
        return {
            "approved": 0,
            "techniques_recalculated": 0,
            "message": "No pending evaluation tests found — nothing to approve.",
        }

    now = datetime.utcnow()
    affected_technique_ids: set = set()

    for test in pending:
        # Approve blue side
        test.blue_validation_status = "approved"
        test.blue_validated_by = current_user.id
        test.blue_validated_at = now
        test.blue_validation_notes = (
            "Bulk-approved via ATT&CK Evaluations admin panel. "
            "Source: MITRE lab environment — not organisational detection."
        )

        # Red side was pre-approved during import → move to validated
        if test.red_validation_status == "approved":
            test.state = TestState.validated
        # else stays in_review (shouldn't happen for eval imports, but be safe)

        if test.technique_id:
            affected_technique_ids.add(test.technique_id)

        log_action(
            db,
            user_id=current_user.id,
            action="bulk_eval_approve",
            entity_type="test",
            entity_id=test.id,
            details={"source": "attck_evaluation_bulk_approve"},
        )

    db.flush()

    # Recalculate coverage for every touched technique
    for tech_id in affected_technique_ids:
        tech = db.query(Technique).filter(Technique.id == tech_id).first()
        if tech:
            recalculate_technique_status(db, tech)

    db.commit()

    logger.info(
        "Bulk eval approval: %d tests validated, %d techniques recalculated (by %s)",
        len(pending), len(affected_technique_ids), current_user.email,
    )

    return {
        "approved": len(pending),
        "techniques_recalculated": len(affected_technique_ids),
        "message": (
            f"{len(pending)} evaluation tests approved and moved to Validated. "
            f"{len(affected_technique_ids)} technique statuses recalculated."
        ),
    }


@router.get("/attck-evaluations/pending-count")
def get_pending_evaluation_count(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Return the number of imported evaluation tests still awaiting Blue approval."""
    from app.models.test import Test
    from app.models.enums import TestState

    count = (
        db.query(Test)
        .filter(
            Test.state == TestState.in_review,
            Test.name.like("[EVAL R%"),
        )
        .count()
    )
    return {"pending": count}


@router.post("/attck-evaluations/re-enrich")
def re_enrich_evaluation_round(
    payload: dict,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Re-enrich already-imported evaluation tests with rich data from the MITRE API.

    Updates procedure_text (attack path + criteria), description (data sources +
    substep references) and red_summary — without changing detection results,
    state or validation status.

    Body: { "adversary_name": "turla", "adversary_display": "Turla", "eval_round": 5 }

    Useful to upgrade tests that were imported before the enrichment feature
    was added.
    """
    from app.services.attck_evaluations_service import re_enrich_evaluation_round as _re_enrich

    adversary_name = payload.get("adversary_name", "")
    adversary_display = payload.get("adversary_display", adversary_name)
    eval_round = payload.get("eval_round", 0)

    if not adversary_name or not eval_round:
        raise HTTPException(status_code=400, detail="adversary_name and eval_round are required")

    try:
        summary = _re_enrich(db, adversary_name, adversary_display, eval_round, current_user)
    except Exception as exc:
        logger.error("ATT&CK Evaluation re-enrich failed: %s", exc, exc_info=True)
        raise HTTPException(status_code=502, detail=f"Re-enrich failed: {exc}")

    return summary


@router.post("/recalculate-coverage")
def recalculate_all_coverage(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Recompute status_global for every technique based on its current tests.

    **Requires** the ``admin`` role.
    Run this after logic changes to the coverage scoring rules.
    """
    from app.models.technique import Technique
    from app.services.status_service import recalculate_technique_status

    techniques = db.query(Technique).all()
    updated = 0
    for tech in techniques:
        old_status = tech.status_global
        recalculate_technique_status(db, tech)
        if tech.status_global != old_status:
            updated += 1
    db.commit()
    logger.info(
        "Bulk coverage recalculation: %d/%d techniques updated (by %s)",
        updated, len(techniques), current_user.email,
    )
    return {
        "total": len(techniques),
        "updated": updated,
        "message": f"Coverage recalculated for {len(techniques)} techniques. {updated} statuses changed.",
    }


@router.post("/email-test")
def send_test_email(
    payload: EmailTestRequest,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_role("admin")),
):
    """Send a test email to verify SMTP configuration.

    **Requires** the ``admin`` role.
    Returns 200 on success, 502 if sending fails.
    """
    from app.services.email_service import send_test_email as _send_test

    ok = _send_test(payload.to, db=db)
    if not ok:
        raise HTTPException(
            status_code=status.HTTP_502_BAD_GATEWAY,
            detail="Failed to send test email. Check SMTP configuration and server logs.",
        )
    return {"detail": f"Test email sent to {payload.to}"}
