import random
import csv
import uuid
import base58
from datetime import datetime, timedelta
import secrets


from django.db.models import Count, Min, Q

from django.shortcuts import render, redirect
from django.utils.timezone import localtime, make_aware
from django.views.decorators.http import require_GET, require_POST
from django.contrib.auth.decorators import login_required, user_passes_test

from django.http import HttpResponse

from collections import defaultdict

from tombola.models import *
from tombola.serializers import RaffleDrawSerializer, HourRangeSerializer


@require_GET
@login_required
@user_passes_test(lambda u: u.is_coord)
def get_tombola_list(request):
    tombola_list = RaffleDraw.objects.all()

    context = {
        "tombola_list": tombola_list,
    }

    return render(request, 'tombola_management/tombola/tombola_list.html', context)


@require_GET
@login_required
@user_passes_test(lambda u: u.is_coord)
def get_tombola_details(request, tombola_id):
    tombola = RaffleDraw.objects.get(pk=tombola_id)

    partners = Partenaire.objects.filter(raffle=tombola)

    winners_t1 = Passeport.objects.filter(raffle=tombola, win_t1__gt=0).order_by('win_t1')
    winners_t2 = Passeport.objects.filter(raffle=tombola, win_t2__gt=0).order_by('win_t2')
    winners_partners = Passeport.objects.filter(raffle=tombola, win_p__gt=0).order_by('win_p')
    winners_24h = Passeport.objects.filter(raffle=tombola, win_24h__gt=0).order_by('win_24h')

    context = {
        'tombola': tombola,
        'participants_range': range(1, tombola.participants),
        'partners': partners,
        'winners_t1': winners_t1,
        'winners_t2': winners_t2,
        'winners_partners': winners_partners,
        'winners_24h': winners_24h
    }

    return render(request, 'tombola_management/tombola/tombola_details.html', context=context)


@require_POST
@login_required
@user_passes_test(lambda u: u.is_coord)
def add_new_tombola(request):
    tombola = RaffleDraw(
        name=request.POST.get('tombola_name'),
        creation_date=datetime.today(),
        participants=int(request.POST.get('tombola_participants')),
        winners_t1 =int(request.POST.get('tombola_nb_winners_t1')),
        winners_t2=int(request.POST.get('tombola_nb_winners_t2')),
        winners_p=int(request.POST.get('tombola_nb_winners_p')),
        winners_24h=int(request.POST.get('tombola_nb_winners_24h')),
        timestamp_raffle=int(request.POST.get('tombola_scan_interval')),
        start_datetime=datetime.strptime(request.POST.get('tombola_start_datetime'), "%Y-%m-%dT%H:%M"),
        end_datetime=datetime.strptime(request.POST.get('tombola_end_datetime'), "%Y-%m-%dT%H:%M"),
        partners_number=request.POST.get('tombola_partners_number'),
        state=2,
        is_active=True,
    )
    tombola.save()

    for i in range(1, tombola.participants + 1):
        passeport = Passeport(
            raffle=tombola,
            number=i,
            unique_id=base58.b58encode(uuid.uuid4().bytes).decode()
        )
        passeport.save()

    for i in range((tombola.end_datetime - tombola.start_datetime).seconds // 3600 + (tombola.end_datetime - tombola.start_datetime).days * 24):
        start_time = tombola.start_datetime + timedelta(hours=i+1)
        end_time = start_time + timedelta(minutes=tombola.timestamp_raffle)

        hour_range = HourRange(
            raffle=tombola,
            start=start_time,
            end=end_time,
        )
        print(f"Création de HourRange: {start_time} -> {end_time}")
        hour_range.save()

    year = datetime.now().year
    for i in range(int(tombola.partners_number)):
        raw_password = secrets.token_urlsafe(8)
        user = User(username='partner' + str(i + 1) + '_' + str(year), is_partner=True, comment=raw_password,
                    is_active=True)
        user.set_password(raw_password)
        user.save()
        Partenaire(raffle=tombola, user=user).save()

    return redirect('tombola:tombola_details', tombola.id)


@require_POST
@login_required
@user_passes_test(lambda u: u.is_coord)
def save_step_2(request, tombola_id):
    print(request.POST)
    for partner in Partenaire.objects.filter(raffle=tombola_id):
        user = partner.user
        user.first_name = request.POST.get(partner.user.username + '_firstname')
        user.last_name = request.POST.get(partner.user.username + '_lastname')
        user.save()

    return redirect('tombola:tombola_details', tombola_id)


@require_POST
@login_required
@user_passes_test(lambda u: u.is_coord)
def unlock_step_3(request, tombola_id):
    tombola = RaffleDraw.objects.get(pk=tombola_id)
    tombola.state = 3
    tombola.step_raffle = 1 # autoriser le premier tirage
    tombola.save()

    return redirect('tombola:tombola_details', tombola_id)



def tirage_1_2(tombola, type_tirage):
    if type_tirage == 1:
        gagnants = tombola.winners_t1
        tickets = Ticket.objects.filter(passeport__raffle=tombola)
    else:
        gagnants = tombola.winners_t2
        tickets = Ticket.objects.filter(passeport__raffle=tombola , passeport__win_t1=0)

    tickets_by_passeport = defaultdict(list)
    for ticket in tickets:
        tickets_by_passeport[ticket.passeport_id].append(ticket)

    pool = []
    passeports_count = {}

    for passeport in Passeport.objects.filter(raffle=tombola):
        user_tickets = tickets_by_passeport.get(passeport.id, [])
        ticket_count = len(user_tickets)

        actual_hourrange_ids = set(tk.hour_range_id for tk in user_tickets if tk.hour_range_id)

        pool.extend([passeport.id] * ticket_count)

        cur_passeport = {"nb_hour": len(actual_hourrange_ids),
                         "nb_partenaire": ticket_count - len(actual_hourrange_ids),
                         "pond": pool.count(passeport.id)}

        passeports_count[passeport.id] = cur_passeport

    gagnants_list = []
    order = 1
    while order <= gagnants and pool:
        gagnant = random.choice(pool)  # Tire au sort un numéro

        gagnants_list.append({"num": gagnant, "order": order, "nb_hour": passeports_count[gagnant]["nb_hour"],
                              "nb_partenaire": passeports_count[gagnant]["nb_partenaire"],
                              "pond": passeports_count[gagnant]["pond"]})
        order = order + 1
        pool = [x for x in pool if x != gagnant]  # Retire toutes ses occurrences

    return gagnants_list




def tirage_24h(tombola):

    '''
    d abord recuperer les gagnants des partenaires
    on recupere les X passeports dans l ordre decroissant des partenaires, puis de l'heure la plus tot dans la manifestation
    '''

    passeports_partenaires_gagnants = Passeport.objects.annotate(
        ticket_count=Count('ticket', filter=Q(ticket__partner__isnull=False)),
        first_ticket_hour=Min('ticket__badge_time', filter=Q(ticket__partner__isnull=False))
    ).filter(
        ticket_count__gt=0  # Optionnel, pour ne garder que ceux qui ont au moins 1 ticket valide
    ).order_by('-ticket_count', 'first_ticket_hour')[:tombola.winners_p]

    gagnants_list_p = []
    order = 1
    for passeport in passeports_partenaires_gagnants:
        gagnants_list_p.append({"num": passeport.number, "order": order, "nb_partenaire": passeport.ticket_count })
        order = order + 1

    '''
    Puis faire un tirage sur ceux qui ont 24h uniquement mais en excluant les passeports qui ont deja ete tirés pour les partenaires
    '''

    passeports_24h_gagnants = list(Passeport.objects.annotate( ticket_count=Count('ticket', filter=Q(ticket__hour_range__isnull=False)) ).filter(Q(ticket_count=23)|Q(ticket_count=24)))
    gagnants_24h = random.sample(passeports_24h_gagnants, k=min(tombola.winners_24h, len(passeports_24h_gagnants)))


    gagnants_list_24h = []
    order = 1
    for g in gagnants_24h:
        gagnants_list_24h.append({"num": g.number, "order": order})
        order = order + 1

    return gagnants_list_p, gagnants_list_24h


@require_POST
@login_required
@user_passes_test(lambda u: u.is_coord)
def draw_raffle_1(request, tombola_id):
    tombola = RaffleDraw.objects.get(pk=tombola_id)

    tirage_list = tirage_1_2(tombola , 1)

    for elem in tirage_list:
        try:
            passeport_gagnant = Passeport.objects.get(id=elem["num"])
            passeport_gagnant.win_t1 = elem["order"]
            passeport_gagnant.save()
        except:
            # ne devrait pas arriver
            pass

    tombola.step_raffle = 2 # on autorise le tirage 2
    tombola.save()

    return redirect('tombola:tombola_details', tombola_id)


@require_POST
@login_required
@user_passes_test(lambda u: u.is_coord)
def draw_raffle_2(request, tombola_id):
    tombola = RaffleDraw.objects.get(pk=tombola_id)

    tirage_list = tirage_1_2(tombola , 2)

    for elem in tirage_list:
        try:
            passeport_gagnant = Passeport.objects.get(id=elem["num"])
            passeport_gagnant.win_t2 = elem["order"]
            passeport_gagnant.save()
        except:
            # ne devrait pas arriver
            pass

    tombola.step_raffle = 3 # on autorise le tirage final 24h
    tombola.save()
    return redirect('tombola:tombola_details', tombola_id)



@require_POST
@login_required
@user_passes_test(lambda u: u.is_coord)
def draw_raffle_final(request, tombola_id):
    tombola = RaffleDraw.objects.get(pk=tombola_id)

    tirage_partenaire_list, tirage_24h_list = tirage_24h(tombola )

    for elem in tirage_partenaire_list:
        try:
            passeport_gagnant = Passeport.objects.get(id=elem["num"])
            passeport_gagnant.win_p = elem["order"]
            passeport_gagnant.save()
        except:
            # ne devrait pas arriver
            pass

    for elem in tirage_24h_list:
        try:
            passeport_gagnant = Passeport.objects.get(id=elem["num"])
            passeport_gagnant.win_24h = elem["order"]
            passeport_gagnant.save()
        except:
            # ne devrait pas arriver
            pass

    tombola.step_raffle = 4 # on autorise le tirage final 24h
    tombola.save()
    return redirect('tombola:tombola_details', tombola_id)


@require_POST
@login_required
@user_passes_test(lambda u: u.is_coord)
def delete_raffle(request, tombola_id):
    tombola = RaffleDraw.objects.get(pk=tombola_id)

    Ticket.objects.filter(passeport__raffle=tombola).delete()
    Passeport.objects.filter(raffle=tombola).delete()
    HourRange.objects.filter(raffle=tombola).delete()
    for partner in Partenaire.objects.filter(raffle=tombola):
        user = partner.user
        partner.delete()
        user.delete()

    tombola.delete()

    return redirect('tombola:tombola_list')


@require_GET
@login_required
@user_passes_test(lambda u: u.is_coord)
def export_passeports_csv(request, tombola_id):
    tombola = RaffleDraw.objects.get(pk=tombola_id)
    passeports = Passeport.objects.filter(raffle=tombola)

    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="passeports.csv"'

    writer = csv.writer(response, delimiter=';')

    for passeport in passeports:
        writer.writerow([passeport.number, passeport.unique_id])

    return response


@require_POST
@login_required
@user_passes_test(lambda u: u.is_coord)
def close_tombola(request, tombola_id):
    raffle = RaffleDraw.objects.get(pk=tombola_id)
    raffle.is_active = False
    raffle.save()

    return redirect('tombola:tombola_list')


@require_GET
@login_required
@user_passes_test(lambda u: u.is_coord)
def staff_badge_space(request):
    raffle = RaffleDraw.objects.get(is_active=True)

    context = {
        'hour_ranges': HourRangeSerializer(HourRange.objects.filter(raffle=raffle), many=True).data
    }

    print(context)
    return render(request, 'staff_space/badge.html', context)

