from core.config import get_db
from core.security.exceptions import CoreDBError
from datetime import datetime, date
from typing import List, Optional
import random
import string

from models.gift_card import GiftCard
from models.parent import Parent
from models import Staff,User
from models.participant import Participant
from schema.gift_card import GiftCardCreate, GiftCardUpdate
from services.wallet import WalletService

class GiftCardService:

    def generate_gift_card_code(self) -> str:
        """Generate a random gift card code in format like ABC123DEF456"""
        part1 = ''.join(random.choices(string.ascii_uppercase, k=3))
        part2 = ''.join(random.choices(string.digits, k=3))
        part3 = ''.join(random.choices(string.ascii_uppercase, k=3))
        part4 = ''.join(random.choices(string.digits, k=3))
        return f"{part1}{part2}{part3}{part4}"

    def save(self, request):
        with get_db() as db:
            code = self.generate_gift_card_code()
            
            # Check if code already exists
            existing_gift_card = db.query(GiftCard).filter(
                GiftCard.code == code,
                GiftCard.deleted_at.is_(None)
            ).first()
            
            if existing_gift_card:
                code = self.generate_gift_card_code()
                existing_gift_card = db.query(GiftCard).filter(
                    GiftCard.code == code,
                    GiftCard.deleted_at.is_(None)
                ).first()
                
                if existing_gift_card:
                    raise CoreDBError("Could not generate unique gift card code")
            
            if request.validate_date < date.today():
                raise CoreDBError("Validation date cannot be in the past")
            
            gift_card = GiftCard(
                code=code,
                amount=request.amount,
                validate_date=request.validate_date,
                is_active=request.is_active,
            )
            
            try:
                db.add(gift_card)
                db.commit()
                db.refresh(gift_card)
                return gift_card
            except Exception as e:
                db.rollback()
                raise CoreDBError(f"Could not create gift card: {e}")

    def get_all_gift_cards(self) -> List[dict]:
        with get_db() as db:
            gift_cards = db.query(GiftCard).filter(GiftCard.deleted_at.is_(None)).all()
            return [
                {
                    "id": gc.id,
                    "code": gc.code,
                    "amount": float(gc.amount),
                    "validate_date": gc.validate_date.isoformat(),
                    "is_active": gc.is_active,
                    "is_used": gc.is_used,
                    "is_used_by": gc.is_used_by,
                    "user_type": gc.user_type,
                    "created_at": gc.created_at.isoformat() if gc.created_at else None,
                    "updated_at": gc.updated_at.isoformat() if gc.updated_at else None
                }
                for gc in gift_cards
            ]

    def get_gift_card_by_id(self, gift_card_id: int) -> Optional[dict]:
        with get_db() as db:
            gift_card = db.query(GiftCard).filter(
                GiftCard.id == gift_card_id,
                GiftCard.deleted_at.is_(None)
            ).first()
            
            if not gift_card:
                return None
                
            return {
                "id": gift_card.id,
                "code": gift_card.code,
                "amount": float(gift_card.amount),
                "validate_date": gift_card.validate_date.isoformat(),
                "is_active": gift_card.is_active,
                "is_used": gift_card.is_used,
                "is_used_by": gift_card.is_used_by,
                "user_type": gift_card.user_type,
                "created_at": gift_card.created_at.isoformat() if gift_card.created_at else None,
                "updated_at": gift_card.updated_at.isoformat() if gift_card.updated_at else None
            }

    def get_gift_card_by_code(self, code: str) -> Optional[GiftCard]:
        with get_db() as db:
            return db.query(GiftCard).filter(
                GiftCard.code == code,
                GiftCard.deleted_at.is_(None)
            ).first()

    def update_gift_card(self, gift_card_id: int, request: GiftCardUpdate) -> Optional[GiftCard]:
        with get_db() as db:
            gift_card = db.query(GiftCard).filter(
                GiftCard.id == gift_card_id,
                GiftCard.deleted_at.is_(None)
            ).first()
            
            if not gift_card:
                return None
            
            if request.validate_date < date.today():
                raise HTTPException(status_code=400, detail="Validation date cannot be in the past")
            
            gift_card.amount = request.amount
            gift_card.validate_date = request.validate_date
            gift_card.is_active = request.is_active
            gift_card.updated_at = datetime.now()
            
            try:
                db.commit()
                db.refresh(gift_card)
                return gift_card
            except Exception as e:
                db.rollback()
                raise CoreDBError(f"Could not update gift card: {e}")

    def delete_gift_card(self, gift_card_id: int) -> bool:
        with get_db() as db:
            gift_card = db.query(GiftCard).filter(
                GiftCard.id == gift_card_id,
                GiftCard.deleted_at.is_(None)
            ).first()
            
            if not gift_card:
                return False
            
            if gift_card.is_used:
                return False
            
            gift_card.deleted_at = datetime.now()
            
            try:
                db.commit()
                return True
            except Exception as e:
                db.rollback()
                raise CoreDBError(f"Could not delete gift card: {e}")

    def validate_gift_card(self, code: str) -> dict:
        with get_db() as db:
            gift_card = db.query(GiftCard).filter(
                GiftCard.code == code,
                GiftCard.deleted_at.is_(None)
            ).first()
            
            if not gift_card:
                return {
                    "valid": False,
                    "message": "Gift card not found",
                    "gift_card": None
                }
            
            if gift_card.is_used:
                return {
                    "valid": False,
                    "message": "Gift card already used",
                    "gift_card": None
                }
            
            if not gift_card.is_active:
                return {
                    "valid": False,
                    "message": "Gift card is not active",
                    "gift_card": None
                }
            
            if gift_card.validate_date < date.today():
                return {
                    "valid": False,
                    "message": "Gift card has expired",
                    "gift_card": None
                }
            
            return {
                "valid": True,
                "message": "Gift card is valid",
                "gift_card": gift_card
            }

    def add_to_wallet(self, gift_card_code: str, user: dict) -> dict:
        with get_db() as db:
            gift_card = self.get_gift_card_by_code(gift_card_code)
            
            if not gift_card:
                raise CoreDBError("Gift card not found")
            
            if gift_card.is_used:
                raise CoreDBError("Gift card already used")
            
            if not gift_card.is_active:
                raise CoreDBError("Gift card is not active")
            
            if gift_card.validate_date < date.today():
                raise CoreDBError("Gift card has expired")
            
            # Ensure objects are tracked by the session
            gift_card = db.merge(gift_card)
            
            # Initialize variables
            wallet_message = ""
            
            if user['user_type'] == "parent":
                parent = db.query(Parent).filter(Parent.id == user['id']).first()
                if not parent:
                    raise CoreDBError("Parent not found")
                parent.wallet_amount += float(gift_card.amount)
                wallet_message = f"Gift card redemption: {gift_card_code}"
            elif user['user_type'] == "participant":
                participant = db.query(Participant).filter(Participant.id == user['id']).first()
                if not participant:
                    raise CoreDBError("Participant not found")
                participant.wallet_amount += float(gift_card.amount)
                wallet_message = f"Successfully added ${gift_card.amount} to wallet"
            elif user['user_type'] == "guest":
                guest_user = db.query(User).filter(User.id == user['id']).first()
                if not guest_user:
                    raise CoreDBError("Guest user not found")
                # Handle guest user wallet (if wallet_amount is None, initialize it to 0)
                if guest_user.wallet_amount is None:
                    guest_user.wallet_amount = 0
                guest_user.wallet_amount += float(gift_card.amount)
                wallet_message = f"Successfully added ${gift_card.amount} to wallet"
            else:
                # For staff and other user types, we could either raise an error or handle similarly to guests
                raise CoreDBError(f"User type {user['user_type']} cannot redeem gift cards")
            
            gift_card.is_used = True
            gift_card.is_used_by = user['id']
            gift_card.user_type = user['user_type']
            gift_card.updated_at = datetime.now()
            
            db.commit()
            db.refresh(gift_card)
            
            if user['user_type'] == "parent":
                db.refresh(parent)
                return {
                    "message": wallet_message,
                    "amount": float(gift_card.amount),
                    "remaining_balance": float(parent.wallet_amount)
                }
            elif user['user_type'] == "participant":
                db.refresh(participant)
                return {
                    "message": wallet_message,
                    "amount": float(gift_card.amount),
                    "remaining_balance": float(participant.wallet_amount)
                }
            elif user['user_type'] == "guest":
                db.refresh(guest_user)
                return {
                    "message": wallet_message,
                    "amount": float(gift_card.amount),
                    "remaining_balance": float(guest_user.wallet_amount)
                }
            else:
                # This shouldn't be reached due to the earlier error, but included for completeness
                return {
                    "message": wallet_message,
                    "amount": float(gift_card.amount),
                    "remaining_balance": 0
                }