# Standard library imports

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth import login, get_user_model, logout, update_session_auth_hash
from django.contrib.auth.views import PasswordResetCompleteView, PasswordResetConfirmView
from django.contrib.sites.shortcuts import get_current_site
from django.contrib.auth.decorators import login_required
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import redirect, render, get_object_or_404
from django.utils import timezone, formats
from django.utils.encoding import force_str, force_bytes
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.http import JsonResponse, HttpResponse, Http404
from django.urls import reverse, reverse_lazy
from django.conf import settings
from django.db.models import Q
from django.views.generic import View
from django.views.generic.edit import UpdateView
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
from django.core.exceptions import ValidationError
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile

import os
import csv
import locale
import calendar

from api.serializers import GroupStageSerializer, MatchSerializer, PlayerRegistrationSerializer, TeamRegistrationSerializer, TeamScoreSerializer
from .utils.extensions import is_not_sqlite_backend
import stripe

from .models import Club, Tournament, CustomUser, Event, Round, Match, TeamScore, TeamRegistration, PlayerRegistration, UserOrigin
from datetime import datetime, timedelta
import time
import json
import asyncio
import zipfile
import pandas as pd

from tournaments.utils.extensions import create_random_filename
from api.tokens import account_activation_token

from qr_code.qrcode.utils import QRCodeOptions

from .services.tournament_unregistration import TournamentUnregistrationService

from .forms import (
    ProfileUpdateForm,
    SimpleCustomUserCreationForm,
    SimpleForm,
    TournamentRegistrationForm,
    AddPlayerForm
)
from .utils.apns import send_push_notification
from .utils.licence_validator import LicenseValidator

from .forms import CustomPasswordChangeForm
from .services.email_service import TournamentEmailService
from .services.tournament_registration import RegistrationCartManager
from .services.payment_service import PaymentService

import logging

from .models import AnimationType

logger = logging.getLogger(__name__)

def index(request):
    now = timezone.now()
    thirty_days_ago = now - timedelta(days=30)
    thirty_days_future = now + timedelta(days=30)

    club_id = request.GET.get('club')
    if club_id:
        tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True, 50)
    else:
        tournaments = tournaments_query(Q(end_date__isnull=True, start_date__gte=thirty_days_ago, start_date__lte=thirty_days_future), club_id, True, 50)

    display_tournament = [t for t in tournaments if t.display_tournament()]
    live = []
    future = []
    for t in display_tournament:
        if t.supposedly_in_progress() and t.are_teams_positioned():
            live.append(t)
        elif t.starts_in_the_future():
            future.append(t)
    clean_ended_tournaments = tournaments_query(Q(end_date__isnull=False), club_id, False, 50)
    clean_ended_tournaments = [t for t in clean_ended_tournaments if t.display_tournament()]
    ended_tournaments = [t for t in display_tournament if t.should_be_over()]

    # Combine both lists
    finished = clean_ended_tournaments + ended_tournaments

    # Sort the combined list by start_date in descending order
    finished.sort(key=lambda t: t.sorting_finished_date(), reverse=True)

    club = None
    if club_id:
        club = get_object_or_404(Club, pk=club_id)

    return render(
        request,
        "tournaments/tournaments.html",
        {
            'future': future[:10],
            'live': live[:10],
            'ended': finished[:10],
            'club': club,
        }
    )

def tournaments_query(query, club_id, ascending, limit=None):
    queries = [query, Q(is_private=False, is_deleted=False, event__club__isnull=False)]

    club = None
    if club_id:
        club = get_object_or_404(Club, pk=club_id)
        q_club = Q(event__club=club)
        queries.append(q_club)
    else:
        q_animation_type = ~Q(animation_type=AnimationType.CUSTOM)
        queries.append(q_animation_type)

    sortkey = 'start_date'
    if not ascending:
        sortkey = '-start_date'

    queryset = Tournament.objects.filter(*queries).prefetch_related(
        'group_stages',
        'rounds',
        'team_registrations',
    ).order_by(sortkey)

    # Apply limit directly in the database query
    if limit is not None and isinstance(limit, int) and limit > 0:
        queryset = queryset[:limit]

    return queryset

def finished_tournaments(club_id, limit=None):
    clean_ended_tournaments = tournaments_query(Q(end_date__isnull=False), club_id, False, limit)
    clean_ended_tournaments = [t for t in clean_ended_tournaments if t.display_tournament()]

    one_day_ago = timezone.now() - timedelta(days=1)
    ended_tournaments = tournaments_query(
        Q(end_date__isnull=True, start_date__lt=one_day_ago),
        club_id,
        False,
        limit
    )
    ended_tournaments = [t for t in ended_tournaments if t.display_tournament() and t.should_be_over()]

    # Combine both lists
    all_tournaments = clean_ended_tournaments + ended_tournaments

    # Sort the combined list by start_date in descending order
    all_tournaments.sort(key=lambda t: t.start_date, reverse=True)

    return all_tournaments

def live_tournaments(club_id, limit=None):
    tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True, limit)
    return [t for t in tournaments if t.display_tournament() and t.supposedly_in_progress() and t.are_teams_positioned()]

def future_tournaments(club_id, limit=None):
    tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True, limit)
    return [t for t in tournaments if t.display_tournament() and t.starts_in_the_future()]

def future_tournaments_by_month(club_id, year, month):
    """Get future tournaments filtered by year and month"""

    logger.info(f'filter {year} {month}')
    tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True)
    filtered_tournaments = []
    for t in tournaments:
        if (t.display_tournament() and t.starts_in_the_future() and
            t.start_date.year == year and t.start_date.month == month + 1):
            filtered_tournaments.append(t)
    return filtered_tournaments

def finished_tournaments_by_month(club_id, year, month):
    """Get finished tournaments filtered by year and month"""
    clean_ended_tournaments = tournaments_query(Q(end_date__isnull=False), club_id, False)
    clean_ended_tournaments = [t for t in clean_ended_tournaments if t.display_tournament()]

    one_day_ago = timezone.now() - timedelta(days=1)
    ended_tournaments = tournaments_query(
        Q(end_date__isnull=True, start_date__lt=one_day_ago),
        club_id,
        False
    )
    ended_tournaments = [t for t in ended_tournaments if t.display_tournament() and t.should_be_over()]

    # Combine both lists
    all_tournaments = clean_ended_tournaments + ended_tournaments

    # Filter by year and month
    filtered_tournaments = []
    for t in all_tournaments:
        if t.start_date.year == year and t.start_date.month == month:
            filtered_tournaments.append(t)

    # Sort by start_date descending
    filtered_tournaments.sort(key=lambda t: t.start_date, reverse=True)
    return filtered_tournaments

def get_available_years_for_tournaments(club_id, filter_type):
    """Get available years for tournaments based on filter type"""
    if filter_type == 0:  # Future tournaments
        tournaments = tournaments_query(Q(end_date__isnull=True), club_id, True)
        tournaments = [t for t in tournaments if t.display_tournament() and t.starts_in_the_future()]
    elif filter_type == 2:  # Finished tournaments
        tournaments = finished_tournaments(club_id)
    else:
        return []

    years = set()
    for t in tournaments:
        years.add(t.start_date.year)

    return sorted(years, reverse=(filter_type == 2))

def tournament_info(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    registered_user = None
    team_registration = None
    is_captain = False
    player_register_check = None
    if request.method == 'POST':
        storage = messages.get_messages(request)
        for _ in storage:
            pass

        if len(storage._loaded_messages) == 1:
            del storage._loaded_messages[0]

    if request.user.is_authenticated:
        # Assuming user's licence_id is stored in the user profile (e.g., request.user.licence_id)
        user_licence_id = request.user.licence_id
        player_register_check = tournament.player_register_check(user_licence_id)
        registered_user = tournament.get_user_registered(request.user)
        # If the user is registered, retrieve their team registration
        if registered_user:
            is_captain = registered_user.captain
            team_registration = registered_user.team_registration

    return render(request, 'tournaments/tournament_info.html', {
        'tournament': tournament,
        'team': team_registration,
        'is_captain': is_captain,
        'player_register_check': player_register_check
    })


def tournaments(request):
    filter_param = request.GET.get('filter')
    filter = None

    if filter_param:
        try:
            filter = int(filter_param)
            if filter not in [0, 1, 2]:  # Valid filter values
                return redirect('/')

        except:
            return redirect('/')

    club_id = request.GET.get('club')
    year_param = request.GET.get('year')
    month_param = request.GET.get('month')

    title = ''
    tournaments = []
    available_years = []
    selected_year = None
    if month_param:
        selected_month = int(month_param)
    else:
        selected_month = timezone.now().month - 1

    if filter == 0:
        title = 'À venir'
        available_years, selected_year = handle_year_month_filtering(year_param, club_id, filter, is_future=True)

        if selected_year and selected_month:
            tournaments = future_tournaments_by_month(club_id, selected_year, selected_month)
        elif selected_year:
            tournaments = future_tournaments(club_id)
        else:
            tournaments = future_tournaments(club_id)

    elif filter == 1:
        title = 'En cours'
        tournaments = live_tournaments(club_id)

    elif filter == 2:
        title = 'Terminés'
        available_years, selected_year = handle_year_month_filtering(year_param, club_id, filter, is_future=False)

        if selected_year and selected_month:
            tournaments = finished_tournaments_by_month(club_id, selected_year, selected_month)
        elif selected_year:
            tournaments = finished_tournaments(club_id)
        else:
            tournaments = finished_tournaments(club_id)

    locale.setlocale(locale.LC_TIME, 'fr_FR.UTF-8')  # Define month names in french

    return render(
        request,
        "tournaments/tournaments_list.html",
        {
            'tournaments': tournaments,
            'first_title': "Tournois",
            'second_title': title,
            'head_title': title,
            'club': club_id,
            'filter': filter,
            'available_years': available_years,
            'available_months': range(0, 12),
            'selected_year': selected_year,
            'selected_month': selected_month,
            'month_names': list(calendar.month_name)[1:],
            'year': year_param,
            'month': month_param,
        }
    )

def handle_year_month_filtering(year_param, club_id, filter_type, is_future=True):
    """Helper function to handle year/month filtering for future and finished tournaments"""
    available_years = get_available_years_for_tournaments(club_id, filter_type)
    selected_year = None

    # Handle year selection
    current_year = timezone.now().year
    if not year_param and available_years:
        if is_future:
            selected_year = current_year if current_year in available_years else available_years[0]
        else:
            selected_year = available_years[0]  # Most recent year for finished tournaments
    elif year_param:
        try:
            selected_year = int(year_param)
            if selected_year not in available_years:
                selected_year = available_years[0] if available_years else None
        except:
            selected_year = available_years[0] if available_years else None

    return available_years, selected_year

def clubs(request):
    # all_clubs = Club.objects.all().order_by('name')
    # clubs = []
    # for club in all_clubs:
    #     if club.events_count() > 0:
    #         clubs.append(club)

    clubs = Club.objects.filter(
        Q(admin_visible=True) | Q(
            events__tournaments__end_date__isnull=False,
            events__tournaments__is_private=False
        )
    ).distinct().order_by('name')

    return render(request, 'tournaments/clubs.html', {
        'clubs': clubs,
    })

def club(request, club_id):
    club = get_object_or_404(Club, pk=club_id)
    return render(request, 'tournaments/summons.html', {
        'tournament': tournament,
        'team_summons': team_summons,
    })

def event(request, event_id):
    event = get_object_or_404(Event, pk=event_id)
    tournaments = event.tournaments.all().order_by('start_date')

    # Get the first tournament for the prog link
    first_tournament_prog_url = None
    if tournaments.exists():
        first_tournament = tournaments.first()
        if first_tournament.display_prog():
            first_tournament_prog_url = reverse('tournament-prog', kwargs={'tournament_id': first_tournament.id})

    if event.name and len(event.name) > 0:
        name = event.name
    else:
        name = 'Événement'

    return render(
        request,
        "tournaments/tournaments_list.html",
        {
            'tournaments': tournaments,
            'first_title': event.club.name,
            'second_title': name,
            'head_title': name,
            'first_tournament_prog_url': first_tournament_prog_url,
        }
    )

def tournament(request, tournament_id):

    tournament = get_object_or_404(
        Tournament.objects.select_related('event')
        .prefetch_related(
            'rounds',
            'rounds__children',
            'rounds__matches',
            'rounds__matches__team_scores',
            'rounds__matches__team_scores__team_registration',
            'rounds__matches__team_scores__team_registration__player_registrations',
            'group_stages__matches__team_scores__team_registration__player_registrations',
            'group_stages__matches__team_scores__team_registration',
            'group_stages__matches__team_scores',
            'group_stages__matches',
            'group_stages',
            'rounds__children__matches__team_scores__team_registration__player_registrations',
            'rounds__children__matches__team_scores__team_registration',
            'rounds__children__matches__team_scores',
            'rounds__children__matches'
        ),
        pk=tournament_id
    )

    round_id = request.GET.get('round')
    group_stage_id = request.GET.get('group_stage')

    # Cache rounds and group stages to avoid repeated queries
    all_rounds = list(tournament.rounds.all())
    all_group_stages = list(tournament.group_stages.all())

    # Filter rounds efficiently
    bracket_rounds = [r for r in all_rounds if r.parent is None and not r.group_stage_loser_bracket]
    loser_bracket_rounds = [r for r in all_rounds if r.group_stage_loser_bracket]

    # Sort rounds by index (descending for bracket_rounds)
    bracket_rounds.sort(key=lambda r: r.index, reverse=True)
    loser_bracket_rounds.sort(key=lambda r: r.index)

    rounds = list(loser_bracket_rounds)
    rounds.extend(bracket_rounds)

    # Optimize group stage filtering with step completion caching
    all_group_stages.sort(key=lambda gs: (-gs.step, gs.index))
    filtered_group_stages = []
    steps_completed = {}

    for group_stage in all_group_stages:
        if group_stage.step == 0:
            filtered_group_stages.append(group_stage)
        elif group_stage.step > 0:
            prev_step = group_stage.step - 1
            if prev_step not in steps_completed:
                prev_step_stages = [gs for gs in all_group_stages if gs.step == prev_step]
                steps_completed[prev_step] = prev_step_stages and all(gs.is_completed() for gs in prev_step_stages)

            if steps_completed[prev_step]:
                filtered_group_stages.append(group_stage)

    group_stages = sorted(filtered_group_stages, key=lambda s: (s.step, s.index))

    # Get match groups with optimized data
    match_groups = tournament.match_groups(False, group_stage_id, round_id)

    if tournament.display_matches() or tournament.display_group_stages():
        return render(request, 'tournaments/matches.html', {
            'tournament': tournament,
            'rounds': rounds,
            'group_stages': group_stages,
            'match_groups': match_groups,
        })
    else:
        return tournament_info(request, tournament_id)

def tournament_teams(request, tournament_id):

    tournament = get_object_or_404(Tournament, pk=tournament_id)
    teams = tournament.teams(True)
    selected_teams = [team for team in teams if team.stage != "Attente"]
    waiting_teams = [team for team in teams if team.stage == "Attente"]

    return render(request, 'tournaments/teams.html', {
        'tournament': tournament,
        'selected_teams': selected_teams,
        'waiting_teams': waiting_teams,
    })

def tournament_summons(request, tournament_id):

    tournament = get_object_or_404(Tournament, pk=tournament_id)
    team_summons = tournament.team_summons()

    return render(request, 'tournaments/summons.html', {
        'tournament': tournament,
        'team_summons': team_summons,
    })

def tournament_broadcasted_summons(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    return render(request, 'tournaments/broadcast/broadcasted_summons.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })

def tournament_summons_json(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    team_summons = [summon.to_dict() for summon in tournament.team_summons()]
    data = json.dumps(team_summons)
    return HttpResponse(data, content_type='application/json')

def tournament_broadcast_home(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    return render(request, 'tournaments/broadcast/broadcast.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })

def automatic_broadcast(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    response = render(request, 'tournaments/broadcast/broadcasted_auto.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })
    # Allow iframe embedding from same origin
    response['X-Frame-Options'] = 'SAMEORIGIN'
    return response

def automatic_broadcast_event(request, event_id):
    event = get_object_or_404(Event, pk=event_id)
    now = timezone.now()

    # Get all tournaments for the event (expanded time range for better coverage)
    past_cutoff = now - timedelta(hours=72)  # Extended past search
    future_cutoff = now + timedelta(hours=72)  # Extended future search

    all_tournaments = Tournament.objects.filter(
        event=event,
        start_date__gte=past_cutoff,
        start_date__lte=future_cutoff
    ).order_by('start_date')

    # Categorize tournaments by priority
    ongoing_tournaments = []
    starting_soon_tournaments = []
    other_tournaments_with_content = []

    for tournament in all_tournaments:
        try:
            # First check if tournament has broadcast content
            content = tournament.broadcast_content()
            has_content = (
                len(content.get('matches', [])) > 0 or
                len(content.get('group_stages', [])) > 0 or
                len(content.get('summons', [])) > 0 or
                len(content.get('rankings', [])) > 0
            )

            if has_content:
                # Categorize based on match activity (highest priority first)
                if tournament.has_ongoing_matches():
                    ongoing_tournaments.append(tournament)
                elif tournament.has_matches_starting_soon():
                    starting_soon_tournaments.append(tournament)
                else:
                    other_tournaments_with_content.append(tournament)
        except Exception as e:
            print(f"Error processing tournament {tournament.id}: {e}")
            continue

    # Sort each category by next match start time and tournament start time
    def sort_by_next_match_time(tournament):
        next_match_time = tournament.get_next_match_start_time()
        return (
            next_match_time if next_match_time else tournament.start_date,
            tournament.start_date
        )

    # Sort ongoing tournaments by next match time (for consistency)
    ongoing_tournaments.sort(key=sort_by_next_match_time)

    # Sort starting soon tournaments by next match time (earliest first)
    starting_soon_tournaments.sort(key=sort_by_next_match_time)

    # Sort other tournaments by tournament start time
    other_tournaments_with_content.sort(key=lambda t: t.start_date)

    # Build final list prioritizing ongoing matches above all else
    tournaments_to_display = []

    # Priority 1: Tournaments with ongoing matches (take max 2)
    tournaments_to_display.extend(ongoing_tournaments[:2])

    # If we need more tournaments and have space
    if len(tournaments_to_display) < 2:
        remaining_slots = 2 - len(tournaments_to_display)

        # Priority 2: Tournaments with matches starting soon
        tournaments_to_display.extend(starting_soon_tournaments[:remaining_slots])

        # If still need more tournaments
        if len(tournaments_to_display) < 2:
            remaining_slots = 2 - len(tournaments_to_display)

            # Priority 3: Other tournaments with content
            tournaments_to_display.extend(other_tournaments_with_content[:remaining_slots])

    # Fallback: If still don't have enough tournaments, expand search
    if len(tournaments_to_display) < 2:
        # Get tournaments from wider time range
        extended_tournaments = Tournament.objects.filter(
            event=event
        ).exclude(
            id__in=[t.id for t in tournaments_to_display]
        ).order_by('-start_date')  # Most recent first

        remaining_needed = 2 - len(tournaments_to_display)
        for tournament in extended_tournaments[:remaining_needed]:
            try:
                # Check if tournament has any content
                content = tournament.broadcast_content()
                has_content = (
                    len(content.get('matches', [])) > 0 or
                    len(content.get('group_stages', [])) > 0 or
                    len(content.get('summons', [])) > 0 or
                    len(content.get('rankings', [])) > 0
                )
                if has_content:
                    tournaments_to_display.append(tournament)
            except:
                # If no content, add anyway as last resort
                tournaments_to_display.append(tournament)

    # Ensure we have exactly what we need (limit to 2)
    tournaments_to_display = tournaments_to_display[:2]

    # Log the prioritization for debugging
    print(f"Event {event_id} broadcast prioritization:")
    print(f"  - Ongoing tournaments: {len(ongoing_tournaments)}")
    print(f"  - Starting soon tournaments: {len(starting_soon_tournaments)}")
    print(f"  - Other tournaments with content: {len(other_tournaments_with_content)}")
    print(f"  - Final selection: {[str(t.id) for t in tournaments_to_display]}")

    tournament_ids = [str(tournament.id) for tournament in tournaments_to_display]

    # Create QR code URL for the event broadcast
    qr_code_url_val = reverse('automatic-broadcast-event', args=[event_id])
    qr_code_url_full = request.build_absolute_uri(qr_code_url_val)

    return render(request, 'tournaments/broadcast/broadcasted_event_auto.html', {
        'tournament_ids': tournament_ids,
        'event': event,
        'qr_code_url': qr_code_url_full,
        'qr_code_options': qr_code_options(),
    })

def qr_code_url(request, tournament_id):
    qr_code_url = reverse('tournament', args=[tournament_id])
    return request.build_absolute_uri(qr_code_url)

def qr_code_url_with_query(request, club_id):
    url_with_query = f"{request.build_absolute_uri('/')}?club={str(club_id)}"
    return url_with_query

def qr_code_options():
    return QRCodeOptions(size=8, border=4, error_correction='L')

def tournament_matches(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    return render(request, 'tournaments/broadcast/broadcasted_matches.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })

def broadcast_json(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    broadcast = tournament.broadcast_content()
    data = json.dumps(broadcast)
    return HttpResponse(data, content_type='application/json')

def tournament_matches_json(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    matches, group_stages = tournament.broadcasted_matches_and_group_stages()

    live_matches_dicts = [match.live_match().to_dict() for match in matches]
    data = json.dumps(live_matches_dicts, default=vars)
    return HttpResponse(data, content_type='application/json')

def tournament_prog_json(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    matches = tournament.broadcasted_prog()

    # Convert matches to JSON-serializable format, handling None/empty slots
    live_matches = []
    for match in matches:
        if match is None or isinstance(match, dict) and match.get('empty'):
            live_matches.append({"empty": True})
        else:
            live_matches.append(match.live_match().to_dict())

    data = json.dumps(live_matches, default=vars)
    return HttpResponse(data, content_type='application/json')

def tournament_planning_json(request, tournament_id):
    tournament = get_object_or_404(Tournament, id=tournament_id)
    day_param = request.GET.get('day', None)

    # Get days and match groups using the planned_matches_by_day method
    days, match_groups = tournament.planned_matches_by_day(day=day_param, all=True, event_mode=True, broadcast=True)

    # Format data for JSON response
    formatted_days = [formats.date_format(day, format='l j F').capitalize() for day in days]

    # Convert match groups to JSON-serializable format
    match_groups_data = []
    for match_group in match_groups:
        if not hasattr(match_group, 'matches') or not match_group.matches:
            continue
        live_matches = []
        for match in match_group.matches:
            live_matches.append(match.to_dict())

        match_groups_data.append({
            'name': match_group.name,
            'matches': live_matches
        })

    response_data = {
        'days': formatted_days,
        'match_groups': match_groups_data,
    }

    return JsonResponse(response_data, safe=False)

def tournament_group_stages(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    live_group_stages = list(tournament.live_group_stages())
    unique_steps = sorted(set(gs.step for gs in live_group_stages), reverse=True)

    return render(request, 'tournaments/group_stages.html', {
        'tournament': tournament,
        'group_stages': live_group_stages,
        'unique_steps': unique_steps,
    })

def tournament_broadcasted_group_stages(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    return render(request, 'tournaments/broadcast/broadcasted_group_stages.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })

def tournament_broadcasted_prog(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    return render(request, 'tournaments/broadcast/broadcasted_prog.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })

def tournament_broadcasted_bracket(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    return render(request, 'tournaments/broadcast/broadcasted_bracket.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })

def tournament_broadcasted_planning(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)

    return render(request, 'tournaments/broadcast/broadcasted_planning.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })


def tournament_bracket_json(request, tournament_id):
    """
    JSON endpoint for tournament bracket data.
    """
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    context = get_butterfly_bracket_view_context(tournament, parent_round=None, double_butterfly_mode=True, display_loser_final=True)

    data = {
        'match_groups': [match_group.to_dict() for match_group in context['match_groups']],
        'double_butterfly_mode': context['double_butterfly_mode'],
        'display_loser_final': context['display_loser_final']
    }

    return JsonResponse(data)


def tournament_live_group_stage_json(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)

    # Get all live group stages and filter for the last running step
    live_group_stages = [gs.live_group_stages() for gs in tournament.last_group_stage_step()]

    gs_dicts = [gs.to_dict() for gs in live_group_stages]
    data = json.dumps(gs_dicts)
    return HttpResponse(data, content_type='application/json')

def tournament_rankings(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    rankings = tournament.rankings()

    return render(request, 'tournaments/rankings.html', {
        'tournament': tournament,
        'rankings': rankings,
    })

def tournament_rankings_json(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    rankings = tournament.rankings()

    data = json.dumps(rankings, default=vars)
    return HttpResponse(data, content_type='application/json')

def tournament_broadcast_rankings(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    return render(request, 'tournaments/broadcast/broadcasted_rankings.html', {
        'tournament': tournament,
        'qr_code_url': qr_code_url(request, tournament_id),
        'qr_code_options': qr_code_options(),
    })

def players(request):
    return render(request, 'tournaments/test.html')

def activate(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = CustomUser.objects.get(pk=uid)
    except(TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
        user = None
    # if user is not None and account_activation_token.check_token(user, token):
    # removed for Philippe Morin Nouvelle Calédonie / Serge Dion user / il faut remettre
    if user is not None:

        token_valid = account_activation_token.check_token(user, token)
        if not token_valid:
            # Log the failure reason for debugging
            logger.warning(f"Token validation would have failed for user {user.username}")
            print(f"Token validation would have failed for user {user.username}")
        user.is_active = True
        user.save()

        # Specify the authentication backend when logging in
        login(request, user, backend='django.contrib.auth.backends.ModelBackend')

        return redirect('activation_success')
    else:
        # Redirect to a custom error page
        return redirect('activation_failed')  # Define this URL in your urls.py

def activation_success(request):
    return render(request, 'registration/activation_success.html')

def activation_failed(request):
    return render(request, 'registration/activation_failed.html')

def club_broadcast(request, broadcast_code):
    club = get_object_or_404(Club, broadcast_code=broadcast_code)
    q_not_deleted = Q(is_deleted=False, event__club=club)
    tournaments = Tournament.objects.filter(q_not_deleted).order_by('-start_date')

    return render(request, 'tournaments/broadcast/broadcast_club.html', {
        'club': club,
        'tournaments': tournaments,
    })

def club_broadcast_auto(request, broadcast_code):
    club = get_object_or_404(Club, broadcast_code=broadcast_code)
    q_not_deleted = Q(is_deleted=False, event__club=club, is_private=False)
    now = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)

    # Primary selection: Active tournaments (current logic)
    recent_time_window = now - timedelta(hours=4)
    active_tournaments = Tournament.objects.filter(
        q_not_deleted,
        (
            (
                Q(end_date__isnull=True) &  # Include ongoing tournaments
                (Q(start_date__gte=now - timedelta(days=3)) & Q(start_date__lt=now + timedelta(days=3)))
            ) |
            (Q(end_date__isnull=False) & Q(end_date__gte=recent_time_window))  # Finished within 4 hours
        )
    ).select_related('event').distinct()

    # Smart fallback: If we have few active tournaments, add recent/upcoming ones
    tournaments_set = set()
    tournaments_list = []

    # Add active tournaments first
    for tournament in active_tournaments:
        if tournament.id not in tournaments_set:
            tournaments_set.add(tournament.id)
            tournaments_list.append(tournament)

    # If we have less than 3 tournaments, add recent finished tournaments
    if len(tournaments_list) < 3:
        recent_finished = Tournament.objects.filter(
            q_not_deleted,
            Q(end_date__isnull=False),
            Q(end_date__gte=now - timedelta(days=7))  # Finished within last 7 days
        ).select_related('event').order_by('-end_date')[:3]

        for tournament in recent_finished:
            if tournament.id not in tournaments_set and len(tournaments_list) < 5:
                tournaments_set.add(tournament.id)
                tournaments_list.append(tournament)

    # If we still have less than 3 tournaments, add upcoming tournaments
    if len(tournaments_list) < 3:
        upcoming = Tournament.objects.filter(
            q_not_deleted,
            Q(start_date__gt=now + timedelta(days=3)),  # Starting more than 3 days from now
            Q(start_date__lte=now + timedelta(days=30))  # But within next 30 days
        ).select_related('event').order_by('start_date')[:3]

        for tournament in upcoming:
            if tournament.id not in tournaments_set and len(tournaments_list) < 5:
                tournaments_set.add(tournament.id)
                tournaments_list.append(tournament)

    # Filter tournaments that have broadcast content (or at least some basic info to show)
    tournaments_with_content = []
    for tournament in tournaments_list:
        try:
            content = tournament.broadcast_content()
            has_content = (
                len(content.get('matches', [])) > 0 or
                len(content.get('group_stages', [])) > 0 or
                len(content.get('summons', [])) > 0 or
                len(content.get('rankings', [])) > 0
            )

            # For upcoming tournaments, show them even without content (they'll show basic info)
            is_upcoming = tournament.start_date > now + timedelta(days=3)
            is_recent_finished = tournament.end_date and tournament.end_date >= now - timedelta(days=7)

            if has_content or is_upcoming or is_recent_finished:
                tournaments_with_content.append(tournament)
        except:
            # For tournaments that error, still include if they're upcoming/recent
            is_upcoming = tournament.start_date > now + timedelta(days=3)
            is_recent_finished = tournament.end_date and tournament.end_date >= now - timedelta(days=7)
            if is_upcoming or is_recent_finished:
                tournaments_with_content.append(tournament)

    # Sort tournaments: Active first, then by start date
    def tournament_priority(t):
        now_time = timezone.now()
        if t.end_date is None and t.start_date <= now_time + timedelta(days=3):
            return (0, t.start_date)  # Active tournaments first
        elif t.end_date and t.end_date >= now_time - timedelta(hours=4):
            return (1, -t.end_date.timestamp())  # Recently finished, newest first
        elif t.start_date > now_time:
            return (2, t.start_date)  # Upcoming tournaments by start date
        else:
            return (3, -t.end_date.timestamp() if t.end_date else 0)  # Other finished tournaments

    tournaments_with_content.sort(key=tournament_priority)

    tournament_ids = [str(tournament.id) for tournament in tournaments_with_content]

    print(f"Club '{club.name}' auto broadcast:")
    print(f"Found {len(active_tournaments)} active, total {len(tournaments_with_content)} tournaments to display")
    for i, tournament in enumerate(tournaments_with_content):
        status = "ACTIVE" if (tournament.end_date is None and tournament.start_date <= timezone.now() + timedelta(days=3)) else \
                "RECENT" if (tournament.end_date and tournament.end_date >= timezone.now() - timedelta(days=7)) else \
                "UPCOMING" if tournament.start_date > timezone.now() else "FINISHED"
        # Fix: Convert UUID to string before slicing
        tournament_id_short = str(tournament.id)[:8]
        print(f"  {i+1}. {tournament_id_short} ({tournament.event.display_name()}) - {tournament.name or 'Unnamed'} [{status}]")

    return render(request, 'tournaments/broadcast/broadcasted_club_auto.html', {
        'tournament_ids': tournament_ids,
        'club_name': club.name,
    })


def download(request):
    return render(request, 'tournaments/download.html')

def test_apns(request):

    user = CustomUser.objects.filter(username='laurent').first()
    for device_token in user.device_tokens.all():
        asyncio.run(send_push_notification(device_token.value, 'LOL?!'))

    # token = DeviceToken.objects.first()

    return HttpResponse('OK!')

def test_websocket(request):
    return render(request, 'tournaments/test_websocket.html')

def terms_of_use(request):
    return render(request, 'terms_of_use.html')

def simple_form_view(request):

    if request.method == 'POST':
        # If this is a POST request, we need to process the form data
        form = SimpleForm(request.POST)
        if form.is_valid():
            # Process the data in form.cleaned_data
            name = form.cleaned_data['name']
            # Do something with the data (e.g., save to the database)
            send_email_to_jap_list()
            # send_email('laurent@staxriver.com', 'Laurent')
            return HttpResponse(f"Hello, {name}!")  # Simple response with name
    else:
        form = SimpleForm()
        # print(request.method)
        # If this is a GET request, we display an empty form

    return render(request, 'tournaments/admin/mail_test.html', {'form': form})

def send_email_to_jap_list():

    # Open the CSV file
    with open('tournaments/static/misc/jap-test.csv', 'r') as file:
        csv_reader = csv.reader(file)

        def log_error(message):
            with open('email_errors.log', 'a') as log_file:
                timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                log_file.write(f"{timestamp}: {message}\n")

        batch = []
        for row in csv_reader:
            if len(row) >= 4:
                name = row[2]
                mail = row[3]
                batch.append((mail, name))

                if len(batch) == 10:
                    for email, recipient_name in batch:
                        try:
                            send_email(email, recipient_name)
                            print(f"Name: {recipient_name}, mail: {email}")
                        except Exception as e:
                            error_message = f"Error sending email to {email}: {str(e)}"
                            log_error(error_message)
                            print(error_message)

                    batch = []
                    time.sleep(2)
            else:
                print(f"Row doesn't have enough elements: {row}")

        # Send any remaining emails in the last batch
        for email, recipient_name in batch:
            try:
                send_email(email, recipient_name)
                print(f"Name: {recipient_name}, mail: {email}")
            except Exception as e:
                error_message = f"Error sending email to {email}: {str(e)}"
                log_error(error_message)
                print(error_message)

def send_email(mail, name):
    if name.lower() not in mail.lower():
        name = ""

    subject = "Tes tournois en toute simplicité avec Padel Club"
    body = f"Salut {name} !\n\nJe me permets de t'écrire car je suis JAP2 en région PACA et développeur, et je viens de lancer Padel Club, une app iOS qui facilite enfin l'organisation des tournois.\n\nAvec elle, tu peux convoquer rapidement, simuler et programmer tes structures, diffuser tous les résultats à tous les joueurs, et ce depuis un iPhone.\n\nTu peux l'essayer gratuitement pour découvrir tout son potentiel ! Télécharge l'app ici et teste la dès ton prochain tournoi: https://padelclub.app/download/\n\nJe suis disponible pour échanger avec toi par mail ou téléphone au 06 81 59 81 93 et voir ce que tu en penses.\nÀ bientôt j'espère !\n\nRazmig"

    email = EmailMessage(subject, body, to=[mail])
    email.send()

def signup(request):
    next_url = request.GET.get('next', '/')

    if request.method == 'POST':
        form = SimpleCustomUserCreationForm(request.POST)
        if form.is_valid():
            user = form.save(commit=False)
            user.is_active = False
            user.origin = UserOrigin.SITE
            user.save()

            send_verification_email(request, user, next_url)

            return render(request, 'registration/signup_success.html', {
                'next_url': next_url,
                'user_email': user.email  # Add the user's email to the context
            })
    else:
        form = SimpleCustomUserCreationForm()

    response = render(request, 'registration/signup.html', {'form': form})
    return response

def send_verification_email(request, user, next_url):
    print('next_url', next_url)

    current_site = get_current_site(request)
    mail_subject = 'Activez votre compte Padel Club !'
    # Prepare the email subject and message
    message = render_to_string('tournaments/acc_active_email.html', {
        'user': user,
        'domain': current_site.domain,
        'uid': urlsafe_base64_encode(force_bytes(user.pk)),
        'token': account_activation_token.make_token(user),
        'next': next_url,  # Pass next URL to the template
    })

    email = EmailMessage(mail_subject, message, to=[user.email])
    email.content_subtype = "html"
    email.send()

def resend_activation_email(request):
    """View to resend activation email for inactive users."""
    if request.method == 'POST':
        username_or_email = request.POST.get('username_or_email', '').strip()

        if not username_or_email:
            messages.error(request, 'Veuillez fournir un nom d\'utilisateur ou un e-mail.')
            return redirect('custom-login')

        # Try to find the user
        user = None
        try:
            # Try by username first
            user = CustomUser.objects.get(username__iexact=username_or_email)
        except CustomUser.DoesNotExist:
            try:
                # Try by email
                user = CustomUser.objects.get(email__iexact=username_or_email)
            except CustomUser.DoesNotExist:
                messages.error(request, 'Aucun compte trouvé avec cet identifiant.')
                return redirect('custom-login')

        # Check if user is already active
        if user.is_active:
            messages.info(request, 'Votre compte est déjà activé. Vous pouvez vous connecter.')
            return redirect('custom-login')

        # Send the activation email
        next_url = request.POST.get('next', '')
        send_verification_email(request, user, next_url)

        messages.success(request, f'Un nouveau lien d\'activation a été envoyé à {user.email}.')
        return redirect('custom-login')

    # If GET request, redirect to login
    return redirect('custom-login')

@login_required
def profile(request):
    user = request.user  # Get the currently authenticated user
    return render(request, 'registration/profile.html', {
        'user_name': user.username
    })

@login_required
def unregister_tournament(request, tournament_id):
    tournament = get_object_or_404(Tournament, id=tournament_id)
    service = TournamentUnregistrationService(request, tournament)

    if not service.can_unregister():
        return redirect('tournament-info', tournament_id=tournament_id)

    service.process_unregistration()
    return redirect('tournament-info', tournament_id=tournament_id)

class CustomPasswordResetConfirmView(PasswordResetConfirmView):
    template_name = 'registration/password_reset_confirm.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['custom_message'] = "Veuillez entrer un nouveau mot de passe."
        return context

    def form_valid(self, form):
        response = super().form_valid(form)
        return response

    def get_user(self, uidb64):
        """
        Override this method to decode the uid and return the corresponding user.
        """
        try:
            # Use get_user_model() instead of direct User import
            User = get_user_model()

            uid = urlsafe_base64_decode(uidb64).decode()
            user = User.objects.get(pk=uid)
            return user
        except (TypeError, ValueError, User.DoesNotExist):
            raise Http404("User not found")

class CustomPasswordResetCompleteView(PasswordResetCompleteView):
    template_name = 'registration/password_reset_complete.html'

    def get(self, request, *args, **kwargs):
        # Get the user from the session
        username = request.session.get('reset_username')

        if username:
            try:
                # Get the user
                User = get_user_model()
                user = User.objects.get(username=username)

                # Log the user in
                login(request, user, backend='django.contrib.auth.backends.ModelBackend')

                # Clean up the session
                if 'reset_username' in request.session:
                    del request.session['reset_username']

                # Redirect to the profile page
                return redirect('profile')
            except User.DoesNotExist:
                pass

        # If no username in session or user not found, proceed with normal view
        return super().get(request, *args, **kwargs)

@login_required
def custom_password_change(request):
    if request.method == 'POST':
        form = CustomPasswordChangeForm(user=request.user, data=request.POST)
        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)  # Important to keep user logged in
            messages.success(request, 'Votre mot de passe a été mis à jour avec succès!')
            return redirect('profile')
        else:
            # Form is invalid, show errors
            profile_form = ProfileUpdateForm(instance=request.user)
            return render(request, 'profile.html', {
                'form': profile_form,
                'password_change_form': form
            })

    # If not POST, redirect to profile page
    return redirect('profile')

@login_required
def my_tournaments(request):
    # Get filtered tournaments using the helper function
    upcoming_tournaments = get_user_tournaments(request.user, future_tournaments(None))
    running_tournaments = get_user_tournaments(request.user, live_tournaments(None))
    ended_tournaments = get_user_tournaments(request.user, [t for t in finished_tournaments(None) if not t.is_canceled()])

    return render(request, 'registration/my_tournaments.html', {
        'upcoming_tournaments': upcoming_tournaments,
        'running_tournaments': running_tournaments,
        'ended_tournaments': ended_tournaments[:12],
        'user_name': request.user.username
    })

@login_required
def all_my_ended_tournaments(request):
    ended_tournaments = get_user_tournaments(request.user, finished_tournaments(None))
    return render(request,
    "tournaments/tournaments_list.html",
    {
        'tournaments': ended_tournaments,
        'first_title': "Palmarès",
        'second_title': f"{request.user.first_name } { request.user.last_name }",
        'head_title': "Palmarès",
    })

class ProfileUpdateView(LoginRequiredMixin, UpdateView):
    model = CustomUser
    form_class = ProfileUpdateForm
    template_name = 'profile.html'
    success_url = reverse_lazy('profile')
    login_url = '/login/'  # Specify where to redirect if user is not logged in

    def get_object(self, queryset=None):
        return self.request.user

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['password_change_form'] = CustomPasswordChangeForm(user=self.request.user)
        return context

@staff_member_required
def tournament_import_view(request):
    if request.method == 'POST':
        zip_file = request.FILES.get('tournament_zip')
        if zip_file:
            print('yes')

            try:
                tournament_id = os.path.splitext(zip_file.name)[0]
                tournament = Tournament.objects.get(id=tournament_id)

                # Delete existing relationships
                print('round delete')

                rounds = Round.objects.filter(tournament=tournament,parent=None)
                for round in rounds:
                    round.delete_dependencies()
                for gs in tournament.group_stages.all():
                    gs.delete_dependencies()
                for tr in tournament.team_registrations.all():
                    tr.delete_dependencies()

                # with transaction.atomic():
                #     for round in tournament.rounds.all():
                #         round.delete()
                tournament.rounds.all().delete()
                tournament.group_stages.all().delete()
                tournament.team_registrations.all().delete()
                # tournament.player_registrations.all().delete()
                # tournament.matches.all().delete()
                # tournament.team_scores.all().delete()

                print(f'round count = {tournament.rounds.count()}')
                print(f'group_stages count = {tournament.group_stages.count()}')
                print(f'team_registrations count = {tournament.team_registrations.count()}')

                print(f'PR count = {PlayerRegistration.objects.count()}')
                print(f'TS count = {TeamScore.objects.count()}')
                print(f'M count = {Match.objects.count()}')

                with zipfile.ZipFile(zip_file) as z:
                    # First, process rounds
                    rounds_data = get_file_data(z, f"{tournament_id}/rounds.json")
                    # print(f'rounds count = {len(rounds_data)}')

                    if rounds_data:
                        # First pass: Create rounds with preserved UUIDs
                        for item in rounds_data:
                            # print('round #1 create')
                            item['tournament'] = tournament.id
                            round_id = item['id']  # Preserve the original UUID
                            Round.objects.create(
                                id=round_id,
                                tournament_id=tournament.id,
                                index=item['index'],
                                format=item.get('format'),
                                start_date=item.get('start_date'),
                                group_stage_loser_bracket=item.get('group_stage_loser_bracket', False),
                                loser_bracket_mode=item.get('loser_bracket_mode', 0)
                            )

                        # Second pass: Set parent relationships
                        for item in rounds_data:
                            # print('round #2 set parent')

                            if item.get('parent'):
                                round_obj = Round.objects.get(id=item['id'])
                                round_obj.parent_id = item['parent']
                                round_obj.save()

                    # Then process all other files
                    serializer_mapping = {
                        # 'rounds.json': RoundSerializer,
                        'group-stages.json': GroupStageSerializer,
                        'team-registrations.json': TeamRegistrationSerializer,
                        'matches.json': MatchSerializer,
                        'player-registrations.json': PlayerRegistrationSerializer,
                        'team-scores.json': TeamScoreSerializer
                    }

                    # Process each remaining file
                    for filename, serializer_class in serializer_mapping.items():
                        process_file(z, filename, tournament_id, tournament, serializer_class)

                return JsonResponse({'status': 'success'})

            except Exception as e:
                print(f'error = {str(e)}')
                return JsonResponse({'status': 'error', 'message': str(e)})
    else:
        return render(request, 'tournaments/admin/tournament_cleaner.html')

def process_file(zip_file, filename, tournament_id, tournament, serializer_class):
    """Helper function to process individual files"""
    try:
        print(f'process {filename}')
        file_path = f"{tournament_id}/{filename}"
        json_data = get_file_data(zip_file, file_path)

        if json_data:
            # Add tournament to each item
            for item in json_data:
                # print(f'process {item}')
                item['tournament'] = tournament.id
                item['store_id'] = str(tournament.id)

            serializer = serializer_class(data=json_data, many=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()

    except Exception as e:
        print(f"Error processing {filename}: {str(e)}")

def get_file_data(zip_file, file_path):
    """Helper function to read and parse JSON data from zip file"""
    try:
        file_content = zip_file.read(file_path)
        return json.loads(file_content)
    except KeyError:
        print(f"File not found: {file_path}")
        return None
    except json.JSONDecodeError as e:
        print(f"JSON Error for {file_path}: {str(e)}")
        raise Exception(f"Invalid JSON in file {file_path}")

def status_page(request):
    return render(request, 'tournaments/admin/status.html')

def team_details(request, tournament_id, team_id):
    # First check if team_id is None or invalid
    if team_id is None or team_id == 'None':
        # Redirect to tournament page or show an error
        return redirect('tournament-info', tournament_id=tournament_id)

    tournament = get_object_or_404(Tournament, id=tournament_id)
    try:
        team = get_object_or_404(TeamRegistration, id=team_id)
    except (ValueError, ValidationError):
        # Handle invalid UUID
        return redirect('tournament-info', tournament_id=tournament_id)

    # Get all matches for this team
    all_matches = team.get_matches()

    return render(request, 'tournaments/team_details.html', {
        'tournament': tournament,
        'team': team,
        'matches': all_matches,
        'debug': False  # Set to False in production
    })

def tournament_bracket(request, tournament_id):
    """
    View to display tournament bracket structure.
    """
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    context = get_butterfly_bracket_view_context(tournament, parent_round=None, double_butterfly_mode=False, display_loser_final=True)
    return render(request, 'tournaments/tournament_bracket.html', context)

def round_bracket(request, tournament_id, round_id):
    """
    View to display round bracket structure.
    """
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    round = get_object_or_404(Round, pk=round_id)
    context = get_butterfly_bracket_view_context(tournament, round, double_butterfly_mode=False, display_loser_final=False)
    return render(request, 'tournaments/tournament_bracket.html', context)

def get_butterfly_bracket_view_context(tournament, parent_round=None, double_butterfly_mode=False, display_loser_final=True):
    if parent_round:
        double_butterfly_mode = False
        display_loser_final = True

    serializable_match_groups = tournament.get_butterfly_bracket_match_group(parent_round, double_butterfly_mode, display_loser_final)

    context = {
        'tournament': tournament,
        'match_groups': serializable_match_groups,
        'double_butterfly_mode': double_butterfly_mode,
        'display_loser_final': display_loser_final
    }

    return context

def tournament_prog(request, tournament_id):
    tournament = get_object_or_404(Tournament, id=tournament_id)

    # Get the day parameter from the request, if present
    day_param = request.GET.get('day', None)

    # Get days and match groups using our new method
    days, match_groups = tournament.planned_matches_by_day(day=day_param, event_mode=True)

    # Format days for template
    formatted_days = [day.strftime('%Y-%m-%d') for day in days]

    # If no day is requested, check if today's date is in the list of days
    selected_day = day_param
    if not selected_day and days:
        today = timezone.now()
        today_str = today.strftime('%Y-%m-%d')

        # Check if today's date exists in formatted_days
        if today_str in formatted_days:
            selected_day = today_str
        else:
            # Default to first day if today is not in the list
            if tournament.has_ended():
                selected_day = days[-1].strftime('%Y-%m-%d')
            else:
                selected_day = days[0].strftime('%Y-%m-%d')
    else:
        if tournament.has_ended():
            selected_day = selected_day or (days[-1].strftime('%Y-%m-%d') if days else None)
        else:
            selected_day = selected_day or (days[0].strftime('%Y-%m-%d') if days else None)

    context = {
        'tournament': tournament,
        'prog_mode': True,
        'match_groups': match_groups,
        'days': days,
        'formatted_days': formatted_days,
        'selected_day': selected_day
    }
    return render(request, 'tournaments/prog.html', context)

def custom_logout(request):
    logout(request)
    return redirect('index')  # or whatever URL you want to redirect to

@login_required
def confirm_tournament_registration(request, tournament_id):
    if request.method == 'POST':
        tournament = get_object_or_404(Tournament, pk=tournament_id)
        team_registration = tournament.get_user_team_registration(request.user)
        if team_registration is None:
            messages.error(request, "Aucune inscription trouvée pour ce tournoi.")
            return redirect('tournament-info', tournament_id=tournament_id)

        if team_registration.is_confirmation_expired():
            team_registration.check_confirmation_deadline()
            messages.error(request, "Le délai de confirmation est expiré. Vous ne pouvez plus confirmer votre participation.")
            return redirect('tournament-info', tournament_id=tournament_id)

        # Confirm registration
        team_registration.confirm_registration()

        messages.success(request, "Votre participation est confirmée !")
        return redirect('tournament-info', tournament_id=tournament_id)

    return redirect('tournament-info', tournament_id=tournament_id)

@login_required
def proceed_to_payment(request, tournament_id):
    """Handle direct payment from tournament info page"""
    tournament = get_object_or_404(Tournament, id=tournament_id)
    team = tournament.get_user_team_registration(request.user)

    if not team:
        messages.error(request, "Aucune inscription trouvée pour ce tournoi.")
        return redirect('tournament-info', tournament_id=tournament_id)

    if team.is_confirmation_expired():
        team.check_confirmation_deadline()
        messages.error(request, "Le délai de confirmation est expiré. Vous ne pouvez plus régler votre participation.")
        return redirect('tournament-info', tournament_id=tournament_id)

    if team.is_paid():
        messages.info(request, "Votre inscription est déjà payée.")
        return redirect('tournament-info', tournament_id=tournament_id)

    try:
        # Create payment session
        payment_service = PaymentService(request)
        checkout_session = payment_service.create_checkout_session(
            tournament_id=str(tournament_id),  # Convert UUID to string
            team_fee=team.get_remaining_fee(),
            team_registration_id=str(team.id)  # Convert UUID to string
        )

        # Redirect to Stripe checkout
        return redirect(checkout_session.url)
    except Exception as e:
        print(f"Payment error: {str(e)}")  # Add debug print
        messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}")
        return redirect('tournament-info', tournament_id=tournament_id)

@login_required
def tournament_payment_success(request, tournament_id):
    """Handle successful Stripe payment for tournament registration"""
    # Get checkout session ID from session
    checkout_session_id = request.session.get('stripe_checkout_session_id')
    if not checkout_session_id:
        messages.error(request, "Session de paiement introuvable.")
        return redirect('tournament-info', tournament_id=tournament_id)

    try:
        # Verify payment status with Stripe
        stripe.api_key = settings.STRIPE_SECRET_KEY

        stripe_account_id = request.session.get('stripe_account_id')
        if not stripe_account_id:
            checkout_session = stripe.checkout.Session.retrieve(checkout_session_id)
        else:
            checkout_session = stripe.checkout.Session.retrieve(checkout_session_id, stripe_account=stripe_account_id)

        if checkout_session.payment_status == 'paid':
            # Process the payment success
            payment_service = PaymentService(request)
            success = payment_service.process_successful_payment(checkout_session)

            if success:
                # Set a flag for successful registration if the payment was from registration page
                source_page = request.session.get('payment_source_page', 'tournament_info')
                if source_page == 'register_tournament':
                    request.session['registration_successful'] = True
                    request.session['registration_paid'] = True

                messages.success(request, "Paiement réussi et inscription confirmée !")
            else:
                messages.error(request, "Erreur lors du traitement du paiement.")
        else:
            messages.error(request, "Le paiement n'a pas été complété.")

    except Exception as e:
        print(f"Payment processing error: {str(e)}")  # Add debug print
        messages.error(request, f"Erreur lors de la vérification du paiement: {str(e)}")

    # Get the source page to determine where to redirect
    source_page = request.session.get('payment_source_page', 'tournament_info')

    # Clean up session variables
    for key in ['stripe_checkout_session_id', 'team_registration_id', 'payment_source_page', 'stripe_account_id']:
        if key in request.session:
            del request.session[key]

    # Redirect to the appropriate page
    if source_page == 'register_tournament':
        # For payments during registration, redirect back to registration page
        # with registration_successful flag in session
        return redirect('register_tournament', tournament_id=tournament_id)
    else:
        # For direct payments, go to tournament info
        return redirect('tournament-info', tournament_id=tournament_id)

@csrf_protect
def register_tournament(request, tournament_id):
    tournament = get_object_or_404(Tournament, id=tournament_id)
    # Print full session content for debugging
    print("=" * 60)
    print(f"[SESSION DEBUG] register_tournament called for tournament {tournament_id}")
    print(f"[SESSION DEBUG] Request method: {request.method}")
    print(f"[SESSION DEBUG] Session key: {request.session.session_key}")
    print("[SESSION DEBUG] Full session contents:")
    for key, value in request.session.items():
        print(f"  {key}: {value}")
    print("=" * 60)

    # Check for registration_successful flag
    registration_successful = request.session.pop('registration_successful', False)
    registration_paid = request.session.pop('registration_paid', False)

    # Handle payment cancellation - check for cancelled team registration
    cancel_team_registration_id = request.session.pop('cancel_team_registration_id', None)
    if cancel_team_registration_id:
        print(f"[PAYMENT CANCEL] Handling cancelled team registration: {cancel_team_registration_id}")
        try:
            # Get the team registration that was created but payment was cancelled
            team_registration = TeamRegistration.objects.get(id=cancel_team_registration_id)

            # Release the reserved spot if this was a cart registration
            if tournament.reserved_spots > 0:
                tournament.reserved_spots -= 1
                tournament.save()
                print(f"[PAYMENT CANCEL] Released reserved spot for tournament {tournament_id}")

            # Delete the unpaid team registration if it's not paid
            if not team_registration.is_paid():
                team_registration.delete()
                print(f"[PAYMENT CANCEL] Deleted unpaid team registration {cancel_team_registration_id}")

        except TeamRegistration.DoesNotExist:
            print(f"[PAYMENT CANCEL] Team registration {cancel_team_registration_id} not found")
        except Exception as e:
            print(f"[PAYMENT CANCEL] Error handling cancellation: {str(e)}")

        # Clean up any other payment-related session data
        for key in ['stripe_checkout_session_id', 'team_registration_id', 'cancel_team_registration_id', 'stripe_account_id']:
            request.session.pop(key, None)

        # Add a user message about the cancellation
        messages.warning(request, "Paiement annulé. Vous pouvez relancer votre inscription ci-dessous.")

    # If registration was successful, render success page immediately
    if registration_successful:
        storage = messages.get_messages(request)
        for _ in storage:
            pass

        if len(storage._loaded_messages) == 1:
            del storage._loaded_messages[0]

        context = {
            'tournament': tournament,
            'registration_successful': True,
            'registration_paid': registration_paid,
            'current_players': [],
            'cart_data': {'players': []}
        }
        return render(request, 'register_tournament.html', context)

    cart_manager = RegistrationCartManager(request)

    # Debug session content
    print_session_debug(request, "SESSION DUMP AT START")

    # Check if tournament is open for registration
    if not tournament.enable_online_registration:
        messages.error(request, "L'inscription en ligne n'est pas activée pour ce tournoi.")
        return redirect('tournament-info', tournament_id=tournament_id)

    # Check if user is already registered
    if request.user.is_authenticated and request.user.licence_id and tournament.is_user_registered(request.user):
        messages.info(request, "Vous êtes déjà inscrit à ce tournoi.")
        return redirect('tournament-info', tournament_id=tournament_id)

    # Only initialize a fresh cart for GET requests
    # For POST requests, use the existing cart to maintain state
    if request.method == 'GET':
        print("Initializing cart")
        storage = messages.get_messages(request)
        for _ in storage:
            pass

        if len(storage._loaded_messages) == 1:
            del storage._loaded_messages[0]

        # ALWAYS initialize a fresh cart when entering the registration page (GET request)
        # This ensures no old cart data persists
        cart_manager.initialize_cart(tournament_id)

        # Auto-add the authenticated user with license
        if request.user.is_authenticated and request.user.licence_id:
            cart_manager.add_authenticated_user()
    else:
        # For POST, ensure tournament ID is correct
        current_tournament_id = cart_manager.get_tournament_id()
        if current_tournament_id != str(tournament_id):
            cart_manager.initialize_cart(tournament_id)

            # Re-add the authenticated user if they have a license
            if request.user.is_authenticated and request.user.licence_id:
                cart_manager.add_authenticated_user()

    # Get cart data
    cart_data = cart_manager.get_cart_data()
    print(f"View - Cart Players Count: {len(cart_data['players'])}")

    # Initialize context with cart data
    context = {
        'tournament': tournament,
        'current_players': cart_data['players'],
        'registration_successful': False,
        'registration_paid': False,
        'cart_data': cart_data
    }

    # Initialize forms
    context['team_form'] = TournamentRegistrationForm(initial={
        'email': request.user.email if request.user.is_authenticated else '',
        'mobile_number': request.user.phone if request.user.is_authenticated else cart_data.get('mobile_number', '')
    })

    # Initialize the add player form
    context['add_player_form'] = create_add_player_form(request, tournament, cart_data)

    # Handle POST requests
    if request.method == 'POST':
        if 'add_player' in request.POST:
            handle_add_player_request(request, tournament, cart_manager, context)
        elif 'remove_player' in request.POST:
            handle_remove_player_request(request, tournament, cart_manager, context)
        elif 'register_team' in request.POST:
            handle_register_team_request(request, tournament, cart_manager, context)
        elif 'proceed_to_payment' in request.POST:
            result = handle_payment_request(request, cart_manager, context, tournament_id)
            if result:
                return result  # This is the redirect to Stripe checkout

    # Debug session content before rendering
    print_session_debug(request, "SESSION DUMP BEFORE RENDER")

    return render(request, 'register_tournament.html', context)

def create_add_player_form(request, tournament, cart_data):
    """Create the appropriate add player form based on user status"""
    # Special handling for authenticated user without license
    if request.user.is_authenticated and not request.user.licence_id and not cart_data['players']:
        # Setup form for user without license
        initial_data = {
            'first_name': request.user.first_name,
            'last_name': request.user.last_name
        }

        # If license is required, only show license field initially
        if tournament.license_is_required:
            add_player_form = AddPlayerForm(initial=initial_data)
            add_player_form.user_without_licence = True
            return add_player_form
        else:
            # If license not required, show all fields
            return AddPlayerForm(initial=initial_data)
    elif not cart_data['players'] or len(cart_data['players']) < tournament.minimum_player_per_team:
        # Regular partner addition form
        return AddPlayerForm()

    return None

def handle_add_player_request(request, tournament, cart_manager, context):
    """Handle the 'add_player' POST action"""
    add_player_form = AddPlayerForm(request.POST)
    if add_player_form.is_valid():
        # Get the current form data for phone number
        team_form = TournamentRegistrationForm(request.POST)
        if team_form.is_valid():
            # Update cart with mobile number before adding player
            cart_manager.update_contact_info(
                mobile_number=team_form.cleaned_data.get('mobile_number')
            )

        success, message = cart_manager.add_player(add_player_form.cleaned_data)
        if success:
            messages.success(request, message)
            # Refresh cart data
            cart_data = cart_manager.get_cart_data()
            context['current_players'] = cart_data['players']

            context['team_form'] = TournamentRegistrationForm(initial={
                'email': request.user.email if request.user.is_authenticated else '',
                'mobile_number': cart_data.get('mobile_number', '')
            })

            # Prepare a fresh form for the next player if needed
            if len(cart_data['players']) < tournament.minimum_player_per_team:
                context['add_player_form'] = AddPlayerForm()
            else:
                # Remove the form if we've reached the team limit
                context['add_player_form'] = None
        else:
            messages.error(request, message)
            if cart_manager.first_tournament:
                add_player_form.first_tournament = True

            context['add_player_form'] = add_player_form
    else:
        for field, errors in add_player_form.errors.items():
            for error in errors:
                messages.error(request, f"{field}: {error}")
        context['add_player_form'] = add_player_form

def handle_remove_player_request(request, tournament, cart_manager, context):
    """Handle the 'remove_player' POST action"""
    success, message = cart_manager.remove_player()
    if success:
        messages.info(request, message)
        # Refresh cart data
        cart_data = cart_manager.get_cart_data()
        context['current_players'] = cart_data['players']

        # Update the add player form based on the new state
        if not cart_data['players'] and request.user.is_authenticated:
            if not request.user.licence_id and tournament.license_is_required:
                initial_data = {
                    'first_name': request.user.first_name,
                    'last_name': request.user.last_name
                }
                add_player_form = AddPlayerForm(initial=initial_data)
                add_player_form.user_without_licence = True
            else:
                add_player_form = AddPlayerForm()

            context['add_player_form'] = add_player_form
        else:
            context['add_player_form'] = AddPlayerForm()
    else:
        messages.error(request, message)

def handle_register_team_request(request, tournament, cart_manager, context):
    """Handle the 'register_team' POST action"""
    team_form = TournamentRegistrationForm(request.POST)
    if team_form.is_valid():
        # Debug print before checkout
        cart_data = cart_manager.get_cart_data()
        print(f"Before checkout - Players in cart: {len(cart_data['players'])}")

        # Ensure the session data is correctly saved before proceeding
        request.session.modified = True
        request.session.save()

        # Update cart with contact info
        cart_manager.update_contact_info(
            mobile_number=team_form.cleaned_data.get('mobile_number')
        )

        # Get fresh cart data again after updating contact info
        fresh_cart_data = cart_manager.get_cart_data()
        print(f"After contact update - Players in cart: {len(fresh_cart_data['players'])}")

        # Debug session content
        print_session_debug(request, "SESSION DUMP BEFORE CHECKOUT")

        # Checkout and create registration
        success, result = cart_manager.checkout(confirmed=True)
        if success:
            waiting_list_position = cart_data.get('waiting_list_position', -1)
            if is_not_sqlite_backend():
                email_service = TournamentEmailService()
                email_service.send_registration_confirmation(
                    request,
                    tournament,
                    result,  # team_registration
                    waiting_list_position
                )

            context['registration_successful'] = True
            context['registration_paid'] = False
            context['current_players'] = []
            context['add_player_form'] = None  # No more adding players after success
        else:
            messages.error(request, result)
    else:
        for field, errors in team_form.errors.items():
            for error in errors:
                messages.error(request, f"{field}: {error}")
        context['team_form'] = team_form

def handle_payment_request(request, cart_manager, context, tournament_id):
    """Handle the 'proceed_to_payment' POST action"""
    team_form = TournamentRegistrationForm(request.POST)
    if team_form.is_valid():
        # Update cart with contact info
        cart_manager.update_contact_info(
            mobile_number=team_form.cleaned_data.get('mobile_number')
        )

        # Create payment session
        try:
            # Create and redirect to payment session
            payment_service = PaymentService(request)
            checkout_session = payment_service.create_checkout_session(
                tournament_id=tournament_id,
                team_fee=cart_manager.team_fee_from_cart_players(),  # Use the appropriate fee field
                cart_manager=cart_manager
            )

            # Redirect to Stripe checkout
            return redirect(checkout_session.url)
        except Exception as e:
            messages.error(request, f"Erreur lors de la création de la session de paiement: {str(e)}")
    else:
        for field, errors in team_form.errors.items():
            for error in errors:
                messages.error(request, f"{field}: {error}")
        context['team_form'] = team_form

    return None

def print_session_debug(request, header):
    """Debug utility to print session contents"""
    print(f"===== {header} =====")
    for key, value in request.session.items():
        if key.startswith('registration_'):
            print(f"{key}: {value}")
    print("================================")

@login_required
def cancel_registration(request, tournament_id):
    """Cancel the current registration process and clear the cart"""
    cart_manager = RegistrationCartManager(request)
    cart_manager.clear_cart()

    try:
        tournament = Tournament.objects.get(id=tournament_id)
        tournament.reserved_spots = max(0, tournament.reserved_spots - 1)
        tournament.save()
    except Tournament.DoesNotExist:
        messages.error(request, "Tournoi introuvable.")
        return redirect('tournaments-list')

    messages.info(request, "Processus d'inscription annulé.")
    return redirect('tournament-info', tournament_id=tournament_id)

@login_required
def confirm_call(request, tournament_id):
    if request.method == 'POST':
        tournament = get_object_or_404(Tournament, pk=tournament_id)
        team = tournament.get_user_team_registration(request.user)

        if team and team.call_date:
            team.confirmation_date = timezone.now()
            team.save()
            # messages.success(request, 'Réception de la convocation confirmée.')
        else:
            messages.error(request, 'Une erreur est survenue.')

    return redirect('tournament-info', tournament_id=tournament_id)

@csrf_exempt
def xls_to_csv(request):
    # Check if the request has a file
    if 'file' in request.FILES:
        uploaded_file = request.FILES['file']

        # Save the uploaded file
        directory = 'tmp/csv/'
        file_path = os.path.join(directory, uploaded_file.name)
        file_name = default_storage.save(file_path, ContentFile(uploaded_file.read()))

        # Check available sheets and look for 'inscriptions'
        xls = pd.ExcelFile(file_name)
        sheet_names = xls.sheet_names

        # Determine which sheet to use
        target_sheet = 0  # Default to first sheet
        if 'inscriptions' in [name.lower() for name in sheet_names]:
            for i, name in enumerate(sheet_names):
                if name.lower() == 'inscriptions':
                    target_sheet = i  # or use the name directly: target_sheet = name
                    break

        # Convert to csv and save
        data_xls = pd.read_excel(file_name, sheet_name=target_sheet, index_col=None)
        csv_file_name = create_random_filename('players', 'csv')
        output_path = os.path.join(directory, csv_file_name)
        data_xls.to_csv(output_path, sep=';', index=False, encoding='utf-8')

        # Send the processed file back
        with default_storage.open(output_path, 'rb') as file:
            response = HttpResponse(file.read(), content_type='application/octet-stream')
            response['Content-Disposition'] = f'attachment; filename="players.csv"'

        # Clean up: delete both files
        default_storage.delete(file_path)
        default_storage.delete(output_path)

        return response
    else:
        return HttpResponse("No file was uploaded", status=400)

def get_user_tournaments(user, tournaments):
    user_tournaments = []
    if user.is_authenticated is False:
        return user_tournaments

    user_licence_id = user.licence_id
    stripped_license = None
    # If no licence_id, return empty lists
    if user_licence_id is not None:
        # Get all tournaments for the user based on their licence
        validator = LicenseValidator(user_licence_id)
        stripped_license = validator.stripped_license

    for t in tournaments:
        # First check for direct user relationship
        direct_registration_exists = t.team_registrations.filter(
            player_registrations__user=user,
            walk_out=False
        ).exists()

        # If direct relationship exists, add the tournament
        if direct_registration_exists:
            user_tournaments.append(t)
            continue

        # Otherwise, check by license
        license_registration_exists = None
        if stripped_license:
            license_registration_exists = t.team_registrations.filter(
                player_registrations__licence_id__icontains=stripped_license,
            walk_out=False
        ).exists()

        if license_registration_exists:
            user_tournaments.append(t)

    return user_tournaments

def stripe_onboarding_complete(request):
    return render(request, 'stripe/onboarding_complete.html')

def stripe_refresh_account_link(request):
    return render(request, 'stripe/refresh_account_link.html')

def toggle_tournament_private(request, tournament_id):
    """Toggle tournament privacy status (for superusers only)"""

    # Check if user is superuser
    if not request.user.is_superuser:
        if request.headers.get('Content-Type') == 'application/json':
            return JsonResponse({'error': 'Accès non autorisé'}, status=403)
        messages.error(request, 'Accès non autorisé')
        return redirect('tournament-info', tournament_id=tournament_id)

    # Only allow POST requests
    if request.method != 'POST':
        if request.headers.get('Content-Type') == 'application/json':
            return JsonResponse({'error': 'Méthode non autorisée'}, status=405)
        messages.error(request, 'Méthode non autorisée')
        return redirect('tournament-info', tournament_id=tournament_id)

    try:
        tournament = get_object_or_404(Tournament, pk=tournament_id)

        # Toggle the private status
        tournament.is_private = not tournament.is_private
        tournament.save()

        # Check if this is an AJAX request
        if request.headers.get('Content-Type') == 'application/json':
            return JsonResponse({
                'success': True,
                'is_private': tournament.is_private,
                'message': f'Tournoi défini comme {"privé" if tournament.is_private else "public"}'
            })
        else:
            # Regular form submission - add success message and redirect
            status = "privé" if tournament.is_private else "public"
            messages.success(request, f'Tournoi défini comme {status}')
            return redirect('tournament-info', tournament_id=tournament_id)

    except Exception as e:
        if request.headers.get('Content-Type') == 'application/json':
            return JsonResponse({'error': f'Erreur: {str(e)}'}, status=500)
        messages.error(request, f'Erreur: {str(e)}')
        return redirect('tournament-info', tournament_id=tournament_id)

def private_tournaments(request):
    """
    View for displaying private tournaments (staff-only).
    Similar to index, but shows private tournaments instead.
    """
    if not request.user.is_staff:
        messages.error(request, 'Accès non autorisé')
        return redirect('index')

    now = timezone.now()
    thirty_days_ago = now - timedelta(days=30)
    thirty_days_future = now + timedelta(days=30)

    # Define a custom private tournaments query function
    def private_tournaments_query(query, ascending, limit=None):
        queries = [query, Q(is_private=True, is_deleted=False, event__club__isnull=False)]

        sortkey = 'start_date'
        if not ascending:
            sortkey = '-start_date'

        queryset = Tournament.objects.filter(*queries).prefetch_related(
            'group_stages',
            'rounds',
            'team_registrations',
        ).order_by(sortkey)

        # Apply limit directly in the database query
        if limit is not None and isinstance(limit, int) and limit > 0:
            queryset = queryset[:limit]

        return queryset

    # Get all tournaments matching our criteria (similar to index but for private tournaments)
    tournaments = private_tournaments_query(
        Q(end_date__isnull=True, start_date__gte=thirty_days_ago, start_date__lte=thirty_days_future),
        False, 50
    )

    # Filter tournaments that should be displayed
    display_tournament = tournaments

    # Categorize tournaments by status
    live = []
    future = []
    ended = []
    for t in display_tournament:
        if t.supposedly_in_progress():
            live.append(t)
        elif t.starts_in_the_future():
            future.append(t)
        else:
            ended.append(t)

    # Get ended tournaments
    clean_ended_tournaments = private_tournaments_query(Q(end_date__isnull=False), False, 50)
    clean_ended_tournaments = [t for t in clean_ended_tournaments if t.display_tournament()]
    ended_tournaments = [t for t in display_tournament if t.should_be_over()]

    # Combine both lists
    finished = clean_ended_tournaments + ended_tournaments + ended

    # Sort the combined list by start_date in descending order
    finished.sort(key=lambda t: t.sorting_finished_date(), reverse=True)
    future.sort(key=lambda t: t.sorting_finished_date(), reverse=False)

    return render(
        request,
        "tournaments/tournaments.html",
        {
            'future': future[:10],
            'live': live[:10],
            'ended': finished[:10],
            'is_private_section': True,  # Flag to indicate we're in the private tournaments section
            'section_title': 'Tournois privés',  # Title for the private tournaments page
        }
    )

def tournament_live_matches(request, tournament_id):
    tournament = get_object_or_404(Tournament, pk=tournament_id)
    # Get all matches from the tournament
    current_time = timezone.now()
    matches = Match.objects.filter(
        Q(round__tournament=tournament) | Q(group_stage__tournament=tournament),
        start_date__isnull=False,    # Match has a start date
        start_date__lte=current_time,
        confirmed=True,              # Match is confirmed
        end_date__isnull=True        # Match hasn't ended yet
    ).order_by('start_date')

    # Convert to live match format
    live_matches = [match.live_match() for match in matches]

    return render(request, 'tournaments/live_matches.html', {
        'tournament': tournament,
        'live_matches': live_matches,
    })


class UserListExportView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        users = CustomUser.objects.order_by('date_joined')

        # Prepare the response
        response = HttpResponse(content_type='text/plain; charset=utf-8')

        # Write header
        headers = [
            'Prenom', 'Nom', 'Club', 'Email', 'Telephone', 'Username',
            'Origine', 'Actif', 'Inscription', 'Tournois'
        ]
        response.write('\t'.join(headers) + '\n')

        # Write data rows
        for user in users:
            row = [
                str(user.first_name or ''),
                str(user.last_name or ''),
                str(user.latest_event_club_name() or ''),
                str(user.email or ''),
                str(user.phone or ''),
                user.username,
                str(user.get_origin_display()),
                'Oui' if user.is_active else 'Non',
                user.date_joined.strftime('%Y-%m-%d %H:%M:%S'),
                str(user.event_count())
            ]
            # Replace any tabs or newlines in the data to prevent formatting issues
            row = [field.replace('\t', ' ') for field in row]
            response.write('\t'.join(row) + '\r\n')

        return response
