import math
import os
import platform
import random
from collections import Counter

from django import get_version as django_version
from django.shortcuts import render, redirect, get_object_or_404
from django.http import JsonResponse
import json
from django.utils import timezone
from django.contrib import messages

from .forms import LotterySimulatorForm
from .models import Lottery, DrawResult, AdminAccess

def check_admin(request):
    """Verifica se a chave privada na sessão é válida."""
    key = request.session.get('admin_key')
    return AdminAccess.objects.filter(private_key=key).exists()

def admin_login(request):
    """Tela de login simples para a Chave Privada."""
    if request.method == 'POST':
        key = request.POST.get('private_key')
        if AdminAccess.objects.filter(private_key=key).exists():
            request.session['admin_key'] = key
            return redirect('admin_dashboard')
        else:
            messages.error(request, "Chave Privada Inválida!")
    return render(request, 'core/admin_login.html')

def admin_logout(request):
    request.session.flush()
    return redirect('home')

def admin_dashboard(request):
    """Painel principal do administrador."""
    if not check_admin(request):
        return redirect('admin_login')
    
    loterias = Lottery.objects.all()
    return render(request, 'core/admin_dashboard.html', {'loterias': loterias})

def edit_lottery(request, lottery_id):
    """Editor específico para cada jogo (anular números)."""
    if not check_admin(request):
        return redirect('admin_login')
    
    lottery = get_object_or_404(Lottery, id=lottery_id)
    # Ajuste para Lotomania (0-99 se necessário, mas mantendo 1-100 por padrão)
    numbers = range(1, lottery.max_number + 1)
    annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]

    if request.method == 'POST':
        selected_numbers = request.POST.getlist('numbers')
        lottery.annulled_numbers = ",".join(selected_numbers)
        lottery.save()
        messages.success(request, f"Configurações da {lottery.get_name_display()} salvas!")
        return redirect('admin_dashboard')

    return render(request, 'core/edit_lottery.html', {
        'lottery': lottery,
        'numbers': numbers,
        'annulled': annulled
    })

def _format_odds(total_combinations):
    if total_combinations >= 1_000_000_000_000:
        return f"1 em {total_combinations:.2e}"
    return f"1 em {total_combinations:,}".replace(",", ".")

def _format_percent(odds):
    percent = 100 / odds
    if percent < 0.000001:
        return "<0,000001%"
    return f"{percent:.6f}%".replace(".", ",")

def ai_auto_predict(request, lottery_id):
    """Calcula automaticamente os números quentes via IA baseada em estatísticas reais e excluindo anulados."""
    if not check_admin(request):
        return redirect('admin_login')
    
    lottery = get_object_or_404(Lottery, id=lottery_id)
    annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]
    
    # Pega todos os sorteios (Histórico Completo)
    draws_db = DrawResult.objects.filter(lottery=lottery)
    
    if not draws_db.exists():
        messages.warning(request, "Não há sorteios reais cadastrados para análise completa.")
        return redirect('edit_lottery', lottery_id=lottery_id)

    # Lógica de IA: Frequência + Atraso (Delay)
    draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
    frequency = Counter(number for draw in draw_lists for number in draw)
    
    # Calcular atraso
    last_seen = {}
    for i, draw in enumerate(reversed(draw_lists)):
        for num in draw:
            if num not in last_seen:
                last_seen[num] = i
    
    # Score IA = (Frequência * 0.7) + (Atraso * 0.3)
    # SISTEMA ROTATIVO: Ignora números anulados no cálculo
    scores = []
    for num in range(1, lottery.max_number + 1):
        if num in annulled:
            continue
        freq = frequency.get(num, 0)
        delay = last_seen.get(num, len(draw_lists))
        score = (freq * 0.7) + (delay * 0.3)
        scores.append((num, score))
    
    # Pega os 20% melhores números restantes como "IA Hot"
    scores.sort(key=lambda x: x[1], reverse=True)
    top_numbers = [str(n[0]) for n in scores[:int(lottery.max_number * 0.25)]]
    
    lottery.ai_predictions = ",".join(top_numbers)
    lottery.save()
    
    messages.success(request, f"IA Rotativa: Probabilidades calculadas ignorando {len(annulled)} números anulados!")
    return redirect('edit_lottery', lottery_id=lottery_id)

def home(request):
    """Gera números a serem sorteados com base em análise matemática."""
    host_name = request.get_host().lower()
    agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
    now = timezone.now()

    loterias_db = Lottery.objects.all()
    lottery_choices = [(l.name, l.get_name_display()) for l in loterias_db]
    
    lottery_cards = []
    for l in loterias_db:
        total_combinations = math.comb(l.max_number, l.numbers_to_draw)
        lottery_cards.append({
            "key": l.name,
            "label": l.get_name_display(),
            "tagline": f"{l.numbers_to_draw} dezenas entre {l.max_number}",
            "range_max": l.max_number,
            "picks": l.numbers_to_draw,
            "odds": _format_odds(total_combinations),
        })

    form = LotterySimulatorForm(
        request.POST or None,
        lottery_choices=lottery_choices,
    )
    result = None

    if form.is_valid():
        lottery_key = form.cleaned_data["lottery_type"]
        draws_to_consider = int(form.cleaned_data["draws_to_consider"])
        games_to_generate = int(form.cleaned_data["games_to_generate"])
        
        lottery_obj = Lottery.objects.get(name=lottery_key)
        annulled = [int(n) for n in lottery_obj.annulled_numbers.split(',') if n]
        ai_hot = [int(n) for n in lottery_obj.ai_predictions.split(',') if n]
        
        # Filtro de histórico (0 = Todos)
        if draws_to_consider == 0:
            draws_db = DrawResult.objects.filter(lottery=lottery_obj)
        else:
            draws_db = DrawResult.objects.filter(lottery=lottery_obj)[:draws_to_consider]
            
        draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
        
        # Fallback para dados simulados se vazio
        if not draw_lists:
            rng_mock = random.Random(42)
            pop = list(range(1, lottery_obj.max_number + 1))
            for _ in range(50): # Simula 50 sorteios para cálculo
                draw_lists.append(rng_mock.sample(pop, lottery_obj.numbers_to_draw))

        frequency = Counter(number for draw in draw_lists for number in draw)
        numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in annulled]
        
        # Definição de Cores: Verde (Hot) e Vermelho (Frio)
        # MATEMÁTICA AO VIVO: Calcula a elite estatística no momento do acesso, ignorando anulados.
        available_numbers = [n for n in range(1, lottery_obj.max_number + 1) if n not in annulled]
        
        # IA DE RECLAMAÇÃO: Identifica números anulados com alta probabilidade de sorteio imediato
        # Calculamos o atraso (delay) para todos os números
        last_seen_all = {}
        for i, draw in enumerate(reversed(draw_lists)):
            for num in draw:
                if num not in last_seen_all:
                    last_seen_all[num] = i
        
        # Score de Reclamação para Anulados
        reclaimed_numbers = []
        for n in annulled:
            freq = frequency.get(n, 0)
            delay = last_seen_all.get(n, len(draw_lists))
            # Se o número tem frequência alta E está "atrasado", ele é um forte candidato a voltar
            reclaim_score = (freq * 0.6) + (delay * 0.4)
            # Se o score dele estiver no topo 15% de probabilidade global, nós o "reclamamos"
            if reclaim_score > (max(frequency.values()) * 0.8):
                reclaimed_numbers.append(n)

        # Ordena os números disponíveis pela frequência (quentes primeiro)
        sorted_by_freq = sorted(available_numbers, key=lambda n: frequency.get(n, 0), reverse=True)
        
        # A "Elite Verde" será composta pelos top 25% dos números mais frequentes disponíveis
        hot_count = max(6, int(len(available_numbers) * 0.25))
        hot_numbers = sorted_by_freq[:hot_count]
        
        cold_numbers = [n for n in sorted_by_freq[hot_count:]]

        suggestions = []
        # Suporte a múltiplas escalas de Trilhões
        is_trillion = (games_to_generate >= 1_000_000_000_000)
        actual_games_to_gen = 6 if is_trillion else games_to_generate
        
        trillion_label = ""
        if games_to_generate == 1_000_000_000_000: trillion_label = "1 Trilhão"
        elif games_to_generate == 10_000_000_000_000: trillion_label = "10 Trilhões"
        elif games_to_generate == 30_000_000_000_000: trillion_label = "30 Trilhões"
        elif games_to_generate == 60_000_000_000_000: trillion_label = "60 Trilhões"

        for _ in range(actual_games_to_gen):
            if len(numbers) >= lottery_obj.numbers_to_draw:
                temp_numbers = numbers.copy()
                game = []
                for _p in range(lottery_obj.numbers_to_draw):
                    ws = [frequency.get(n, 0) * 2 if n in hot_numbers else frequency.get(n, 0) + 1 for n in temp_numbers]
                    idx = random.choices(range(len(temp_numbers)), weights=ws, k=1)[0]
                    game.append(temp_numbers[idx])
                    del temp_numbers[idx]
                suggestions.append(sorted(game))

        total_combinations = math.comb(lottery_obj.max_number, lottery_obj.numbers_to_draw)
        
        result = {
            "lottery": lottery_obj.get_name_display(),
            "draws_used": len(draw_lists),
            "is_trillion": is_trillion,
            "trillion_label": trillion_label,
            "suggestions": suggestions,
            "hot_numbers": hot_numbers,
            "cold_numbers": cold_numbers[:15], # Amostra de frios
            "annulled_numbers": annulled,
            "reclaimed_numbers": reclaimed_numbers,
        }

    context = {
        "project_name": "Gerador de Números Sorteados",
        "project_description": "IA de Alta Performance para Loterias - Histórico Completo",
        "agent_brand": agent_brand,
        "django_version": django_version(),
        "python_version": platform.python_version(),
        "current_time": now,
        "form": form,
        "result": result,
        "lottery_cards": lottery_cards,
        "is_admin": check_admin(request),
    }
    return render(request, "core/index.html", context)

def live_math(request):
    """Calcula a probabilidade matemática ao vivo para números selecionados manualmente."""
    if request.method == "POST":
        try:
            data = json.loads(request.body)
            lottery_key = data.get("lottery")
            manual_numbers = [int(n) for n in data.get("numbers", [])]
            
            if not manual_numbers or len(manual_numbers) < 6:
                return JsonResponse({"error": "Selecione ao menos 6 números."}, status=400)

            lottery = get_object_or_404(Lottery, name=lottery_key)
            draws_db = DrawResult.objects.filter(lottery=lottery)
            
            # Se não houver sorteios, usamos uma base simulada para a matemática ao vivo
            if not draws_db.exists():
                frequency = {n: random.randint(5, 15) for n in range(1, lottery.max_number + 1)}
            else:
                draw_lists = [[int(n) for n in d.numbers.split(',')] for d in draws_db]
                frequency = Counter(number for draw in draw_lists for number in draw)

            ai_hot = [int(n) for n in lottery.ai_predictions.split(',') if n]
            annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n]

            # Cálculo de "Calor" (Heat Score)
            # 1. Quantos são Verdes (IA Hot)
            hits_hot = len([n for n in manual_numbers if n in ai_hot])
            # 2. Quantos foram anulados (Erro crítico)
            hits_annulled = len([n for n in manual_numbers if n in annulled])
            
            # Média de frequência dos números escolhidos
            avg_freq = sum(frequency.get(n, 0) for n in manual_numbers) / len(manual_numbers)
            max_freq = max(frequency.values()) if frequency else 1
            
            # Score final de 0 a 100
            score = (hits_hot / len(manual_numbers) * 50) + (avg_freq / max_freq * 50)
            if hits_annulled > 0:
                score = score * (1 - (hits_annulled / len(manual_numbers)))

            return JsonResponse({
                "score": round(score, 2),
                "hits_hot": hits_hot,
                "hits_annulled": hits_annulled,
                "message": "Cálculo Matemático Ao Vivo Concluído!",
                "status": "success" if score > 50 else "warning"
            })
        except Exception as e:
            return JsonResponse({"error": str(e)}, status=500)
    return JsonResponse({"error": "Método inválido"}, status=405)
