from fastapi import APIRouter, Depends, HTTPException, status, Request, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from sqlalchemy.orm import selectinload
from typing import Optional
from datetime import datetime
from decimal import Decimal

from app.database import get_db
from app.models import (
    User, UserStatus, SportEvent, SpreadBet, AdminSettings, EventStatus,
    Wallet, Transaction, TransactionType, TransactionStatus, AdminAuditLog, Bet
)
from app.schemas.sport_event import SportEventCreate, SportEventUpdate, SportEvent as SportEventSchema
from app.schemas.admin import (
    AuditLogResponse, AuditLogListResponse,
    WipePreviewResponse, WipeRequest, WipeResponse,
    SeedRequest, SeedResponse,
    SimulationConfig, SimulationStatusResponse, SimulationStartRequest, SimulationStartResponse, SimulationStopResponse,
    AdminUserListItem, AdminUserListResponse, AdminUserDetailResponse,
    AdminUserUpdateRequest, AdminUserStatusRequest,
    AdminBalanceAdjustRequest, AdminBalanceAdjustResponse,
    AdminDashboardStats,
)
from app.routers.auth import get_current_user
from app.services.audit_service import (
    AuditService, log_event_create, log_event_update, log_event_delete,
    log_user_status_change, log_user_balance_adjust, log_user_admin_change,
    log_user_update, log_settings_update, log_simulation_start, log_simulation_stop
)
from app.services.wiper_service import WiperService
from app.services.seeder_service import SeederService
from app.services.simulation_service import simulation_manager

router = APIRouter(prefix="/api/v1/admin", tags=["admin"])


# ============================================================
# Helper to get client IP
# ============================================================
def get_client_ip(request: Request) -> Optional[str]:
    """Extract client IP from request."""
    forwarded = request.headers.get("X-Forwarded-For")
    if forwarded:
        return forwarded.split(",")[0].strip()
    return request.client.host if request.client else None


# ============================================================
# Admin Dependency
# ============================================================
async def get_admin_user(current_user: User = Depends(get_current_user)) -> User:
    if not current_user.is_admin:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Admin access required"
        )
    return current_user


# ============================================================
# Dashboard Stats
# ============================================================
@router.get("/dashboard", response_model=AdminDashboardStats)
async def get_dashboard_stats(
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user)
):
    """Get dashboard statistics for admin panel."""
    # User counts
    total_users = (await db.execute(select(func.count(User.id)))).scalar() or 0
    active_users = (await db.execute(
        select(func.count(User.id)).where(User.status == UserStatus.ACTIVE)
    )).scalar() or 0
    suspended_users = (await db.execute(
        select(func.count(User.id)).where(User.status == UserStatus.SUSPENDED)
    )).scalar() or 0
    admin_users = (await db.execute(
        select(func.count(User.id)).where(User.is_admin == True)
    )).scalar() or 0

    # Event counts
    total_events = (await db.execute(select(func.count(SportEvent.id)))).scalar() or 0
    upcoming_events = (await db.execute(
        select(func.count(SportEvent.id)).where(SportEvent.status == EventStatus.UPCOMING)
    )).scalar() or 0
    live_events = (await db.execute(
        select(func.count(SportEvent.id)).where(SportEvent.status == EventStatus.LIVE)
    )).scalar() or 0

    # Bet counts
    total_bets = (await db.execute(select(func.count(SpreadBet.id)))).scalar() or 0
    open_bets = (await db.execute(
        select(func.count(SpreadBet.id)).where(SpreadBet.status == "open")
    )).scalar() or 0
    matched_bets = (await db.execute(
        select(func.count(SpreadBet.id)).where(SpreadBet.status == "matched")
    )).scalar() or 0

    # Volume calculations
    total_volume_result = await db.execute(
        select(func.sum(SpreadBet.stake_amount))
    )
    total_volume = total_volume_result.scalar() or Decimal("0.00")

    escrow_result = await db.execute(select(func.sum(Wallet.escrow)))
    escrow_locked = escrow_result.scalar() or Decimal("0.00")

    return AdminDashboardStats(
        total_users=total_users,
        active_users=active_users,
        suspended_users=suspended_users,
        admin_users=admin_users,
        total_events=total_events,
        upcoming_events=upcoming_events,
        live_events=live_events,
        total_bets=total_bets,
        open_bets=open_bets,
        matched_bets=matched_bets,
        total_volume=total_volume,
        escrow_locked=escrow_locked,
        simulation_running=simulation_manager.is_running,
    )


# ============================================================
# Audit Logs
# ============================================================
@router.get("/audit-logs", response_model=AuditLogListResponse)
async def get_audit_logs(
    page: int = Query(1, ge=1),
    page_size: int = Query(50, ge=1, le=100),
    action: Optional[str] = None,
    admin_id: Optional[int] = None,
    target_type: Optional[str] = None,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user)
):
    """Get paginated audit logs with optional filters."""
    logs, total = await AuditService.get_logs(
        db=db,
        page=page,
        page_size=page_size,
        action_filter=action,
        admin_id_filter=admin_id,
        target_type_filter=target_type,
    )
    return AuditLogListResponse(
        logs=[AuditLogResponse.model_validate(log) for log in logs],
        total=total,
        page=page,
        page_size=page_size,
    )


# ============================================================
# Data Wiper
# ============================================================
@router.get("/data/wipe/preview", response_model=WipePreviewResponse)
async def preview_wipe(
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user)
):
    """Preview what will be deleted in a wipe operation."""
    return await WiperService.get_preview(db)


@router.post("/data/wipe", response_model=WipeResponse)
async def execute_wipe(
    request: WipeRequest,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    """Execute a database wipe. Requires confirmation phrase."""
    try:
        ip = get_client_ip(req) if req else None
        return await WiperService.execute_wipe(db, admin, request, ip)
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))


# ============================================================
# Data Seeder
# ============================================================
@router.post("/data/seed", response_model=SeedResponse)
async def seed_database(
    request: SeedRequest,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    """Seed the database with test data."""
    ip = get_client_ip(req) if req else None
    return await SeederService.seed(db, admin, request, ip)


# ============================================================
# Simulation Control
# ============================================================
@router.get("/simulation/status", response_model=SimulationStatusResponse)
async def get_simulation_status(
    admin: User = Depends(get_admin_user)
):
    """Get current simulation status."""
    return simulation_manager.get_status()


@router.post("/simulation/start", response_model=SimulationStartResponse)
async def start_simulation(
    request: SimulationStartRequest = None,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    """Start the activity simulation."""
    config = request.config if request else None

    if simulation_manager.is_running:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Simulation is already running"
        )

    success = await simulation_manager.start(admin.username, config)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to start simulation"
        )

    # Log the action
    ip = get_client_ip(req) if req else None
    await log_simulation_start(
        db=db,
        admin=admin,
        config=config.model_dump() if config else {},
        ip_address=ip,
    )
    await db.commit()

    return SimulationStartResponse(
        success=True,
        message="Simulation started successfully",
        status=simulation_manager.get_status(),
    )


@router.post("/simulation/stop", response_model=SimulationStopResponse)
async def stop_simulation(
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    """Stop the activity simulation."""
    if not simulation_manager.is_running:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Simulation is not running"
        )

    iterations, duration = await simulation_manager.stop()

    # Log the action
    ip = get_client_ip(req) if req else None
    await log_simulation_stop(
        db=db,
        admin=admin,
        iterations=iterations,
        duration_seconds=duration,
        ip_address=ip,
    )
    await db.commit()

    return SimulationStopResponse(
        success=True,
        message="Simulation stopped successfully",
        total_iterations=iterations,
        ran_for_seconds=duration,
    )


# ============================================================
# User Management
# ============================================================
@router.get("/users", response_model=AdminUserListResponse)
async def list_users(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    search: Optional[str] = None,
    status_filter: Optional[str] = None,
    is_admin: Optional[bool] = None,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user)
):
    """Get paginated list of users."""
    query = select(User).options(selectinload(User.wallet))

    # Apply filters
    if search:
        search_term = f"%{search}%"
        query = query.where(
            (User.username.ilike(search_term)) |
            (User.email.ilike(search_term)) |
            (User.display_name.ilike(search_term))
        )
    if status_filter:
        query = query.where(User.status == UserStatus(status_filter))
    if is_admin is not None:
        query = query.where(User.is_admin == is_admin)

    # Get total count
    count_query = select(func.count()).select_from(query.subquery())
    total = (await db.execute(count_query)).scalar() or 0

    # Apply pagination
    query = query.order_by(User.created_at.desc())
    query = query.offset((page - 1) * page_size).limit(page_size)

    result = await db.execute(query)
    users = result.scalars().all()

    user_items = []
    for user in users:
        wallet = user.wallet
        user_items.append(AdminUserListItem(
            id=user.id,
            email=user.email,
            username=user.username,
            display_name=user.display_name,
            is_admin=user.is_admin,
            status=user.status.value,
            balance=wallet.balance if wallet else Decimal("0.00"),
            escrow=wallet.escrow if wallet else Decimal("0.00"),
            total_bets=user.total_bets,
            wins=user.wins,
            losses=user.losses,
            win_rate=user.win_rate,
            created_at=user.created_at,
        ))

    return AdminUserListResponse(
        users=user_items,
        total=total,
        page=page,
        page_size=page_size,
    )


@router.get("/users/{user_id}", response_model=AdminUserDetailResponse)
async def get_user_detail(
    user_id: int,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user)
):
    """Get detailed user information."""
    result = await db.execute(
        select(User).options(selectinload(User.wallet)).where(User.id == user_id)
    )
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    wallet = user.wallet

    # Get additional counts
    open_bets = (await db.execute(
        select(func.count(SpreadBet.id)).where(
            (SpreadBet.creator_id == user_id) & (SpreadBet.status == "open")
        )
    )).scalar() or 0

    matched_bets = (await db.execute(
        select(func.count(SpreadBet.id)).where(
            ((SpreadBet.creator_id == user_id) | (SpreadBet.taker_id == user_id)) &
            (SpreadBet.status == "matched")
        )
    )).scalar() or 0

    transaction_count = (await db.execute(
        select(func.count(Transaction.id)).where(Transaction.user_id == user_id)
    )).scalar() or 0

    return AdminUserDetailResponse(
        id=user.id,
        email=user.email,
        username=user.username,
        display_name=user.display_name,
        avatar_url=user.avatar_url,
        bio=user.bio,
        is_admin=user.is_admin,
        status=user.status.value,
        created_at=user.created_at,
        updated_at=user.updated_at,
        balance=wallet.balance if wallet else Decimal("0.00"),
        escrow=wallet.escrow if wallet else Decimal("0.00"),
        total_bets=user.total_bets,
        wins=user.wins,
        losses=user.losses,
        win_rate=user.win_rate,
        open_bets_count=open_bets,
        matched_bets_count=matched_bets,
        transaction_count=transaction_count,
    )


@router.patch("/users/{user_id}")
async def update_user(
    user_id: int,
    request: AdminUserUpdateRequest,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    """Update user details."""
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    changes = {}
    ip = get_client_ip(req) if req else None

    if request.display_name is not None and request.display_name != user.display_name:
        changes["display_name"] = {"old": user.display_name, "new": request.display_name}
        user.display_name = request.display_name

    if request.email is not None and request.email != user.email:
        # Check if email already exists
        existing = await db.execute(select(User).where(User.email == request.email))
        if existing.scalar_one_or_none():
            raise HTTPException(status_code=400, detail="Email already in use")
        changes["email"] = {"old": user.email, "new": request.email}
        user.email = request.email

    if request.is_admin is not None and request.is_admin != user.is_admin:
        # Cannot remove own admin status
        if user.id == admin.id and not request.is_admin:
            raise HTTPException(status_code=400, detail="Cannot remove your own admin privileges")

        await log_user_admin_change(db, admin, user, request.is_admin, ip)
        changes["is_admin"] = {"old": user.is_admin, "new": request.is_admin}
        user.is_admin = request.is_admin

    if changes:
        await log_user_update(db, admin, user, changes, ip)

    await db.commit()
    return {"message": "User updated successfully", "changes": changes}


@router.patch("/users/{user_id}/status")
async def change_user_status(
    user_id: int,
    request: AdminUserStatusRequest,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    """Enable or disable a user."""
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    # Cannot suspend yourself
    if user.id == admin.id:
        raise HTTPException(status_code=400, detail="Cannot change your own status")

    old_status = user.status.value
    new_status = UserStatus(request.status)

    if user.status == new_status:
        return {"message": "Status unchanged"}

    user.status = new_status

    ip = get_client_ip(req) if req else None
    await log_user_status_change(db, admin, user, old_status, request.status, request.reason, ip)

    await db.commit()
    return {"message": f"User status changed to {request.status}"}


@router.post("/users/{user_id}/balance", response_model=AdminBalanceAdjustResponse)
async def adjust_user_balance(
    user_id: int,
    request: AdminBalanceAdjustRequest,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    """Adjust user balance (add or subtract funds)."""
    result = await db.execute(
        select(User).options(selectinload(User.wallet)).where(User.id == user_id)
    )
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    wallet = user.wallet
    if not wallet:
        raise HTTPException(status_code=400, detail="User has no wallet")

    previous_balance = wallet.balance
    new_balance = previous_balance + request.amount

    # Validate new balance
    if new_balance < Decimal("0.00"):
        raise HTTPException(
            status_code=400,
            detail=f"Cannot reduce balance below $0. Current: ${previous_balance}, Adjustment: ${request.amount}"
        )

    if new_balance < wallet.escrow:
        raise HTTPException(
            status_code=400,
            detail=f"Cannot reduce balance below escrow amount (${wallet.escrow})"
        )

    wallet.balance = new_balance

    # Create transaction record
    tx_type = TransactionType.ADMIN_CREDIT if request.amount > 0 else TransactionType.ADMIN_DEBIT
    transaction = Transaction(
        user_id=user.id,
        wallet_id=wallet.id,
        type=tx_type,
        amount=request.amount,
        balance_after=new_balance,
        description=f"Admin adjustment: {request.reason}",
        status=TransactionStatus.COMPLETED,
    )
    db.add(transaction)
    await db.flush()

    ip = get_client_ip(req) if req else None
    await log_user_balance_adjust(
        db, admin, user,
        float(previous_balance), float(new_balance), float(request.amount),
        request.reason, transaction.id, ip
    )

    await db.commit()

    return AdminBalanceAdjustResponse(
        success=True,
        user_id=user.id,
        username=user.username,
        previous_balance=previous_balance,
        adjustment=request.amount,
        new_balance=new_balance,
        reason=request.reason,
        transaction_id=transaction.id,
    )


# ============================================================
# Settings Management (existing endpoints, enhanced with audit)
# ============================================================
@router.get("/settings")
async def get_admin_settings(
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user)
):
    result = await db.execute(select(AdminSettings).limit(1))
    settings = result.scalar_one_or_none()
    if not settings:
        settings = AdminSettings()
        db.add(settings)
        await db.commit()
        await db.refresh(settings)
    return settings


@router.patch("/settings")
async def update_admin_settings(
    updates: dict,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    result = await db.execute(select(AdminSettings).limit(1))
    settings = result.scalar_one_or_none()
    if not settings:
        settings = AdminSettings()
        db.add(settings)

    changes = {}
    for key, value in updates.items():
        if hasattr(settings, key):
            old_value = getattr(settings, key)
            if old_value != value:
                changes[key] = {"old": str(old_value), "new": str(value)}
                setattr(settings, key, value)

    if changes:
        ip = get_client_ip(req) if req else None
        await log_settings_update(db, admin, changes, ip)

    await db.commit()
    await db.refresh(settings)
    return settings


# ============================================================
# Event Management (existing endpoints, enhanced with audit)
# ============================================================
@router.post("/events", response_model=SportEventSchema)
async def create_event(
    event_data: SportEventCreate,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    event = SportEvent(
        **event_data.model_dump(),
        created_by=admin.id
    )
    db.add(event)
    await db.flush()

    ip = get_client_ip(req) if req else None
    await log_event_create(db, admin, event.id, f"{event.home_team} vs {event.away_team}", ip)

    await db.commit()
    await db.refresh(event)
    return event


@router.get("/events", response_model=list[SportEventSchema])
async def list_events(
    skip: int = 0,
    limit: int = 50,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user)
):
    result = await db.execute(
        select(SportEvent).offset(skip).limit(limit).order_by(SportEvent.game_time.desc())
    )
    return result.scalars().all()


@router.patch("/events/{event_id}", response_model=SportEventSchema)
async def update_event(
    event_id: int,
    updates: SportEventUpdate,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    result = await db.execute(select(SportEvent).where(SportEvent.id == event_id))
    event = result.scalar_one_or_none()
    if not event:
        raise HTTPException(status_code=404, detail="Event not found")

    update_data = updates.model_dump(exclude_unset=True)
    changes = {}
    for key, value in update_data.items():
        old_value = getattr(event, key)
        if old_value != value:
            changes[key] = {"old": str(old_value), "new": str(value)}
            setattr(event, key, value)

    if changes:
        ip = get_client_ip(req) if req else None
        await log_event_update(db, admin, event.id, f"{event.home_team} vs {event.away_team}", changes, ip)

    await db.commit()
    await db.refresh(event)
    return event


@router.delete("/events/{event_id}")
async def delete_event(
    event_id: int,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
    req: Request = None,
):
    result = await db.execute(select(SportEvent).where(SportEvent.id == event_id))
    event = result.scalar_one_or_none()
    if not event:
        raise HTTPException(status_code=404, detail="Event not found")

    # Check if there are any matched bets
    bets_result = await db.execute(
        select(SpreadBet).where(
            SpreadBet.event_id == event_id,
            SpreadBet.status != "open"
        )
    )
    if bets_result.scalar_one_or_none():
        raise HTTPException(
            status_code=400,
            detail="Cannot delete event with matched bets"
        )

    event_title = f"{event.home_team} vs {event.away_team}"

    ip = get_client_ip(req) if req else None
    await log_event_delete(db, admin, event_id, event_title, ip)

    await db.delete(event)
    await db.commit()
    return {"message": "Event deleted"}
