from datetime import UTC, datetime, timedelta
from uuid import UUID
from fastapi import APIRouter, HTTPException
from fastapi.params import Depends
from sqlalchemy import select
from sqlalchemy.orm import Session as DbSession

from app.database import get_db
from app.schemas.session import Session
from app.schemas.auth_token import AccessToken
from app.schemas.user import PasswordUpdate, UserUpdate, User
from app.security.jwt_bearer import JWTBearer
from app.services import session_service, user_service
from app.util.errors import InvalidStateError, NotFoundError
from app.schemas.dashboard import DashboardResponse, DashboardStats
from app.models.transaction import Transaction as DbTransaction
from app.schemas.transaction import TransactionStatus

router = APIRouter(prefix="/me", tags=["Me (v1)"])


@router.get(path="", response_model=User)
async def get_myself(
    db: DbSession = Depends(get_db), token: AccessToken = Depends(JWTBearer())
):
    """
    Get the currently authenticated user.
    """
    user = await user_service.get_user(db=db, id=UUID(token.subject))
    if not user:
        raise HTTPException(status_code=404, detail="user_not_found")
    else:
        return user


@router.patch(path="", response_model=User)
async def update_myself(
    user_update: UserUpdate,
    db: DbSession = Depends(get_db),
    token: AccessToken = Depends(JWTBearer()),
):
    """
    Update the currently authenticated user. Changing the email address automatically marks it as not verified
    and starts a new verification workflow.
    """
    try:
        return await user_service.update_user(
            db, UUID(token.subject), user_update
        )
    except NotFoundError:
        raise HTTPException(status_code=404, detail="user_not_found")


@router.post(path="/password", response_model=list[None])
async def change_password(
    update: PasswordUpdate,
    db: DbSession = Depends(get_db),
    token: AccessToken = Depends(JWTBearer()),
):
    """
    Change the password of the currently authenticated user.
    """
    try:
        await user_service.change_user_password(
            db=db, id=UUID(token.subject), update=update
        )
        return list()
    except NotFoundError:
        raise HTTPException(status_code=404, detail="user_not_found")
    except InvalidStateError:
        raise HTTPException(status_code=409, detail="incorrect_password")


@router.get(path="/sessions", response_model=list[Session])
async def get_user_sessions(
    db: DbSession = Depends(get_db), token: AccessToken = Depends(JWTBearer())
):
    """
    List the active sessions of the currently authenticated user.
    """
    return await session_service.get_sessions_by_user(
        db=db, user_id=UUID(token.subject)
    )


@router.delete(path="/sessions", response_model=list[None])
async def clear_user_sessions(
    db: DbSession = Depends(get_db), token: AccessToken = Depends(JWTBearer())
):
    """
    Clear all sessions of the currently authenticated user.
    """
    await session_service.remove_all_sessions_for_user(
        db=db, user_id=UUID(token.subject),
    )
    return list()


@router.delete(path="/sessions/{session_id}", response_model=list[None])
async def delete_user_session(
    session_id: UUID,
    db: DbSession = Depends(get_db),
    token: AccessToken = Depends(JWTBearer()),
):
    """
    Invalidate a specific session of the currently authenticated user.
    """
    try:
        await session_service.remove_session_for_user(
            db=db,
            id=session_id,
            user_id=UUID(token.subject),
        )
    except NotFoundError:
        raise HTTPException(status_code=404, detail="session_not_found")
    return list()

@router.get(path="/dashboard", response_model=DashboardResponse)
async def get_dashboard(
    db: DbSession = Depends(get_db),
    token: AccessToken = Depends(JWTBearer()),
):
    """
    Get dashboard information for the currently authenticated user.
    """
    # Currently ongoing transaction
    stmt_current_transaction = select(DbTransaction).where(DbTransaction.user_id == token.subject).where(DbTransaction.status == TransactionStatus.ONGOING)
    current_transaction = db.execute(stmt_current_transaction).scalars().first()

    # Common base query
    stmt_base = select(DbTransaction).where(DbTransaction.user_id == token.subject).where( DbTransaction.status == TransactionStatus.ENDED).order_by(DbTransaction.ended_at.desc())

    # 5 most recent transactions
    stmt_transactions_recent = stmt_base.limit(5)
    recent_transactions = db.execute(stmt_transactions_recent).scalars().all()

    # Calculate beginning of the current and previous month
    current_date = datetime.now(UTC).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None)

    beginning_current_month = current_date.replace(day=1)

    beginning_previous_month = beginning_current_month.replace(
        year=(beginning_current_month.year - 1 if beginning_current_month.month == 1 else beginning_current_month.year),
        month=(12 if beginning_current_month.month == 1 else beginning_current_month.month - 1)
    )

    # Transactions for total calculations
    stmt_transactions = stmt_base.where(DbTransaction.ended_at >= beginning_previous_month)
    transactions = db.execute(stmt_transactions).scalars().all()

    # Current month totals
    count = 0
    energy_total = 0
    cost_total = 0

    # Previous month totals
    count_previous = 0
    energy_total_previous = 0
    cost_total_previous = 0

    # Calculate totals
    for trans in transactions:
        trans_energy = trans.meter_end - trans.meter_start
        trans_cost = trans_energy * trans.price
        if trans.ended_at >= beginning_current_month:
            # Current month
            energy_total += trans_energy
            cost_total += trans_cost
            count += 1
        else:
            # Previous month
            energy_total_previous += trans_energy
            cost_total_previous += trans_cost
            count_previous += 1
    
    return DashboardResponse(
        stats=DashboardStats(
            transaction_count=count,
            transaction_count_previous=count_previous,
            transaction_energy_total=energy_total,
            transaction_energy_total_previous=energy_total_previous,
            transaction_cost_total=cost_total,
            transaction_cost_total_previous=cost_total_previous
        ),
        current_transaction=current_transaction,
        recent_transactions=recent_transactions
    )