from datetime import date
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import 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.hackathon import Hackathon, HackathonTeam, HackathonTeamMember
from app.models.user import User

router = APIRouter(prefix="/hackathons", tags=["Hackathons"])


# --- Request models ---

class CreateHackathonRequest(BaseModel):
    name: str
    description: str | None = None
    event_date: date
    duration_hours: int | None = None
    location: str | None = None
    max_participants: int | None = None
    registration_deadline: date | None = None
    activity_id: str | None = None


class RegisterHackathonRequest(BaseModel):
    team_name: str | None = None
    role: str = "member"


# --- Routes ---

@router.get("")
async def list_hackathons(
    status: str | None = Query(None, description="Filter by status: upcoming, active, completed, cancelled"),
    db: AsyncSession = Depends(get_db),
    _=Depends(get_current_user),
):
    """List all hackathons, optionally filtered by status."""
    query = select(Hackathon).options(selectinload(Hackathon.teams)).order_by(Hackathon.event_date.desc())
    if status:
        query = query.where(Hackathon.status == status)

    result = await db.execute(query)
    hackathons = result.scalars().unique().all()

    return [
        {
            "id": str(h.id),
            "name": h.name,
            "description": h.description,
            "event_date": h.event_date.isoformat(),
            "duration_hours": h.duration_hours,
            "location": h.location,
            "max_participants": h.max_participants,
            "registration_deadline": h.registration_deadline.isoformat() if h.registration_deadline else None,
            "status": h.status,
            "team_count": len(h.teams),
            "created_at": h.created_at.isoformat(),
        }
        for h in hackathons
    ]


@router.get("/{hackathon_id}")
async def get_hackathon(
    hackathon_id: str,
    db: AsyncSession = Depends(get_db),
    _=Depends(get_current_user),
):
    """Get a single hackathon with its teams and members."""
    result = await db.execute(
        select(Hackathon)
        .where(Hackathon.id == UUID(hackathon_id))
        .options(
            selectinload(Hackathon.teams).selectinload(HackathonTeam.members),
        )
    )
    hackathon = result.scalars().unique().one_or_none()
    if not hackathon:
        raise HTTPException(status_code=404, detail="Hackathon not found")

    teams = []
    for team in hackathon.teams:
        members = []
        for member in team.members:
            user = await db.get(User, member.user_id)
            members.append({
                "id": str(member.id),
                "user": {
                    "id": str(user.id),
                    "full_name": user.full_name,
                    "email": user.email,
                } if user else None,
                "role": member.role,
            })
        teams.append({
            "id": str(team.id),
            "name": team.name,
            "project_title": team.project_title,
            "project_description": team.project_description,
            "placement": team.placement,
            "members": members,
            "created_at": team.created_at.isoformat(),
        })

    return {
        "id": str(hackathon.id),
        "name": hackathon.name,
        "description": hackathon.description,
        "event_date": hackathon.event_date.isoformat(),
        "duration_hours": hackathon.duration_hours,
        "location": hackathon.location,
        "max_participants": hackathon.max_participants,
        "registration_deadline": hackathon.registration_deadline.isoformat() if hackathon.registration_deadline else None,
        "status": hackathon.status,
        "activity_id": str(hackathon.activity_id) if hackathon.activity_id else None,
        "teams": teams,
        "created_at": hackathon.created_at.isoformat(),
        "updated_at": hackathon.updated_at.isoformat(),
    }


@router.post("")
async def create_hackathon(
    req: CreateHackathonRequest,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
):
    """Create a new hackathon (admin only)."""
    hackathon = Hackathon(
        name=req.name,
        description=req.description,
        event_date=req.event_date,
        duration_hours=req.duration_hours,
        location=req.location,
        max_participants=req.max_participants,
        registration_deadline=req.registration_deadline,
        activity_id=UUID(req.activity_id) if req.activity_id else None,
    )
    db.add(hackathon)
    await db.flush()

    return {
        "id": str(hackathon.id),
        "name": hackathon.name,
        "event_date": hackathon.event_date.isoformat(),
        "status": hackathon.status,
        "created_at": hackathon.created_at.isoformat(),
    }


@router.post("/{hackathon_id}/register")
async def register_for_hackathon(
    hackathon_id: str,
    req: RegisterHackathonRequest | None = None,
    db: AsyncSession = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """Register the current user for a hackathon. Creates or joins a team."""
    hackathon = await db.get(Hackathon, UUID(hackathon_id))
    if not hackathon:
        raise HTTPException(status_code=404, detail="Hackathon not found")

    if hackathon.status not in ("upcoming", "active"):
        raise HTTPException(status_code=400, detail="Registration is closed for this hackathon")

    # Check if user is already registered in any team for this hackathon
    existing = await db.execute(
        select(HackathonTeamMember)
        .join(HackathonTeam, HackathonTeamMember.team_id == HackathonTeam.id)
        .where(
            HackathonTeam.hackathon_id == UUID(hackathon_id),
            HackathonTeamMember.user_id == user.id,
        )
    )
    if existing.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="Already registered for this hackathon")

    # Check max participants
    if hackathon.max_participants:
        member_count_result = await db.execute(
            select(HackathonTeamMember)
            .join(HackathonTeam, HackathonTeamMember.team_id == HackathonTeam.id)
            .where(HackathonTeam.hackathon_id == UUID(hackathon_id))
        )
        current_count = len(member_count_result.scalars().all())
        if current_count >= hackathon.max_participants:
            raise HTTPException(status_code=400, detail="Hackathon has reached maximum participants")

    body = req or RegisterHackathonRequest()
    team_name = body.team_name or f"{user.full_name}'s Team"

    # Create a new team for the user (or they could join existing — simplified here)
    team = HackathonTeam(
        hackathon_id=UUID(hackathon_id),
        name=team_name,
    )
    db.add(team)
    await db.flush()

    member = HackathonTeamMember(
        team_id=team.id,
        user_id=user.id,
        role=body.role,
    )
    db.add(member)
    await db.flush()

    return {
        "message": "Successfully registered",
        "team": {
            "id": str(team.id),
            "name": team.name,
        },
        "member": {
            "id": str(member.id),
            "role": member.role,
        },
    }
