import logging

from fastapi import APIRouter
from pydantic import BaseModel
from sqlalchemy import select, func
from sqlalchemy.orm import selectinload

from app.api.deps import DbSession, CurrentUser, BotSecretDep
from app.core.config import settings
from app.core.security import create_telegram_link_token, verify_telegram_link_token
from app.models import User, Participant, Marathon, Assignment, Challenge, Event, Game

logger = logging.getLogger(__name__)

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


# Schemas
class TelegramLinkToken(BaseModel):
    token: str
    bot_url: str


class TelegramConfirmLink(BaseModel):
    token: str
    telegram_id: int
    telegram_username: str | None = None
    telegram_first_name: str | None = None
    telegram_last_name: str | None = None
    telegram_avatar_url: str | None = None


class TelegramLinkResponse(BaseModel):
    success: bool
    nickname: str | None = None
    error: str | None = None


class TelegramUserResponse(BaseModel):
    id: int
    nickname: str
    login: str
    avatar_url: str | None = None

    class Config:
        from_attributes = True


class TelegramMarathonResponse(BaseModel):
    id: int
    title: str
    status: str
    total_points: int
    position: int

    class Config:
        from_attributes = True


class TelegramMarathonDetails(BaseModel):
    marathon: dict
    participant: dict
    position: int
    active_events: list[dict]
    current_assignment: dict | None


class TelegramStatsResponse(BaseModel):
    marathons_completed: int
    marathons_active: int
    challenges_completed: int
    total_points: int
    best_streak: int


# Endpoints
@router.post("/generate-link-token", response_model=TelegramLinkToken)
async def generate_link_token(current_user: CurrentUser):
    """Generate a one-time token for Telegram account linking."""
    logger.info(f"[TG_LINK] Generating link token for user {current_user.id} ({current_user.nickname})")

    # Create a short token (≤64 chars) for Telegram deep link
    token = create_telegram_link_token(
        user_id=current_user.id,
        expire_minutes=settings.TELEGRAM_LINK_TOKEN_EXPIRE_MINUTES
    )
    logger.info(f"[TG_LINK] Token created: {token} (length: {len(token)})")

    bot_username = settings.TELEGRAM_BOT_USERNAME or "BCMarathonbot"
    bot_url = f"https://t.me/{bot_username}?start={token}"
    logger.info(f"[TG_LINK] Bot URL: {bot_url}")

    return TelegramLinkToken(token=token, bot_url=bot_url)


@router.post("/confirm-link", response_model=TelegramLinkResponse)
async def confirm_telegram_link(data: TelegramConfirmLink, db: DbSession, _: BotSecretDep):
    """Confirm Telegram account linking (called by bot)."""
    logger.info(f"[TG_CONFIRM] ========== CONFIRM LINK REQUEST ==========")
    logger.info(f"[TG_CONFIRM] telegram_id: {data.telegram_id}")
    logger.info(f"[TG_CONFIRM] telegram_username: {data.telegram_username}")
    logger.info(f"[TG_CONFIRM] token: {data.token}")

    # Verify short token and extract user_id
    user_id = verify_telegram_link_token(data.token)
    logger.info(f"[TG_CONFIRM] Verified user_id: {user_id}")

    if user_id is None:
        logger.error(f"[TG_CONFIRM] FAILED: Token invalid or expired")
        return TelegramLinkResponse(success=False, error="Ссылка недействительна или устарела")

    # Get user
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    logger.info(f"[TG_CONFIRM] Found user: {user.nickname if user else 'NOT FOUND'}")

    if not user:
        logger.error(f"[TG_CONFIRM] FAILED: User not found")
        return TelegramLinkResponse(success=False, error="Пользователь не найден")

    # Check if telegram_id already linked to another user
    result = await db.execute(
        select(User).where(User.telegram_id == data.telegram_id, User.id != user_id)
    )
    existing_user = result.scalar_one_or_none()
    if existing_user:
        logger.error(f"[TG_CONFIRM] FAILED: Telegram already linked to user {existing_user.id}")
        return TelegramLinkResponse(
            success=False,
            error="Этот Telegram аккаунт уже привязан к другому пользователю"
        )

    # Link account
    logger.info(f"[TG_CONFIRM] Linking telegram_id={data.telegram_id} to user_id={user_id}")
    user.telegram_id = data.telegram_id
    user.telegram_username = data.telegram_username
    user.telegram_first_name = data.telegram_first_name
    user.telegram_last_name = data.telegram_last_name
    user.telegram_avatar_url = data.telegram_avatar_url

    await db.commit()
    logger.info(f"[TG_CONFIRM] SUCCESS! User {user.nickname} linked to Telegram {data.telegram_id}")

    return TelegramLinkResponse(success=True, nickname=user.nickname)


@router.get("/user/{telegram_id}", response_model=TelegramUserResponse | None)
async def get_user_by_telegram_id(telegram_id: int, db: DbSession, _: BotSecretDep):
    """Get user by Telegram ID."""
    logger.info(f"[TG_USER] Looking up user by telegram_id={telegram_id}")

    result = await db.execute(
        select(User).where(User.telegram_id == telegram_id)
    )
    user = result.scalar_one_or_none()

    if not user:
        logger.info(f"[TG_USER] No user found for telegram_id={telegram_id}")
        return None

    logger.info(f"[TG_USER] Found user: {user.id} ({user.nickname})")
    return TelegramUserResponse(
        id=user.id,
        nickname=user.nickname,
        login=user.login,
        avatar_url=user.avatar_url
    )


@router.post("/unlink/{telegram_id}", response_model=TelegramLinkResponse)
async def unlink_telegram(telegram_id: int, db: DbSession, _: BotSecretDep):
    """Unlink Telegram account."""
    result = await db.execute(
        select(User).where(User.telegram_id == telegram_id)
    )
    user = result.scalar_one_or_none()

    if not user:
        return TelegramLinkResponse(success=False, error="Аккаунт не найден")

    user.telegram_id = None
    user.telegram_username = None

    await db.commit()

    return TelegramLinkResponse(success=True)


@router.get("/marathons/{telegram_id}", response_model=list[TelegramMarathonResponse])
async def get_user_marathons(telegram_id: int, db: DbSession, _: BotSecretDep):
    """Get user's marathons by Telegram ID."""
    # Get user
    result = await db.execute(
        select(User).where(User.telegram_id == telegram_id)
    )
    user = result.scalar_one_or_none()

    if not user:
        return []

    # Get participations with marathons
    result = await db.execute(
        select(Participant, Marathon)
        .join(Marathon, Participant.marathon_id == Marathon.id)
        .where(Participant.user_id == user.id)
        .order_by(Marathon.created_at.desc())
    )
    participations = result.all()

    marathons = []
    for participant, marathon in participations:
        # Calculate position
        position_result = await db.execute(
            select(func.count(Participant.id) + 1)
            .where(
                Participant.marathon_id == marathon.id,
                Participant.total_points > participant.total_points
            )
        )
        position = position_result.scalar() or 1

        marathons.append(TelegramMarathonResponse(
            id=marathon.id,
            title=marathon.title,
            status=marathon.status.value if hasattr(marathon.status, 'value') else marathon.status,
            total_points=participant.total_points,
            position=position
        ))

    return marathons


@router.get("/marathon/{marathon_id}", response_model=TelegramMarathonDetails | None)
async def get_marathon_details(marathon_id: int, telegram_id: int, db: DbSession, _: BotSecretDep):
    """Get marathon details for user by Telegram ID."""
    # Get user
    result = await db.execute(
        select(User).where(User.telegram_id == telegram_id)
    )
    user = result.scalar_one_or_none()

    if not user:
        return None

    # Get marathon
    result = await db.execute(
        select(Marathon).where(Marathon.id == marathon_id)
    )
    marathon = result.scalar_one_or_none()

    if not marathon:
        return None

    # Get participant
    result = await db.execute(
        select(Participant)
        .where(Participant.marathon_id == marathon_id, Participant.user_id == user.id)
    )
    participant = result.scalar_one_or_none()

    if not participant:
        return None

    # Calculate position
    position_result = await db.execute(
        select(func.count(Participant.id) + 1)
        .where(
            Participant.marathon_id == marathon_id,
            Participant.total_points > participant.total_points
        )
    )
    position = position_result.scalar() or 1

    # Get active events
    result = await db.execute(
        select(Event)
        .where(Event.marathon_id == marathon_id, Event.is_active == True)
    )
    active_events = result.scalars().all()

    events_data = [
        {
            "id": e.id,
            "type": e.type.value if hasattr(e.type, 'value') else e.type,
            "start_time": e.start_time.isoformat() if e.start_time else None,
            "end_time": e.end_time.isoformat() if e.end_time else None
        }
        for e in active_events
    ]

    # Get current assignment
    result = await db.execute(
        select(Assignment)
        .options(
            selectinload(Assignment.challenge).selectinload(Challenge.game)
        )
        .where(
            Assignment.participant_id == participant.id,
            Assignment.status == "active"
        )
        .order_by(Assignment.started_at.desc())
        .limit(1)
    )
    assignment = result.scalar_one_or_none()

    assignment_data = None
    if assignment:
        challenge = assignment.challenge
        game = challenge.game if challenge else None
        assignment_data = {
            "id": assignment.id,
            "status": assignment.status.value if hasattr(assignment.status, 'value') else assignment.status,
            "challenge": {
                "id": challenge.id if challenge else None,
                "title": challenge.title if challenge else None,
                "difficulty": challenge.difficulty.value if challenge and hasattr(challenge.difficulty, 'value') else (challenge.difficulty if challenge else None),
                "points": challenge.points if challenge else None,
                "game": {
                    "id": game.id if game else None,
                    "title": game.title if game else None
                }
            } if challenge else None
        }

    return TelegramMarathonDetails(
        marathon={
            "id": marathon.id,
            "title": marathon.title,
            "status": marathon.status.value if hasattr(marathon.status, 'value') else marathon.status,
            "description": marathon.description
        },
        participant={
            "total_points": participant.total_points,
            "current_streak": participant.current_streak,
            "drop_count": participant.drop_count
        },
        position=position,
        active_events=events_data,
        current_assignment=assignment_data
    )


@router.get("/stats/{telegram_id}", response_model=TelegramStatsResponse | None)
async def get_user_stats(telegram_id: int, db: DbSession, _: BotSecretDep):
    """Get user's overall statistics by Telegram ID."""
    # Get user
    result = await db.execute(
        select(User).where(User.telegram_id == telegram_id)
    )
    user = result.scalar_one_or_none()

    if not user:
        return None

    # Get participations
    result = await db.execute(
        select(Participant, Marathon)
        .join(Marathon, Participant.marathon_id == Marathon.id)
        .where(Participant.user_id == user.id)
    )
    participations = result.all()

    marathons_completed = 0
    marathons_active = 0
    total_points = 0
    best_streak = 0

    for participant, marathon in participations:
        status = marathon.status.value if hasattr(marathon.status, 'value') else marathon.status
        if status == "finished":
            marathons_completed += 1
        elif status == "active":
            marathons_active += 1

        total_points += participant.total_points
        if participant.current_streak > best_streak:
            best_streak = participant.current_streak

    # Count completed assignments
    result = await db.execute(
        select(func.count(Assignment.id))
        .join(Participant, Assignment.participant_id == Participant.id)
        .where(Participant.user_id == user.id, Assignment.status == "completed")
    )
    challenges_completed = result.scalar() or 0

    return TelegramStatsResponse(
        marathons_completed=marathons_completed,
        marathons_active=marathons_active,
        challenges_completed=challenges_completed,
        total_points=total_points,
        best_streak=best_streak
    )
