from django.utils import timezone
import uuid
import datetime
from ..models import PlayerRegistration, TeamRegistration, Tournament
from ..utils.licence_validator import LicenseValidator
from ..utils.player_search import get_player_name_from_csv
from ..models.enums import FederalCategory, RegistrationStatus
from ..models.player_enums import PlayerSexType, PlayerDataSource
from django.contrib.auth import get_user_model
from django.conf import settings
from django.utils.dateparse import parse_datetime
from .currency_service import CurrencyService

class RegistrationCartManager:
    """
    Manages the registration cart for tournament registrations.
    Handles session-based cart operations, player additions/removals,
    and checkout processes.
    """

    CART_EXPIRY_SECONDS = 300

    def __init__(self, request):
        self.request = request
        self.session = request.session
        self.first_tournament = False

    def _clean_expired_cart(self):
        """Clean up expired cart data from session"""
        if 'registration_cart_expiry' in self.session:
            try:
                expiry_str = self.session['registration_cart_expiry']
                expiry = datetime.datetime.fromisoformat(expiry_str)
                if timezone.now() > expiry:
                    # Clear expired cart
                    keys_to_delete = [
                        'registration_cart_id',
                        'registration_tournament_id',
                        'registration_cart_players',
                        'registration_cart_expiry',
                        'registration_mobile_number'
                    ]
                    for key in keys_to_delete:
                        if key in self.session:
                            del self.session[key]
                    self.session.modified = True
            except (ValueError, TypeError):
                # Invalid expiry format, clear it
                if 'registration_cart_expiry' in self.session:
                    del self.session['registration_cart_expiry']
                    self.session.modified = True

    def get_or_create_cart_id(self):
        """Get or create a registration cart ID in the session"""
        if 'registration_cart_id' not in self.session:
            self.session['registration_cart_id'] = str(uuid.uuid4())  # Ensure it's a string
            self.session.modified = True
        return self.session['registration_cart_id']

    def get_cart_expiry(self):
        """Get the cart expiry time from the session"""
        if 'registration_cart_expiry' not in self.session:
            # Set default expiry to 30 minutes from now
            expiry = timezone.now() + datetime.timedelta(seconds=self.CART_EXPIRY_SECONDS)
            self.session['registration_cart_expiry'] = expiry.isoformat()
            self.session.modified = True
        return self.session['registration_cart_expiry']

    def is_cart_expired(self):
        """Check if the registration cart is expired"""
        if 'registration_cart_expiry' not in self.session:
            return False

        expiry_str = self.session['registration_cart_expiry']
        try:
            expiry = parse_datetime(expiry_str)
            if expiry is None:
                self._clean_expired_cart()
                return True
            return timezone.now() > expiry
        except (ValueError, TypeError):
            self._clean_expired_cart()
            return True

    def reset_cart_expiry(self):
        """Reset the cart expiry time"""
        expiry = timezone.now() + datetime.timedelta(seconds=self.CART_EXPIRY_SECONDS)
        self.session['registration_cart_expiry'] = expiry.isoformat()
        self.session.modified = True

    def get_tournament_id(self):
        """Get the tournament ID associated with the current cart"""
        return self.session.get('registration_tournament_id')

    def initialize_cart(self, tournament_id):
        """Initialize a new registration cart for a tournament"""
        # Clear any existing cart
        self.clear_cart()

        try:
            tournament = Tournament.objects.get(id=tournament_id)
        except Tournament.DoesNotExist:
            return False, "Tournoi introuvable."

        # Update tournament reserved spots
        waiting_list_position = tournament.get_waiting_list_position()

        # Set up the new cart
        self.session['registration_cart_id'] = str(uuid.uuid4())  # Ensure it's a string
        self.session['waiting_list_position'] = waiting_list_position
        self.session['registration_tournament_id'] = str(tournament_id)  # Ensure it's a string
        self.session['registration_cart_players'] = []
        self.reset_cart_expiry()
        self.session.modified = True

        return True, "Cart initialized successfully"

    def get_cart_data(self):
        """Get the data for the current registration cart"""
        # Ensure cart players array exists
        if 'registration_cart_players' not in self.session:
            self.session['registration_cart_players'] = []
            self.session.modified = True

        # Ensure tournament ID exists
        if 'registration_tournament_id' not in self.session:
            # If no tournament ID but we have players, this is an inconsistency
            if self.session.get('registration_cart_players'):
                print("WARNING: Found players but no tournament ID - clearing players")
                self.session['registration_cart_players'] = []
                self.session.modified = True

        # Get user phone if authenticated
        user_phone = ''
        if hasattr(self.request.user, 'phone'):
            user_phone = self.request.user.phone

        # Parse the expiry time from ISO format to datetime
        expiry_str = self.get_cart_expiry()
        expiry_datetime = None
        if expiry_str:
            try:
                # Parse the ISO format string to datetime
                expiry_datetime = parse_datetime(expiry_str)
            except (ValueError, TypeError):
                # If parsing fails, set a new expiry
                expiry_datetime = timezone.now() + datetime.timedelta(seconds=self.CART_EXPIRY_SECONDS)

        cart_data = {
            'cart_id': self.get_or_create_cart_id(),
            'tournament_id': self.session.get('registration_tournament_id'),
            'waiting_list_position': self.session.get('waiting_list_position'),
            'players': self.session.get('registration_cart_players', []),
            'expiry': expiry_datetime,  # Now a datetime object, not a string
            'is_cart_expired': self.is_cart_expired(),
            'team_fee_from_cart_players': self.team_fee_from_cart_players(),
            'team_fee_from_cart_players_formatted': self.team_fee_from_cart_players_formatted(),
            'mobile_number': self.session.get('registration_mobile_number', user_phone)
        }

        # Debug: print the cart content
        print(f"Cart data - Tournament ID: {cart_data['tournament_id']}")
        print(f"Cart data - Players count: {len(cart_data['players'])}")

        return cart_data

    def team_fee_from_cart_players(self):
        # Get tournament
        tournament_id = self.session.get('registration_tournament_id')

        try:
            tournament = Tournament.objects.get(id=tournament_id)
        except Tournament.DoesNotExist:
            return 0

        players = self.session.get('registration_cart_players', []),
        entry_fee = tournament.entry_fee
        if entry_fee is not None and entry_fee > 0 and tournament.enable_online_payment:
            fee = entry_fee * tournament.minimum_player_per_team
            players = self.session.get('registration_cart_players', [])
            club_members = sum(1 for player in players if player.get('club_member', False))
            if tournament.club_member_fee_deduction is not None:
                return fee - club_members * tournament.club_member_fee_deduction
            return fee
        else:
            return 0

    def team_fee_from_cart_players_formatted(self):
        """Get the team fee formatted with the tournament's currency."""
        team_fee = self.team_fee_from_cart_players()
        tournament_id = self.session.get('registration_tournament_id')

        try:
            tournament = Tournament.objects.get(id=tournament_id)
            currency_code = tournament.currency_code if tournament and tournament.currency_code else 'EUR'
        except Tournament.DoesNotExist:
            currency_code = 'EUR'

        return CurrencyService.format_amount(team_fee, currency_code)

    def add_player(self, player_data):
        """Add a player to the registration cart"""
        print("add_player", player_data)
        if self.is_cart_expired():
            return False, "Votre session d'inscription a expiré, veuillez réessayer."

        # Get cart data
        tournament_id = self.session.get('registration_tournament_id')
        if not tournament_id:
            return False, "Pas d'inscription active."

        # Get tournament
        try:
            tournament = Tournament.objects.get(id=tournament_id)
        except Tournament.DoesNotExist:
            return False, "Tournoi introuvable."

        # Get existing players directly from session
        players = self.session.get('registration_cart_players', [])

        # Check if we've reached the team limit
        if len(players) >= 2:  # Assuming teams of 2 for padel
            return False, "Nombre maximum de joueurs déjà ajouté."

        # Process player data
        licence_id = player_data.get('licence_id', '').upper() if player_data.get('licence_id') else None
        first_name = player_data.get('first_name', '')
        last_name = player_data.get('last_name', '').upper() if player_data.get('last_name') else ''

        # Handle license validation logic
        result = self._process_player_license(
            tournament, licence_id, first_name, last_name, players, len(players) == 0
        )
        if not result[0]:
            return result  # Return the error

        tournament_federal_category = tournament.federal_category

        if licence_id:
            # Get federation data
            fed_data, found = get_player_name_from_csv(tournament_federal_category, licence_id)
            if found is False and fed_data:
                is_woman = fed_data.get('is_woman', False)

                if tournament_federal_category == FederalCategory.MIXED and len(players) == 1:
                    other_player_is_woman = players[0].get('is_woman', False)
                    if other_player_is_woman == is_woman:
                        is_woman = not is_woman

                player_data.update({
                    'rank': fed_data['rank'],
                    'is_woman': is_woman,
                })
            if found and fed_data:
                # Use federation data (including check for eligibility)
                if tournament_federal_category == FederalCategory.MIXED and len(players) == 1:
                    is_woman = fed_data.get('is_woman', False)
                    other_player_is_woman = players[0].get('is_woman', False)
                    if other_player_is_woman == is_woman:
                        return False, f"En mixte l'équipe doit obligatoirement contenir une joueuse et un joueur. La licence {licence_id} correspond à {'une' if is_woman else 'un'} {'femme' if is_woman else 'homme'}."

                player_register_check = tournament.player_register_check(licence_id)
                if player_register_check:
                    return False, ", ".join(player_register_check)

                # Update player data from federation data
                player_data.update({
                    'first_name': fed_data['first_name'],
                    'last_name': fed_data['last_name'],
                    'rank': fed_data['rank'],
                    'is_woman': fed_data['is_woman'],
                    'points': fed_data.get('points'),
                    'assimilation': fed_data.get('assimilation'),
                    'tournament_count': fed_data.get('tournament_count'),
                    'ligue_name': fed_data.get('ligue_name'),
                    'club_name': fed_data.get('club_name'),
                    'club_code': fed_data.get('club_code'),
                    'birth_year': fed_data.get('birth_year'),
                    'found_in_french_federation': True,
                    'club_member': tournament.is_cart_player_from_club(fed_data)
                })
            elif not first_name or not last_name:
                # License not required or not found, but name is needed
                print("Le prénom et le nom sont obligatoires pour les joueurs dont la licence n'a pas été trouvée.")
                self.first_tournament = True
                return False, "Le prénom et le nom sont obligatoires pour les joueurs dont la licence n'a pas été trouvée."
        elif not tournament.license_is_required:
            # License not required, check if name is provided
            if not first_name or not last_name:
                return False, "Le prénom et le nom sont obligatoires."
        else:
            # License is required but not provided
            return False, "Le numéro de licence est obligatoire."

        # Create player registrations
        sex, rank, computed_rank = self._compute_rank_and_sex(
            tournament,
            player_data
        )

        player_data['computed_rank'] = computed_rank

        # Add player to cart
        players.append(player_data)
        self.session['registration_cart_players'] = players
        self.reset_cart_expiry()
        self.session.modified = True

        if sex == PlayerSexType.FEMALE:
            return True, "Joueuse ajoutée avec succès."
        else:
            return True, "Joueur ajouté avec succès."

    def _process_player_license(self, tournament, licence_id, first_name, last_name, players, is_first_player):
        """
        Process and validate player license
        Returns (True, None) if valid, (False, error_message) if invalid
        """
        # Handle case where license is required
        if tournament.license_is_required:
            # If license is required but not provided
            if not licence_id:
                # First player (authentication check) or partner
                user_message = "Le numéro de licence est obligatoire." if is_first_player else "Le numéro de licence de votre partenaire est obligatoire."
                return False, user_message

            # Validate the license format
            validator = LicenseValidator(licence_id)
            if not validator.validate_license():
                if settings.DEBUG:
                    return False, f"Le numéro de licence est invalide, la lettre ne correspond pas. {validator.get_computed_license_key(validator.stripped_license)}"
                else:
                    return False, "Le numéro de licence est invalide, la lettre ne correspond pas."

            # Check if player is already registered in tournament
            stripped_license = validator.stripped_license
            if self._is_player_already_registered(stripped_license, tournament):
                return False, "Un joueur avec ce numéro de licence est déjà inscrit dans une équipe."

            # Check if this is the authenticated user trying to register as first player
            if self.request.user.is_authenticated and is_first_player and self.request.user.licence_id is None:
                # Try to update the user's license ID in the database
                try:
                    self.request.user.licence_id = validator.computed_licence_id
                    self.request.user.save()
                    self.request.user.refresh_from_db()
                except:
                    return False, "Erreur lors de la mise à jour de votre licence: cette licence est déjà utilisée par un autre joueur."

        # Check for duplicate licenses in cart
        existing_licenses = [p.get('licence_id') for p in players if p.get('licence_id')]
        if licence_id and licence_id in existing_licenses:
            return False, "Ce joueur est déjà dans l'équipe."

        return True, None

    def remove_player(self):
        """Remove the last player from the cart"""
        if self.is_cart_expired():
            return False, "Votre session d'inscription a expiré, veuillez réessayer."

        players = self.session.get('registration_cart_players', [])
        if not players:
            return False, "Pas de joueur à supprimer."

        # Remove last player
        players.pop()
        self.session['registration_cart_players'] = players
        self.reset_cart_expiry()
        self.session.modified = True

        return True, "Joueur retiré."

    def update_contact_info(self, mobile_number=None):
        """Update contact info for the cart"""
        if self.is_cart_expired():
            return False, "Votre session d'inscription a expiré, veuillez réessayer."

        if mobile_number is not None:
            self.session['registration_mobile_number'] = mobile_number

        self.reset_cart_expiry()
        self.session.modified = True

        return True, "Informations de contact mises à jour."

    def checkout(self, confirmed):
        """Convert cart to an actual tournament registration"""
        print("Checkout")
        if self.is_cart_expired():
            return False, "Votre session d'inscription a expiré, veuillez réessayer."

        # Get cart data
        cart_data = self.get_cart_data()
        tournament_id = cart_data.get('tournament_id')
        players = cart_data.get('players')
        mobile_number = cart_data.get('mobile_number')

        # Validate cart data
        if not tournament_id:
            return False, "Aucun tournoi sélectionné."

        if not players:
            return False, "Aucun joueur dans l'inscription."

        # Get tournament
        try:
            tournament = Tournament.objects.get(id=tournament_id)
        except Tournament.DoesNotExist:
            return False, "Tournoi introuvable."

        # Check minimum players
        if len(players) < tournament.minimum_player_per_team:
            return False, f"Vous avez besoin d'au moins {tournament.minimum_player_per_team} joueurs pour vous inscrire."

        # Identify captain from user's license
        #         # Update user phone if provided
        if self.request.user.is_authenticated and mobile_number:
            self.request.user.phone = mobile_number
            self.request.user.save(update_fields=['phone'])

        stripped_license = None
        if self.request.user.is_authenticated and self.request.user.licence_id:
            validator = LicenseValidator(self.request.user.licence_id)
            stripped_license = validator.stripped_license

        weight = sum(int(player_data.get('computed_rank', 0) or 0) for player_data in players)

        # Create team registration
        if confirmed:
            target_tournament = tournament
        else:
            target_tournament = None
        team_registration = TeamRegistration.objects.create(
            tournament=target_tournament,
            registration_date=timezone.now(),
            walk_out=False,
            weight=weight,
            user=self.request.user
        )

        for player_data in players:            # Compute rank and sex using the original logic
            # Determine if this player is the captain
            is_captain = False
            player_licence_id = player_data.get('licence_id')
            if player_licence_id and stripped_license:
                if stripped_license.lower() in player_licence_id.lower():
                    is_captain = True

            # Determine data source
            data_source = None
            if player_data.get('found_in_french_federation', False) == True:
                data_source = PlayerDataSource.FRENCH_FEDERATION  # Now using the enum value

            User = get_user_model()
            if is_captain:
                matching_user = self.request.user
            else:
                matching_user = None
                if player_licence_id and (stripped_license is None or is_captain is False):
                    try:
                        # Using icontains for case-insensitive match
                        matching_user = User.objects.get(licence_id__icontains=player_licence_id)
                    except User.DoesNotExist:
                        pass

            is_woman = player_data.get('is_woman')
            if is_woman is not None:
                if is_woman:
                    sex = 0
                else:
                    sex = 1
            else:
                sex = None

            tournament_count = player_data.get('tournament_count', None)
            if tournament_count is not None:
                try:
                    tournament_played = int(tournament_count)
                except ValueError:
                    tournament_played = None
            else:
                tournament_played = None

            points = player_data.get('points', None)
            if points is not None:
                try:
                    points = float(points)
                except ValueError:
                    points = None
            else:
                points = None

            # Create player registration with all the original fields
            PlayerRegistration.objects.create(
                team_registration=team_registration,
                user=matching_user,
                captain=is_captain,
                source=data_source,
                registered_online=True,
                first_name=player_data.get('first_name'),
                last_name=player_data.get('last_name'),
                points=points,
                assimilation=player_data.get('assimilation'),
                tournament_played=tournament_played,
                ligue_name=player_data.get('ligue_name'),
                club_name=player_data.get('club_name'),
                club_code=player_data.get('club_code'),
                club_member=tournament.is_cart_player_from_club(player_data),
                birthdate=player_data.get('birth_year'),
                sex= sex,
                rank=player_data.get('rank'),
                computed_rank=player_data.get('computed_rank'),
                licence_id=player_data.get('licence_id'),
                email=matching_user.email if matching_user else player_data.get('email'),
                phone_number=matching_user.phone if matching_user else player_data.get('mobile_number'),
                registration_status=RegistrationStatus.CONFIRMED if self.session.get('waiting_list_position', 0) < 0 else RegistrationStatus.WAITING
            )

        # Clear the cart
        self.clear_cart()

        if confirmed:
            tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
            tournament.save()

        return True, team_registration

    def clear_cart(self):
        """Clear the registration cart"""
        keys_to_clear = [
            'registration_cart_id',
            'team_registration_id',
            'registration_tournament_id',
            'registration_cart_players',
            'registration_cart_expiry',
            'registration_mobile_number'
        ]

        for key in keys_to_clear:
            if key in self.session:
                del self.session[key]

        self.session.modified = True

    def _is_player_already_registered(self, stripped_license, tournament):
        """Check if a player is already registered in the tournament"""
        return PlayerRegistration.objects.filter(
            team_registration__tournament=tournament,
            licence_id__icontains=stripped_license,
            team_registration__walk_out=False
        ).exists()

    def add_authenticated_user(self):
        """
        Adds the authenticated user to the cart if they have a valid license.
        Returns True if added, False otherwise.
        """
        if not self.request.user.is_authenticated or not self.request.user.licence_id:
            return False

        # Create player data for the authenticated user
        player_data = {
            'first_name': self.request.user.first_name,
            'last_name': self.request.user.last_name,
            'licence_id': self.request.user.licence_id,
            'email': self.request.user.email,
            'phone': self.request.user.phone
        }

        # Add the user to the cart
        success, _ = self.add_player(player_data)
        return success

    def _compute_rank_and_sex(self, tournament, player_data):
        """
        Compute the player's sex, rank, and computed rank based on tournament category.
        This reimplements the original logic from TournamentRegistrationRepository.
        """
        is_woman = player_data.get('is_woman', False)
        rank = player_data.get('rank', None)

        if rank is None:
            rank_int = None
            computed_rank = 100000
        else:
            # Ensure rank is an integer for calculations
            try:
                rank_int = int(rank)
                computed_rank = rank_int
            except (ValueError, TypeError):
                # If rank can't be converted to int, set a default
                rank_int = None
                computed_rank = 100000

        # Use the integer enum values
        sex = PlayerSexType.FEMALE if is_woman else PlayerSexType.MALE

        # Apply assimilation for women playing in men's tournaments
        if is_woman and tournament.federal_category == FederalCategory.MEN and rank_int is not None:
            assimilation_addition = FederalCategory.female_in_male_assimilation_addition(rank_int, tournament.season_year())
            computed_rank = computed_rank + assimilation_addition

        print(f"_compute_rank_and_sex: {player_data.get('last_name')}, {sex}, {rank}, {computed_rank}")

        return sex, rank, str(computed_rank)
