"""
Activities + Completions API — CRUD and completion management for the DWA AI Adoption Tracker.
"""

from datetime import datetime, timezone
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.auth.demo_auth import get_admin_user, get_current_user
from app.database import get_db
from app.models.activity import Activity, ActivityCompletion
from app.models.user import User

# ===================================================================
# Activities Router
# ===================================================================

router = APIRouter(prefix="/activities", tags=["Activities"])

# ---------------------------------------------------------------------------
# Inline Pydantic schemas
# ---------------------------------------------------------------------------


class ActivityCreate(BaseModel):
    title: str
    description: str | None = None
    category: str  # course | reading | video | hackathon | workshop | tool_usage | certification | contribution | agent_creation
    platform: str | None = None
    external_url: str | None = None
    external_id: str | None = None
    xp_value: int = 0
    xp_open_pct: int = 100
    estimated_duration_minutes: int | None = None
    difficulty: str = "beginner"
    is_mandatory: bool = False
    is_recurring: bool = False
    recurrence_period: str | None = None
    trust_tier: int = 1
    thumbnail_url: str | None = None
    tags: list[str] | None = None
    quiz_id: UUID | None = None


class ActivityUpdate(BaseModel):
    title: str | None = None
    description: str | None = None
    category: str | None = None
    platform: str | None = None
    external_url: str | None = None
    external_id: str | None = None
    xp_value: int | None = None
    xp_open_pct: int | None = None
    estimated_duration_minutes: int | None = None
    difficulty: str | None = None
    is_mandatory: bool | None = None
    is_active: bool | None = None
    is_recurring: bool | None = None
    recurrence_period: str | None = None
    trust_tier: int | None = None
    thumbnail_url: str | None = None
    tags: list[str] | None = None
    quiz_id: UUID | None = None


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _activity_to_dict(activity: Activity) -> dict:
    return {
        "id": str(activity.id),
        "title": activity.title,
        "description": activity.description,
        "category": activity.category,
        "platform": activity.platform,
        "external_url": activity.external_url,
        "external_id": activity.external_id,
        "xp_value": activity.xp_value,
        "xp_open_pct": activity.xp_open_pct,
        "estimated_duration_minutes": activity.estimated_duration_minutes,
        "difficulty": activity.difficulty,
        "is_mandatory": activity.is_mandatory,
        "is_active": activity.is_active,
        "is_recurring": activity.is_recurring,
        "recurrence_period": activity.recurrence_period,
        "trust_tier": activity.trust_tier,
        "thumbnail_url": activity.thumbnail_url,
        "tags": activity.tags,
        "quiz_id": str(activity.quiz_id) if activity.quiz_id else None,
        "created_by": str(activity.created_by) if activity.created_by else None,
        "created_at": activity.created_at.isoformat() if activity.created_at else None,
        "updated_at": activity.updated_at.isoformat() if activity.updated_at else None,
    }


# ---------------------------------------------------------------------------
# GET /activities — list with filters
# ---------------------------------------------------------------------------


@router.get("")
async def list_activities(
    category: str | None = Query(None),
    platform: str | None = Query(None),
    difficulty: str | None = Query(None),
    is_active: bool | None = Query(None),
    search: str | None = Query(None, description="Search in title or description"),
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    db: AsyncSession = Depends(get_db),
    _=Depends(get_current_user),
):
    stmt = select(Activity)

    if category:
        stmt = stmt.where(Activity.category == category)
    if platform:
        stmt = stmt.where(Activity.platform == platform)
    if difficulty:
        stmt = stmt.where(Activity.difficulty == difficulty)
    if is_active is not None:
        stmt = stmt.where(Activity.is_active == is_active)
    if search:
        pattern = f"%{search}%"
        stmt = stmt.where(
            or_(
                Activity.title.ilike(pattern),
                Activity.description.ilike(pattern),
            )
        )

    count_stmt = select(func.count()).select_from(stmt.subquery())
    total = (await db.execute(count_stmt)).scalar() or 0

    stmt = stmt.order_by(Activity.created_at.desc()).offset(skip).limit(limit)
    result = await db.execute(stmt)
    activities = result.scalars().all()

    return {
        "items": [_activity_to_dict(a) for a in activities],
        "total": total,
        "skip": skip,
        "limit": limit,
    }


# ---------------------------------------------------------------------------
# GET /activities/{activity_id} — single activity with completion stats
# ---------------------------------------------------------------------------


@router.get("/{activity_id}")
async def get_activity(
    activity_id: UUID,
    db: AsyncSession = Depends(get_db),
    _=Depends(get_current_user),
):
    result = await db.execute(select(Activity).where(Activity.id == activity_id))
    activity = result.scalar_one_or_none()
    if not activity:
        raise HTTPException(status_code=404, detail="Activity not found")

    # Completion stats
    total_completions = (
        await db.execute(
            select(func.count(ActivityCompletion.id)).where(
                ActivityCompletion.activity_id == activity_id,
                ActivityCompletion.status.in_(["completed", "verified"]),
            )
        )
    ).scalar() or 0

    in_progress = (
        await db.execute(
            select(func.count(ActivityCompletion.id)).where(
                ActivityCompletion.activity_id == activity_id,
                ActivityCompletion.status == "in_progress",
            )
        )
    ).scalar() or 0

    avg_xp = (
        await db.execute(
            select(func.coalesce(func.avg(ActivityCompletion.xp_awarded), 0)).where(
                ActivityCompletion.activity_id == activity_id,
                ActivityCompletion.status.in_(["completed", "verified"]),
            )
        )
    ).scalar() or 0

    data = _activity_to_dict(activity)
    data["stats"] = {
        "total_completions": total_completions,
        "in_progress": in_progress,
        "avg_xp_awarded": round(float(avg_xp)),
    }
    return data


# ---------------------------------------------------------------------------
# POST /activities — create activity (admin only)
# ---------------------------------------------------------------------------


@router.post("", status_code=201)
async def create_activity(
    body: ActivityCreate,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
):
    activity = Activity(
        title=body.title,
        description=body.description,
        category=body.category,
        platform=body.platform,
        external_url=body.external_url,
        external_id=body.external_id,
        xp_value=body.xp_value,
        xp_open_pct=body.xp_open_pct,
        estimated_duration_minutes=body.estimated_duration_minutes,
        difficulty=body.difficulty,
        is_mandatory=body.is_mandatory,
        is_recurring=body.is_recurring,
        recurrence_period=body.recurrence_period,
        trust_tier=body.trust_tier,
        thumbnail_url=body.thumbnail_url,
        tags=body.tags,
        quiz_id=body.quiz_id,
        created_by=admin.id,
    )
    db.add(activity)
    await db.flush()

    return _activity_to_dict(activity)


# ---------------------------------------------------------------------------
# PUT /activities/{activity_id} — update activity (admin only)
# ---------------------------------------------------------------------------


@router.put("/{activity_id}")
async def update_activity(
    activity_id: UUID,
    body: ActivityUpdate,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
):
    result = await db.execute(select(Activity).where(Activity.id == activity_id))
    activity = result.scalar_one_or_none()
    if not activity:
        raise HTTPException(status_code=404, detail="Activity not found")

    update_data = body.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(activity, field, value)

    await db.flush()
    return _activity_to_dict(activity)


# ---------------------------------------------------------------------------
# DELETE /activities/{activity_id} — soft delete (admin only)
# ---------------------------------------------------------------------------


@router.delete("/{activity_id}", status_code=204)
async def delete_activity(
    activity_id: UUID,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
):
    result = await db.execute(select(Activity).where(Activity.id == activity_id))
    activity = result.scalar_one_or_none()
    if not activity:
        raise HTTPException(status_code=404, detail="Activity not found")

    activity.is_active = False
    await db.flush()
    return None


# ===================================================================
# Completions Router
# ===================================================================

completions_router = APIRouter(prefix="/completions", tags=["Completions"])

# ---------------------------------------------------------------------------
# Inline Pydantic schemas
# ---------------------------------------------------------------------------


class CompletionCreate(BaseModel):
    user_id: UUID | None = None  # Defaults to current user if not provided
    activity_id: UUID
    status: str = "completed"
    progress_pct: int = 100
    evidence_url: str | None = None
    source: str = "manual"
    source_detail: str | None = None
    trust_tier: int = 3
    period_key: str | None = None


class CompletionUpdate(BaseModel):
    status: str | None = None
    progress_pct: int | None = None
    evidence_url: str | None = None
    verification_note: str | None = None
    xp_awarded: int | None = None


class CompletionVerify(BaseModel):
    action: str  # approve | reject
    verification_note: str | None = None


class BulkCompletionItem(BaseModel):
    user_id: UUID
    activity_id: UUID
    status: str = "completed"
    progress_pct: int = 100
    source: str = "excel_import"
    source_detail: str | None = None
    trust_tier: int = 2
    period_key: str | None = None
    evidence_url: str | None = None


class BulkCompletionCreate(BaseModel):
    items: list[BulkCompletionItem]


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _completion_to_dict(c: ActivityCompletion, *, activity: Activity | None = None, user: User | None = None) -> dict:
    data = {
        "id": str(c.id),
        "user_id": str(c.user_id),
        "activity_id": str(c.activity_id),
        "status": c.status,
        "progress_pct": c.progress_pct,
        "xp_awarded": c.xp_awarded,
        "trust_tier": c.trust_tier,
        "source": c.source,
        "source_detail": c.source_detail,
        "evidence_url": c.evidence_url,
        "period_key": c.period_key,
        "verified_by": str(c.verified_by) if c.verified_by else None,
        "verification_note": c.verification_note,
        "flagged_reason": c.flagged_reason,
        "started_at": c.started_at.isoformat() if c.started_at else None,
        "completed_at": c.completed_at.isoformat() if c.completed_at else None,
        "created_at": c.created_at.isoformat() if c.created_at else None,
        "updated_at": c.updated_at.isoformat() if c.updated_at else None,
    }
    act = activity or getattr(c, "activity", None)
    if act:
        data["activity_title"] = act.title
        data["activity_category"] = act.category
        data["activity_platform"] = act.platform
    usr = user or getattr(c, "user", None)
    if usr:
        data["user_name"] = usr.full_name
        data["user_email"] = usr.email
    return data


# ---------------------------------------------------------------------------
# POST /completions — create completion + run XP engine
# ---------------------------------------------------------------------------


@completions_router.post("", status_code=201)
async def create_completion(
    body: CompletionCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    user_id = body.user_id or current_user.id

    # Non-admins can only create completions for themselves
    if user_id != current_user.id and current_user.role not in ("admin", "team_lead"):
        raise HTTPException(status_code=403, detail="Cannot create completions for other users")

    # Validate activity
    activity = (await db.execute(select(Activity).where(Activity.id == body.activity_id))).scalar_one_or_none()
    if not activity:
        raise HTTPException(status_code=404, detail="Activity not found")
    if not activity.is_active:
        raise HTTPException(status_code=400, detail="Activity is inactive")

    # Check for duplicate (same user + activity + period_key)
    dup_stmt = select(ActivityCompletion).where(
        ActivityCompletion.user_id == user_id,
        ActivityCompletion.activity_id == body.activity_id,
    )
    if body.period_key:
        dup_stmt = dup_stmt.where(ActivityCompletion.period_key == body.period_key)
    else:
        dup_stmt = dup_stmt.where(ActivityCompletion.period_key.is_(None))

    existing = (await db.execute(dup_stmt)).scalar_one_or_none()
    if existing:
        raise HTTPException(status_code=409, detail="Completion already exists for this activity/period")

    now = datetime.now(timezone.utc)
    completion = ActivityCompletion(
        user_id=user_id,
        activity_id=body.activity_id,
        status=body.status,
        progress_pct=body.progress_pct,
        source=body.source,
        source_detail=body.source_detail,
        trust_tier=body.trust_tier,
        period_key=body.period_key,
        evidence_url=body.evidence_url,
        started_at=now,
        completed_at=now if body.status == "completed" else None,
    )
    db.add(completion)
    await db.flush()

    # Run XP engine (may not exist yet — graceful fallback)
    events = []
    try:
        from app.services.xp_engine import process_completion
        events = await process_completion(
            db, user_id, body.activity_id, completion.id, body.source, body.trust_tier
        )
    except ImportError:
        # XP engine not built yet — award simple XP as fallback
        if body.trust_tier in (1, 2) and body.status == "completed":
            completion.xp_awarded = activity.xp_value
            user = await db.get(User, user_id)
            if user:
                user.total_xp = (user.total_xp or 0) + activity.xp_value
                if body.trust_tier in (1, 2):
                    user.verified_xp = (user.verified_xp or 0) + activity.xp_value
        elif body.trust_tier == 3:
            # Self-reported: 0 XP until approved
            completion.xp_awarded = 0
        await db.flush()

    await db.refresh(completion)
    data = _completion_to_dict(completion, activity=activity)
    data["events"] = events
    return data


# ---------------------------------------------------------------------------
# PUT /completions/{completion_id} — update completion
# ---------------------------------------------------------------------------


@completions_router.put("/{completion_id}")
async def update_completion(
    completion_id: UUID,
    body: CompletionUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    result = await db.execute(
        select(ActivityCompletion)
        .options(selectinload(ActivityCompletion.activity))
        .where(ActivityCompletion.id == completion_id)
    )
    completion = result.scalar_one_or_none()
    if not completion:
        raise HTTPException(status_code=404, detail="Completion not found")

    # Non-admins can only update their own completions
    if completion.user_id != current_user.id and current_user.role not in ("admin", "team_lead"):
        raise HTTPException(status_code=403, detail="Cannot update other users' completions")

    update_data = body.model_dump(exclude_unset=True)

    # If status is changing to completed, set completed_at
    if "status" in update_data and update_data["status"] == "completed" and not completion.completed_at:
        completion.completed_at = datetime.now(timezone.utc)

    for field, value in update_data.items():
        setattr(completion, field, value)

    await db.flush()
    await db.refresh(completion)
    return _completion_to_dict(completion)


# ---------------------------------------------------------------------------
# POST /completions/verify/{completion_id} — admin verify (approve/reject)
# ---------------------------------------------------------------------------


@completions_router.post("/verify/{completion_id}")
async def verify_completion(
    completion_id: UUID,
    body: CompletionVerify,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
):
    result = await db.execute(
        select(ActivityCompletion)
        .options(selectinload(ActivityCompletion.activity))
        .where(ActivityCompletion.id == completion_id)
    )
    completion = result.scalar_one_or_none()
    if not completion:
        raise HTTPException(status_code=404, detail="Completion not found")

    if body.action == "approve":
        completion.status = "verified"
        completion.verified_by = admin.id
        completion.verification_note = body.verification_note

        # Award XP if not already awarded (Tier 3 self-reports start at 0)
        if completion.xp_awarded == 0 and completion.activity:
            completion.xp_awarded = completion.activity.xp_value
            user = await db.get(User, completion.user_id)
            if user:
                user.total_xp = (user.total_xp or 0) + completion.activity.xp_value
                # Verified by admin, so counts as verified XP
                user.verified_xp = (user.verified_xp or 0) + completion.activity.xp_value

        # Run XP engine for badge checks, level-up, etc.
        try:
            from app.services.xp_engine import process_completion
            await process_completion(
                db,
                completion.user_id,
                completion.activity_id,
                completion.id,
                completion.source,
                2,  # Admin-verified = Tier 2 trust
            )
        except ImportError:
            pass

    elif body.action == "reject":
        completion.status = "flagged"
        completion.verified_by = admin.id
        completion.verification_note = body.verification_note
        completion.flagged_reason = body.verification_note or "Rejected by admin"

        # Revoke any XP that was previously awarded
        if completion.xp_awarded > 0:
            user = await db.get(User, completion.user_id)
            if user:
                user.total_xp = max(0, (user.total_xp or 0) - completion.xp_awarded)
                user.verified_xp = max(0, (user.verified_xp or 0) - completion.xp_awarded)
            completion.xp_awarded = 0
    else:
        raise HTTPException(status_code=400, detail="action must be 'approve' or 'reject'")

    await db.flush()
    await db.refresh(completion)
    return _completion_to_dict(completion)


# ---------------------------------------------------------------------------
# POST /completions/bulk — bulk create completions (admin)
# ---------------------------------------------------------------------------


@completions_router.post("/bulk", status_code=201)
async def bulk_create_completions(
    body: BulkCompletionCreate,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
):
    results = {"created": 0, "skipped": 0, "errors": [], "items": []}

    for idx, item in enumerate(body.items):
        # Validate user exists
        user = (await db.execute(select(User).where(User.id == item.user_id))).scalar_one_or_none()
        if not user:
            results["errors"].append({"index": idx, "error": f"User {item.user_id} not found"})
            results["skipped"] += 1
            continue

        # Validate activity exists
        activity = (await db.execute(select(Activity).where(Activity.id == item.activity_id))).scalar_one_or_none()
        if not activity:
            results["errors"].append({"index": idx, "error": f"Activity {item.activity_id} not found"})
            results["skipped"] += 1
            continue

        # Check duplicate
        dup_stmt = select(ActivityCompletion).where(
            ActivityCompletion.user_id == item.user_id,
            ActivityCompletion.activity_id == item.activity_id,
        )
        if item.period_key:
            dup_stmt = dup_stmt.where(ActivityCompletion.period_key == item.period_key)
        else:
            dup_stmt = dup_stmt.where(ActivityCompletion.period_key.is_(None))

        existing = (await db.execute(dup_stmt)).scalar_one_or_none()
        if existing:
            results["errors"].append({"index": idx, "error": "Duplicate completion"})
            results["skipped"] += 1
            continue

        now = datetime.now(timezone.utc)
        completion = ActivityCompletion(
            user_id=item.user_id,
            activity_id=item.activity_id,
            status=item.status,
            progress_pct=item.progress_pct,
            source=item.source,
            source_detail=item.source_detail,
            trust_tier=item.trust_tier,
            period_key=item.period_key,
            evidence_url=item.evidence_url,
            started_at=now,
            completed_at=now if item.status == "completed" else None,
        )
        db.add(completion)
        await db.flush()

        # Run XP engine
        try:
            from app.services.xp_engine import process_completion
            await process_completion(
                db, item.user_id, item.activity_id, completion.id, item.source, item.trust_tier
            )
        except ImportError:
            # Fallback XP
            if item.trust_tier in (1, 2) and item.status == "completed":
                completion.xp_awarded = activity.xp_value
                user.total_xp = (user.total_xp or 0) + activity.xp_value
                if item.trust_tier in (1, 2):
                    user.verified_xp = (user.verified_xp or 0) + activity.xp_value
            await db.flush()

        results["created"] += 1
        results["items"].append(_completion_to_dict(completion, activity=activity))

    return results
