"""
Anti-Cheat Engine — Anomaly detection that runs BEFORE XP is awarded.

Detection rules:
  - speed_violation: Completed faster than 25% of estimated duration
  - burst_violation: More than 5 completions in a single day
  - quiz_without_access: Passed quiz but no file access audit record
  - self_report_no_evidence: Self-reported with no evidence URL (Tier 3)
  - api_contradicts: Self-reported complete but API sync says not done
"""

from datetime import datetime, timedelta, timezone
from uuid import UUID

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.activity import Activity, ActivityCompletion
from app.models.anti_cheat import AnomalyFlag
from app.models.content_tracking import ContentAccessLog, TrackedLink


async def run_anti_cheat_checks(
    db: AsyncSession,
    user_id: UUID,
    activity_id: UUID,
    completion: ActivityCompletion,
) -> list[AnomalyFlag]:
    """
    Run all applicable anti-cheat rules against a completion.
    Returns list of any flags created. Called by XP engine before awarding XP.
    """
    flags: list[AnomalyFlag] = []
    activity = await db.get(Activity, activity_id)
    if not activity:
        return flags

    # Rule 1: Speed violation
    flag = await _check_speed_violation(db, user_id, completion, activity)
    if flag:
        flags.append(flag)

    # Rule 2: Burst violation
    flag = await _check_burst_violation(db, user_id)
    if flag:
        flags.append(flag)

    # Rule 3: Quiz without content access
    if activity.category in ("reading", "video") and activity.xp_open_pct < 100:
        flag = await _check_quiz_without_access(db, user_id, activity)
        if flag:
            flags.append(flag)

    # Rule 4: Self-report without evidence
    if completion.source == "self_reported" and not completion.evidence_url:
        flag = AnomalyFlag(
            user_id=user_id,
            completion_id=completion.id,
            flag_type="self_report_no_evidence",
            severity="info",
            description=f"Self-reported completion of '{activity.title}' without evidence URL.",
        )
        db.add(flag)
        flags.append(flag)

    return flags


async def _check_speed_violation(
    db: AsyncSession,
    user_id: UUID,
    completion: ActivityCompletion,
    activity: Activity,
) -> AnomalyFlag | None:
    """Flag if completed faster than 25% of estimated duration."""
    if not activity.estimated_duration_minutes or not completion.started_at or not completion.completed_at:
        return None

    actual_minutes = (completion.completed_at - completion.started_at).total_seconds() / 60
    min_reasonable = activity.estimated_duration_minutes * 0.25

    if actual_minutes < min_reasonable:
        flag = AnomalyFlag(
            user_id=user_id,
            completion_id=completion.id,
            flag_type="speed_violation",
            severity="warning",
            description=(
                f"Completed '{activity.title}' in {actual_minutes:.0f} min "
                f"(estimated: {activity.estimated_duration_minutes} min, "
                f"minimum threshold: {min_reasonable:.0f} min)."
            ),
        )
        db.add(flag)
        return flag
    return None


async def _check_burst_violation(
    db: AsyncSession,
    user_id: UUID,
    max_daily: int = 5,
) -> AnomalyFlag | None:
    """Flag if user has more than max_daily completions today."""
    today_start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
    today_end = today_start + timedelta(days=1)

    result = await db.execute(
        select(func.count(ActivityCompletion.id)).where(
            ActivityCompletion.user_id == user_id,
            ActivityCompletion.status == "completed",
            ActivityCompletion.completed_at >= today_start,
            ActivityCompletion.completed_at < today_end,
        )
    )
    count = result.scalar() or 0

    if count > max_daily:
        flag = AnomalyFlag(
            user_id=user_id,
            flag_type="burst_violation",
            severity="warning",
            description=f"User has {count} completions today (threshold: {max_daily}).",
        )
        db.add(flag)
        return flag
    return None


async def _check_quiz_without_access(
    db: AsyncSession,
    user_id: UUID,
    activity: Activity,
) -> AnomalyFlag | None:
    """Flag if quiz passed but no content access record exists."""
    # Check if there's a tracked link for this activity
    link_result = await db.execute(
        select(TrackedLink).where(TrackedLink.activity_id == activity.id)
    )
    tracked_link = link_result.scalar_one_or_none()

    if not tracked_link:
        return None  # No tracked link = can't verify, skip

    # Check if user accessed it
    access_result = await db.execute(
        select(func.count(ContentAccessLog.id)).where(
            ContentAccessLog.tracked_link_id == tracked_link.id,
            ContentAccessLog.user_id == user_id,
        )
    )
    access_count = access_result.scalar() or 0

    if access_count == 0:
        flag = AnomalyFlag(
            user_id=user_id,
            flag_type="quiz_without_access",
            severity="critical",
            description=(
                f"Completed quiz for '{activity.title}' but never accessed "
                f"the tracked content link."
            ),
        )
        db.add(flag)
        return flag
    return None


async def get_user_anomaly_summary(db: AsyncSession, user_id: UUID) -> dict:
    """Get anomaly summary for a user profile view."""
    result = await db.execute(
        select(
            func.count(AnomalyFlag.id).filter(AnomalyFlag.resolved == False),  # noqa: E712
            func.count(AnomalyFlag.id).filter(AnomalyFlag.severity == "critical", AnomalyFlag.resolved == False),  # noqa: E712
        ).where(AnomalyFlag.user_id == user_id)
    )
    row = result.one()
    return {
        "unresolved_flags": row[0],
        "critical_flags": row[1],
    }
