from fastapi import APIRouter, HTTPException
from app.core.database import execute_query
from typing import List, Optional, Dict, Any
from pydantic import BaseModel
from datetime import datetime, date
from decimal import Decimal, ROUND_CEILING
import logging

logger = logging.getLogger(__name__)

router = APIRouter()

# Pydantic Models
class PrepaidCard(BaseModel):
    id: Optional[int] = None
    card_number: str
    customer_id: int
    purchased_hours: float
    used_hours: float
    remaining_hours: float
    price_per_hour: float
    total_amount: float
    status: str
    purchased_at: Optional[datetime] = None
    expires_at: Optional[datetime] = None
    economic_invoice_number: Optional[str] = None
    economic_product_number: Optional[str] = None
    notes: Optional[str] = None
    rounding_minutes: Optional[int] = None
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None

class PrepaidCardCreate(BaseModel):
    customer_id: int
    purchased_hours: float
    price_per_hour: float
    expires_at: Optional[date] = None
    notes: Optional[str] = None
    rounding_minutes: Optional[int] = None


class PrepaidCardRoundingUpdate(BaseModel):
    rounding_minutes: int


def _normalize_rounding_minutes(value: Optional[int]) -> int:
    if value is None:
        return 0
    return int(value)


def _apply_rounding(hours: float, rounding_minutes: int) -> float:
    if rounding_minutes <= 0:
        return float(hours)
    interval = Decimal(rounding_minutes) / Decimal(60)
    raw = Decimal(str(hours))
    rounded = (raw / interval).to_integral_value(rounding=ROUND_CEILING) * interval
    return float(rounded)


@router.get("/prepaid-cards", response_model=List[Dict[str, Any]])
async def get_prepaid_cards(status: Optional[str] = None, customer_id: Optional[int] = None):
    """
    Get all prepaid cards with customer information
    """
    try:
        query = """
            SELECT 
                pc.*,
                c.name as customer_name,
                c.email as customer_email,
                (SELECT COUNT(*) FROM tticket_prepaid_transactions WHERE card_id = pc.id) as transaction_count
            FROM tticket_prepaid_cards pc
            LEFT JOIN customers c ON pc.customer_id = c.id
            WHERE 1=1
        """
        params = []
        
        if status:
            query += " AND pc.status = %s"
            params.append(status)
            
        if customer_id:
            query += " AND pc.customer_id = %s"
            params.append(customer_id)
            
        query += " ORDER BY pc.created_at DESC"
        
        cards = execute_query(query, tuple(params) if params else None)
        
        # Recalculate used_hours and remaining_hours from actual timelogs for each card
        for card in cards or []:
            card_id = card['id']
            rounding_minutes = _normalize_rounding_minutes(card.get('rounding_minutes'))
            
            # Get sag timelogs
            sag_logs = execute_query("""
                SELECT original_hours, approved_hours
                FROM tmodule_times
                WHERE prepaid_card_id = %s
            """, (card_id,))
            
            # Get ticket timelogs
            ticket_logs = execute_query("""
                SELECT hours, rounded_hours
                FROM tticket_worklog
                WHERE prepaid_card_id = %s
            """, (card_id,))
            
            # Calculate total rounded hours
            total_rounded = 0.0
            for log in sag_logs or []:
                actual = float(log['original_hours'])
                if log.get('approved_hours'):
                    total_rounded += float(log['approved_hours'])
                else:
                    total_rounded += _apply_rounding(actual, rounding_minutes)
            
            for log in ticket_logs or []:
                if log.get('rounded_hours'):
                    total_rounded += float(log['rounded_hours'])
                else:
                    total_rounded += float(log['hours'])
            
            # Override with calculated values
            purchased = float(card['purchased_hours'])
            card['used_hours'] = total_rounded
            card['remaining_hours'] = purchased - total_rounded
        
        logger.info(f"✅ Retrieved {len(cards) if cards else 0} prepaid cards")
        return cards or []
        
    except Exception as e:
        logger.error(f"❌ Error fetching prepaid cards: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/prepaid-cards/{card_id:int}", response_model=Dict[str, Any])
async def get_prepaid_card(card_id: int):
    """
    Get a specific prepaid card with transactions
    """
    try:
        result = execute_query("""
            SELECT 
                pc.*,
                c.name as customer_name,
                c.email as customer_email
            FROM tticket_prepaid_cards pc
            LEFT JOIN customers c ON pc.customer_id = c.id
            WHERE pc.id = %s
        """, (card_id,))
        
        if not result or len(result) == 0:
            raise HTTPException(status_code=404, detail="Prepaid card not found")
        
        card = result[0]

        rounding_minutes = _normalize_rounding_minutes(card.get('rounding_minutes'))
            
        # Get transactions
        transactions = execute_query("""
            SELECT 
                pt.*,
                w.ticket_id,
                t.subject as ticket_title
            FROM tticket_prepaid_transactions pt
            LEFT JOIN tticket_worklog w ON pt.worklog_id = w.id
            LEFT JOIN tticket_tickets t ON w.ticket_id = t.id
            WHERE pt.card_id = %s
            ORDER BY pt.created_at DESC
        """, (card_id,))
        
        card['transactions'] = transactions or []

        # Timelogs from Sag + Ticket worklog (all entries tied to this prepaid card)
        sag_logs = execute_query("""
            SELECT
                tm.id,
                tm.sag_id,
                tm.worked_date,
                tm.description,
                tm.original_hours,
                tm.approved_hours,
                tm.created_at,
                tm.user_name,
                s.titel AS sag_title
            FROM tmodule_times tm
            LEFT JOIN sag_sager s ON tm.sag_id = s.id
            WHERE tm.prepaid_card_id = %s
        """, (card_id,))

        ticket_logs = execute_query("""
            SELECT
                w.id,
                w.ticket_id,
                w.work_date,
                w.description,
                w.hours,
                w.rounded_hours,
                w.created_at,
                t.subject AS ticket_title,
                t.ticket_number
            FROM tticket_worklog w
            LEFT JOIN tticket_tickets t ON w.ticket_id = t.id
            WHERE w.prepaid_card_id = %s
        """, (card_id,))

        timelogs = []
        for log in sag_logs or []:
            actual_hours = float(log['original_hours'])
            # Use stored approved_hours if available, else calculate
            rounded_hours = float(log.get('approved_hours') or 0) if log.get('approved_hours') else _apply_rounding(actual_hours, rounding_minutes)
            timelogs.append({
                "source": "sag",
                "source_id": log.get("sag_id"),
                "source_title": log.get("sag_title"),
                "worked_date": log.get("worked_date"),
                "created_at": log.get("created_at"),
                "description": log.get("description"),
                "user_name": log.get("user_name"),
                "actual_hours": actual_hours,
                "rounded_hours": rounded_hours
            })

        for log in ticket_logs or []:
            actual_hours = float(log['hours'])
            # Use stored rounded_hours if available, else use actual
            rounded_hours = float(log.get('rounded_hours') or 0) if log.get('rounded_hours') else actual_hours
            ticket_label = log.get("ticket_number") or log.get("ticket_id")
            timelogs.append({
                "source": "ticket",
                "source_id": log.get("ticket_id"),
                "source_title": log.get("ticket_title") or "Ticket",
                "ticket_number": ticket_label,
                "worked_date": log.get("work_date"),
                "created_at": log.get("created_at"),
                "description": log.get("description"),
                "user_name": None,
                "actual_hours": actual_hours,
                "rounded_hours": rounded_hours
            })

        timelogs.sort(
            key=lambda item: (
                item.get("worked_date") or date.min,
                item.get("created_at") or datetime.min
            ),
            reverse=True
        )
        
        # Calculate actual totals from timelogs (sum of rounded hours)
        total_rounded_hours = sum(log['rounded_hours'] for log in timelogs)
        purchased_hours = float(card['purchased_hours'])
        
        # Override DB values with calculated values based on actual timelogs
        card['used_hours'] = total_rounded_hours
        card['remaining_hours'] = purchased_hours - total_rounded_hours
        card['timelogs'] = timelogs
        card['rounding_minutes'] = rounding_minutes
        
        return card
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error fetching prepaid card {card_id}: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/prepaid-cards", response_model=Dict[str, Any])
async def create_prepaid_card(card: PrepaidCardCreate):
    """
    Create a new prepaid card
    
    Note: As of migration 065, customers can have multiple active cards simultaneously.
    """
    try:
        rounding_minutes = _normalize_rounding_minutes(card.rounding_minutes)
        if rounding_minutes not in (0, 15, 30, 60):
            raise HTTPException(status_code=400, detail="Invalid rounding minutes")

        # Calculate total amount
        total_amount = card.purchased_hours * card.price_per_hour
        
        # Create card (need to use fetch=False for INSERT RETURNING)
        conn = None
        try:
            from app.core.database import get_db_connection, release_db_connection
            from psycopg2.extras import RealDictCursor
            
            conn = get_db_connection()
            with conn.cursor(cursor_factory=RealDictCursor) as cursor:
                cursor.execute("""
                    INSERT INTO tticket_prepaid_cards 
                    (customer_id, purchased_hours, price_per_hour, total_amount, expires_at, notes, rounding_minutes)
                    VALUES (%s, %s, %s, %s, %s, %s, %s)
                    RETURNING *
                """, (
                    card.customer_id,
                    card.purchased_hours,
                    card.price_per_hour,
                    total_amount,
                    card.expires_at,
                    card.notes
                    , rounding_minutes
                ))
                conn.commit()
                result = cursor.fetchone()
                
                logger.info(f"✅ Created prepaid card: {result['card_number']}")
                return result
        finally:
            if conn:
                release_db_connection(conn)
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error creating prepaid card: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/prepaid-cards/{card_id:int}/status")
async def update_card_status(card_id: int, status: str):
    """
    Update prepaid card status (cancel, reactivate)
    """
    try:
        if status not in ['active', 'cancelled']:
            raise HTTPException(status_code=400, detail="Invalid status")
        
        conn = None
        try:
            from app.core.database import get_db_connection, release_db_connection
            from psycopg2.extras import RealDictCursor
            
            conn = get_db_connection()
            with conn.cursor(cursor_factory=RealDictCursor) as cursor:
                cursor.execute("""
                    UPDATE tticket_prepaid_cards 
                    SET status = %s
                    WHERE id = %s
                    RETURNING *
                """, (status, card_id))
                conn.commit()
                result = cursor.fetchone()
                
                if not result:
                    raise HTTPException(status_code=404, detail="Card not found")
                
                logger.info(f"✅ Updated card {card_id} status to {status}")
                return result
        finally:
            if conn:
                release_db_connection(conn)
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error updating card status: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/prepaid-cards/{card_id:int}/rounding", response_model=Dict[str, Any])
async def update_card_rounding(card_id: int, payload: PrepaidCardRoundingUpdate):
    """
    Update rounding interval for a prepaid card (minutes)
    """
    try:
        rounding_minutes = _normalize_rounding_minutes(payload.rounding_minutes)
        if rounding_minutes not in (0, 15, 30, 60):
            raise HTTPException(status_code=400, detail="Invalid rounding minutes")

        result = execute_query(
            """
            UPDATE tticket_prepaid_cards
            SET rounding_minutes = %s
            WHERE id = %s
            RETURNING *
            """,
            (rounding_minutes, card_id)
        )

        if not result:
            raise HTTPException(status_code=404, detail="Card not found")

        return result[0]

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error updating rounding for card {card_id}: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.delete("/prepaid-cards/{card_id:int}")
async def delete_prepaid_card(card_id: int):
    """
    Delete a prepaid card (only if no transactions)
    """
    try:
        # Check for transactions
        transactions = execute_query("""
            SELECT COUNT(*) as count FROM tticket_prepaid_transactions 
            WHERE card_id = %s
        """, (card_id,))
        
        if transactions and len(transactions) > 0 and transactions[0]['count'] > 0:
            raise HTTPException(
                status_code=400, 
                detail="Cannot delete card with transactions"
            )
        
        execute_query("DELETE FROM tticket_prepaid_cards WHERE id = %s", (card_id,), fetch=False)
        
        logger.info(f"✅ Deleted prepaid card {card_id}")
        return {"message": "Card deleted successfully"}
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error deleting card: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/prepaid-cards/stats/summary", response_model=Dict[str, Any])
async def get_prepaid_stats():
    """
    Get prepaid cards statistics (calculated from actual timelogs)
    """
    try:
        # Get all cards
        cards = execute_query("""
            SELECT id, status, purchased_hours, total_amount, rounding_minutes
            FROM tticket_prepaid_cards
        """)
        
        stats = {
            'active_count': 0,
            'depleted_count': 0,
            'expired_count': 0,
            'cancelled_count': 0,
            'total_remaining_hours': 0.0,
            'total_used_hours': 0.0,
            'total_purchased_hours': 0.0,
            'total_revenue': 0.0
        }
        
        for card in cards or []:
            card_id = card['id']
            status = card['status']
            purchased = float(card['purchased_hours'])
            rounding_minutes = _normalize_rounding_minutes(card.get('rounding_minutes'))
            
            # Count by status
            if status == 'active':
                stats['active_count'] += 1
            elif status == 'depleted':
                stats['depleted_count'] += 1
            elif status == 'expired':
                stats['expired_count'] += 1
            elif status == 'cancelled':
                stats['cancelled_count'] += 1
            
            # Calculate actual used hours from timelogs
            sag_logs = execute_query("""
                SELECT original_hours, approved_hours
                FROM tmodule_times
                WHERE prepaid_card_id = %s
            """, (card_id,))
            
            ticket_logs = execute_query("""
                SELECT hours, rounded_hours
                FROM tticket_worklog
                WHERE prepaid_card_id = %s
            """, (card_id,))
            
            used = 0.0
            for log in sag_logs or []:
                if log.get('approved_hours'):
                    used += float(log['approved_hours'])
                else:
                    used += _apply_rounding(float(log['original_hours']), rounding_minutes)
            
            for log in ticket_logs or []:
                if log.get('rounded_hours'):
                    used += float(log['rounded_hours'])
                else:
                    used += float(log['hours'])
            
            remaining = purchased - used
            
            stats['total_purchased_hours'] += purchased
            stats['total_used_hours'] += used
            stats['total_revenue'] += float(card['total_amount'])
            
            if status == 'active':
                stats['total_remaining_hours'] += remaining
        
        return stats
        
    except Exception as e:
        logger.error(f"❌ Error fetching prepaid stats: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))
