from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from typing import List, Optional
from sqlalchemy.orm import Session
from sqlalchemy import text
from app.core.database import get_db
from app.core.auth import check_trip_auth
from app.schemas.expense import ExpenseCreate, ExpenseResponse
from app.models.expense import Expense, ExpenseSplit
from app.models.trip import Trip
from app.models.participant import Participant

router = APIRouter()

@router.post("/{trip_id}/expenses", response_model=ExpenseResponse)
async def create_expense(
    trip_id: int,
    expense: ExpenseCreate,
    db: Session = Depends(get_db),
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False))
):
    """Create a new expense with splits (requires authentication if trip has password)"""
    # Check trip authentication
    trip = check_trip_auth(trip_id, credentials, db)

    # Validate splits sum to 100% (allow 98-100.01% to account for rounding down in equal splits)
    total_percentage = sum(split.percentage for split in expense.splits)
    if total_percentage < 98 or total_percentage > 100.01:  # Accept 98-100.01% for equal splits (e.g., 33% x 3 = 99%)
        raise HTTPException(status_code=400, detail=f"Splits must sum to 100%. Current total: {total_percentage:.2f}%")

    # Verify all participants exist and belong to this trip
    participant_ids = [split.participant_id for split in expense.splits]
    participants = db.query(Participant).filter(
        Participant.id.in_(participant_ids),
        Participant.trip_id == trip_id,
        Participant.is_active == True
    ).all()

    if len(participants) != len(participant_ids):
        raise HTTPException(status_code=400, detail="Invalid participants")

    # Verify paid_by participant exists
    paid_by = db.query(Participant).filter(
        Participant.id == expense.paid_by_id,
        Participant.trip_id == trip_id,
        Participant.is_active == True
    ).first()
    if not paid_by:
        raise HTTPException(status_code=400, detail="Invalid paid_by participant")

    # Create expense
    db_expense = Expense(
        trip_id=trip_id,
        description=expense.description,
        amount=expense.amount,
        currency_code=expense.currency_code,
        exchange_rate=expense.exchange_rate,
        category=expense.category,
        receipt_url=expense.receipt_url,
        paid_by_id=expense.paid_by_id,
        date_incurred=datetime.utcnow()
    )
    db.add(db_expense)
    db.commit()
    db.refresh(db_expense)

    # Create expense splits
    splits = []
    for split_data in expense.splits:
        # Calculate amount in trip currency (convert first, then apply percentage)
        amount_in_trip_currency = (expense.amount * expense.exchange_rate) * (split_data.percentage / 100.0)
        db_split = ExpenseSplit(
            expense_id=db_expense.id,
            participant_id=split_data.participant_id,
            percentage=split_data.percentage,
            amount=amount_in_trip_currency
        )
        db.add(db_split)
        splits.append(db_split)

    db.commit()

    # Refresh to get all data
    db.refresh(db_expense)

    # Build response with joined data
    return build_expense_response(db_expense, db)

@router.get("/{trip_id}/expenses", response_model=List[ExpenseResponse])
async def get_expenses(
    trip_id: int,
    db: Session = Depends(get_db),
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False))
):
    """Get all expenses for a trip (requires authentication if trip has password)"""
    # Check trip authentication
    trip = check_trip_auth(trip_id, credentials, db)

    expenses = db.query(Expense).filter(Expense.trip_id == trip_id).order_by(Expense.created_at.desc()).all()

    return [build_expense_response(expense, db) for expense in expenses]

@router.get("/{trip_id}/expenses/{expense_id}", response_model=ExpenseResponse)
async def get_expense(
    trip_id: int,
    expense_id: int,
    db: Session = Depends(get_db),
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False))
):
    """Get a specific expense (requires authentication if trip has password)"""
    # Check trip authentication
    trip = check_trip_auth(trip_id, credentials, db)
    
    expense = db.query(Expense).filter(
        Expense.id == expense_id,
        Expense.trip_id == trip_id
    ).first()

    if not expense:
        raise HTTPException(status_code=404, detail="Expense not found")

    return build_expense_response(expense, db)

def build_expense_response(expense: Expense, db: Session) -> ExpenseResponse:
    """Build expense response with joined data"""
    # Get paid_by name
    paid_by = db.query(Participant).filter(Participant.id == expense.paid_by_id).first()

    # Get splits with participant names
    splits = db.query(ExpenseSplit, Participant).join(
        Participant, ExpenseSplit.participant_id == Participant.id
    ).filter(ExpenseSplit.expense_id == expense.id).all()

    split_responses = []
    for split, participant in splits:
        split_responses.append({
            "id": split.id,
            "expense_id": split.expense_id,
            "participant_id": split.participant_id,
            "participant_name": participant.name,
            "percentage": split.percentage,
            "amount": split.amount,
            "is_settled": split.is_settled,
            "created_at": split.created_at
        })

    return {
        "id": expense.id,
        "trip_id": expense.trip_id,
        "description": expense.description,
        "amount": expense.amount,
        "currency_code": expense.currency_code,
        "exchange_rate": expense.exchange_rate,
        "amount_in_trip_currency": expense.amount * expense.exchange_rate,
        "category": expense.category,
        "receipt_url": expense.receipt_url,
        "paid_by_id": expense.paid_by_id,
        "paid_by_name": paid_by.name if paid_by else "Unknown",
        "date_incurred": expense.date_incurred,
        "created_at": expense.created_at,
        "splits": split_responses
    }

@router.put("/expenses/{expense_id}", response_model=ExpenseResponse)
async def update_expense(
    expense_id: int,
    expense: ExpenseCreate,
    db: Session = Depends(get_db),
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False))
):
    """Update an existing expense with splits (requires authentication if trip has password)"""
    # Check if expense exists
    existing_expense = db.query(Expense).filter(Expense.id == expense_id).first()
    if not existing_expense:
        raise HTTPException(status_code=404, detail="Expense not found")

    # Check trip authentication
    trip = check_trip_auth(existing_expense.trip_id, credentials, db)

    # Validate splits sum to 100% (allow 98-100.01% to account for rounding down in equal splits)
    total_percentage = sum(split.percentage for split in expense.splits)
    if total_percentage < 98 or total_percentage > 100.01:  # Accept 98-100.01% for equal splits (e.g., 33% x 3 = 99%)
        raise HTTPException(status_code=400, detail=f"Splits must sum to 100%. Current total: {total_percentage:.2f}%")

    # Verify all participants exist and belong to this trip
    participant_ids = [split.participant_id for split in expense.splits]
    participants = db.query(Participant).filter(
        Participant.id.in_(participant_ids),
        Participant.trip_id == existing_expense.trip_id,
        Participant.is_active == True
    ).all()

    if len(participants) != len(participant_ids):
        raise HTTPException(status_code=400, detail="Invalid participants")

    # Verify paid_by participant exists
    paid_by = db.query(Participant).filter(
        Participant.id == expense.paid_by_id,
        Participant.trip_id == existing_expense.trip_id,
        Participant.is_active == True
    ).first()
    if not paid_by:
        raise HTTPException(status_code=400, detail="Invalid paid_by participant")

    # Update expense details
    existing_expense.description = expense.description
    existing_expense.amount = expense.amount
    existing_expense.currency_code = expense.currency_code
    existing_expense.exchange_rate = expense.exchange_rate
    existing_expense.category = expense.category
    existing_expense.paid_by_id = expense.paid_by_id
    # Use provided date_incurred or default to current time
    existing_expense.date_incurred = expense.date_incurred if expense.date_incurred else datetime.utcnow()

    # Delete existing splits
    db.query(ExpenseSplit).filter(ExpenseSplit.expense_id == expense_id).delete()

    # Create new splits
    for split_data in expense.splits:
        # Calculate amount in trip currency for this split
        amount_in_trip_currency = (expense.amount * expense.exchange_rate) * (split_data.percentage / 100)

        expense_split = ExpenseSplit(
            expense_id=existing_expense.id,
            participant_id=split_data.participant_id,
            percentage=split_data.percentage,
            amount=amount_in_trip_currency,
            is_settled=False
        )
        db.add(expense_split)

    db.commit()
    db.refresh(existing_expense)

    return build_expense_response(existing_expense, db)