<?php

namespace App\Http\Controllers;

use App\Services\ImageOptimizer;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Models\Club;
use App\Models\Equipo;
use App\Models\Jugador;
use App\Models\Evento;
use App\Models\Promocion;
use App\Models\Sponsor;
use App\Models\Noticia;
use App\Models\QrCode;

class AdminController extends Controller
{
    // ──────── Middleware check ────────
    private function checkSuperAdmin(Request $request)
    {
        if (!session('admin_logged_in') || session('admin_role') != 1) {
            abort(403, 'Acceso denegado. Solo Súper Administradores.');
        }
    }

    private function checkGeneralAdmin(Request $request)
    {
        if (!session('admin_logged_in') || !in_array(session('admin_role'), [1, 2])) {
            abort(403, 'Acceso denegado');
        }
    }

    // ══════════════════════════════════
    //  DASHBOARD
    // ══════════════════════════════════
    public function dashboard(Request $request)
    {
        $this->checkGeneralAdmin($request);

        if (session('admin_role') == 1) {
            $stats = [
                'clubes'      => Club::count(),
                'equipos'     => Equipo::count(),
                'jugadores'   => Jugador::count(),
                'eventos'     => Evento::count(),
                'promociones' => Promocion::count(),
                'noticias'    => Noticia::count(),
            ];
            $miClub = null;
        } else {
            $idClub = session('admin_id_club');
            $miClub = Club::find($idClub);
            
            $stats = [
                'equipos'     => Equipo::where('id_club', $idClub)->count(),
                'jugadores'   => Jugador::where('id_club_actual', $idClub)->count(),
                'eventos'     => Evento::whereHas('equipoLocal', function($q) use ($idClub) {
                                    $q->where('id_club', $idClub);
                                 })->orWhereHas('equipoVisitante', function($q) use ($idClub) {
                                    $q->where('id_club', $idClub);
                                 })->count(),
                'promociones' => Promocion::count(), // Promociones son generales
            ];
        }

        return view('admin.dashboard', compact('stats', 'miClub'));
    }

    // ══════════════════════════════════
    //  CLUBES
    // ══════════════════════════════════
    public function clubesIndex(Request $request)
    {
        $this->checkSuperAdmin($request);
        $search = trim((string) $request->input('q', ''));

        // Recién creados/editados primero; registros viejos sin timestamps caen al fallback por id.
        $query = Club::withCount('equipos', 'jugadores')
            ->orderByRaw("COALESCE(updated_at, '1970-01-01') DESC")
            ->orderBy('id_club', 'desc');

        if ($search !== '') {
            $query->where('nombre', 'like', '%' . $search . '%');
        }

        $clubes = $query->get();
        return view('admin.clubes.index', compact('clubes', 'search'));
    }

    public function clubesCreate(Request $request)
    {
        $this->checkSuperAdmin($request);
        return view('admin.clubes.form', ['club' => null]);
    }

    public function clubesStore(Request $request)
    {
        $this->checkSuperAdmin($request);
        $data = $request->validate([
            'id_club'      => 'nullable|integer',
            'nombre'       => 'required|string|max:100',
            'es_seleccion' => 'nullable|boolean',
        ]);
        $data['es_seleccion'] = $request->boolean('es_seleccion');
        Club::create($data);
        return redirect()->route('admin.clubes.index')->with('admin_msg', 'Club creado correctamente.');
    }

    public function clubesEdit(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        if (session('admin_role') == 2 && session('admin_id_club') != $id) {
            abort(403, 'No tienes permiso para editar este club.');
        }
        $club = Club::findOrFail($id);
        return view('admin.clubes.form', compact('club'));
    }

    public function clubesUpdate(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        if (session('admin_role') == 2 && session('admin_id_club') != $id) {
            abort(403, 'No tienes permiso para editar este club.');
        }

        $club = Club::findOrFail($id);
        
        $rules = [
            'qr_color_texto' => 'nullable|string|max:20',
            'qr_background'  => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048',
            'logo_club'      => 'nullable|image|mimes:jpeg,png,jpg,webp|max:1024'
        ];
        
        if (session('admin_role') == 1) {
            $rules['nombre']       = 'required|string|max:100';
            $rules['es_seleccion'] = 'nullable|boolean';
        }

        $data = $request->validate($rules);

        if (session('admin_role') == 1) {
            $data['es_seleccion'] = $request->boolean('es_seleccion');
        }
        
        // Manejo de Logo del Club
        if ($request->hasFile('logo_club')) {
            // Eliminar logo anterior si existe
            if ($club->imagen && file_exists(public_path($club->imagen))) {
                @unlink(public_path($club->imagen));
            }
            $logoPath = app(ImageOptimizer::class)->storeAndOptimize($request->file('logo_club'), 'clubes');
            $data['imagen'] = 'storage/' . $logoPath;
        }

        // Manejo de Fondo QR
        if ($request->hasFile('qr_background')) {
            // Eliminar fondo anterior si existe
            if ($club->qr_background && file_exists(public_path($club->qr_background))) {
                @unlink(public_path($club->qr_background));
            }
            $path = app(ImageOptimizer::class)->storeAndOptimize($request->file('qr_background'), 'qr');
            $data['qr_background'] = 'storage/' . $path;
        }

        $club->update($data);

        if (session('admin_role') == 2) {
            return back()->with('admin_msg', 'Plantilla de QR actualizada correctamente.');
        }

        return redirect()->route('admin.clubes.index')->with('admin_msg', 'Club actualizado correctamente.');
    }

    public function clubesDestroy(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $club = Club::findOrFail($id);
        $club->delete();
        return redirect()->route('admin.clubes.index')->with('admin_msg', 'Club eliminado correctamente.');
    }

    // ══════════════════════════════════
    //  EQUIPOS
    // ══════════════════════════════════
    public function equiposIndex(Request $request)
    {
        $this->checkGeneralAdmin($request);
        $search = trim((string) $request->input('q', ''));

        $query = Equipo::with('club')->withCount('jugadores')
            ->orderByRaw("COALESCE(updated_at, '1970-01-01') DESC")
            ->orderBy('id_equipo', 'desc');

        if (session('admin_role') == 2) {
            $query->where('id_club', session('admin_id_club'));
        }

        if ($search !== '') {
            $like = '%' . $search . '%';
            $query->where(function ($q) use ($like, $search) {
                $q->where('categoria', 'like', $like)
                  ->orWhere('division', 'like', $like)
                  ->orWhereHas('club', function ($c) use ($like) {
                      $c->where('nombre', 'like', $like);
                  });

                if (ctype_digit($search)) {
                    $q->orWhere('id_equipo', (int) $search);
                }
            });
        }

        $equipos = $query->get();
        return view('admin.equipos.index', compact('equipos', 'search'));
    }

    public function equiposCreate(Request $request)
    {
        $this->checkGeneralAdmin($request);
        
        if (session('admin_role') == 1) {
            $clubes = Club::orderBy('nombre')->get();
        } else {
            $clubes = Club::where('id_club', session('admin_id_club'))->get();
        }

        $categorias = \App\Models\Categoria::orderBy('nombre')->get();
        return view('admin.equipos.form', ['equipo' => null, 'clubes' => $clubes, 'categorias' => $categorias]);
    }

    public function equiposStore(Request $request)
    {
        $this->checkGeneralAdmin($request);
        
        $data = $request->validate([
            'id_club'    => session('admin_role') == 1 ? 'required|integer|exists:clubes,id_club' : 'nullable',
            'categoria'  => 'required|string|max:20',
            'division'   => 'required|string|max:5',
        ]);

        if (session('admin_role') == 2) {
            $data['id_club'] = session('admin_id_club');
        }

        Equipo::create($data);
        return redirect()->route('admin.equipos.index')->with('admin_msg', 'Equipo creado correctamente.');
    }

    public function equiposEdit(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $equipo = Equipo::findOrFail($id);

        if (session('admin_role') == 2 && $equipo->id_club != session('admin_id_club')) {
            abort(403, 'No tienes permiso para editar este equipo.');
        }

        if (session('admin_role') == 1) {
            $clubes = Club::orderBy('nombre')->get();
        } else {
            $clubes = Club::where('id_club', session('admin_id_club'))->get();
        }

        $categorias = \App\Models\Categoria::orderBy('nombre')->get();
        return view('admin.equipos.form', compact('equipo', 'clubes', 'categorias'));
    }

    public function equiposUpdate(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $equipo = Equipo::findOrFail($id);

        if (session('admin_role') == 2 && $equipo->id_club != session('admin_id_club')) {
            abort(403, 'No tienes permiso para editar este equipo.');
        }

        $data = $request->validate([
            'id_club'    => session('admin_role') == 1 ? 'required|integer|exists:clubes,id_club' : 'nullable',
            'categoria'  => 'required|string|max:20',
            'division'   => 'required|string|max:5',
        ]);

        if (session('admin_role') == 2) {
            $data['id_club'] = session('admin_id_club');
        }

        $equipo->update($data);
        return redirect()->route('admin.equipos.index')->with('admin_msg', 'Equipo actualizado correctamente.');
    }

    public function equiposDestroy(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $equipo = Equipo::findOrFail($id);

        if (session('admin_role') == 2 && $equipo->id_club != session('admin_id_club')) {
            abort(403, 'No tienes permiso para eliminar este equipo.');
        }

        $equipo->delete();
        return redirect()->route('admin.equipos.index')->with('admin_msg', 'Equipo eliminado correctamente.');
    }

    public function equipoJugadores($id)
    {
        $this->checkGeneralAdmin(request());
        $equipo = Equipo::with('club', 'jugadores')->findOrFail($id);

        if (session('admin_role') == 2 && $equipo->id_club != session('admin_id_club')) {
            abort(403, 'No tienes permiso para gestionar jugadores de este equipo.');
        }

        return view('admin.equipos.jugadores', compact('equipo'));
    }

    public function equipoAddJugador(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $equipo = Equipo::findOrFail($id);

        if (session('admin_role') == 2 && $equipo->id_club != session('admin_id_club')) {
            abort(403);
        }

        $data = $request->validate([
            'id_jugador' => 'required|string|exists:jugadores,id_jugador',
        ]);

        // Verificar que el jugador sea del mismo club, salvo que el equipo pertenezca a una selección
        $jugador = Jugador::where('id_jugador', $data['id_jugador'])->firstOrFail();
        $esSeleccion = $equipo->club && $equipo->club->es_seleccion;
        if (!$esSeleccion && $jugador->id_club_actual != $equipo->id_club) {
            return back()->with('admin_error', 'El jugador no pertenece al mismo club que el equipo.');
        }

        // Evitar duplicados
        if ($equipo->jugadores()->where('jugador_equipo.id_jugador', $data['id_jugador'])->exists()) {
            return back()->with('admin_error', 'El jugador ya está asignado a este equipo.');
        }

        $equipo->jugadores()->attach($data['id_jugador'], ['fecha_alta' => now()]);

        return back()->with('admin_msg', 'Jugador asignado correctamente.');
    }

    public function equipoRemoveJugador($id, $id_jugador)
    {
        $this->checkGeneralAdmin(request());
        $equipo = Equipo::findOrFail($id);

        if (session('admin_role') == 2 && $equipo->id_club != session('admin_id_club')) {
            abort(403);
        }

        $equipo->jugadores()->detach($id_jugador);

        return back()->with('admin_msg', 'Jugador removido del equipo.');
    }

    public function jugadoresSearchAjax(Request $request)
    {
        $this->checkGeneralAdmin($request);
        $q = $request->input('q');
        
        $query = Jugador::query();

        if (session('admin_role') == 2) {
            $query->where('id_club_actual', session('admin_id_club'));
        }

        if ($q) {
            $query->where(function($sub) use ($q) {
                $sub->where('nombre', 'like', "%$q%")
                    ->orWhere('apellido', 'like', "%$q%")
                    ->orWhere('documento', 'like', "%$q%");
            });
        }

        $jugadores = $query->limit(10)->get(['id_jugador', 'nombre', 'apellido', 'documento']);

        return response()->json($jugadores);
    }

    public function jugadoresCategoriaPorEdad(Request $request)
    {
        $this->checkGeneralAdmin($request);
        $fecha = $request->input('fecha');
        if (!$fecha) return response()->json(['categoria' => 'Sin categoría']);

        $anio = \Carbon\Carbon::parse($fecha)->format('Y');
        $edadCategoria = date('Y') - $anio;

        $categoria = \App\Models\Categoria::where('edad_min', '<=', $edadCategoria)
                          ->where('edad_max', '>=', $edadCategoria)
                          ->first();

        return response()->json(['categoria' => $categoria ? $categoria->nombre : 'Sin categoría']);
    }


    // ══════════════════════════════════
    //  JUGADORES
    // ══════════════════════════════════
    public function jugadoresIndex(Request $request)
    {
        $this->checkGeneralAdmin($request);
        $search = $request->input('q');
        $query = Jugador::with('clubActual')
            ->orderByRaw("COALESCE(updated_at, '1970-01-01') DESC")
            ->orderByRaw('CAST(id_jugador AS UNSIGNED) DESC');

        if (session('admin_role') == 2) {
            $query->where('id_club_actual', session('admin_id_club'));
        }

        if ($search) {
            // Tokenizar por espacios. Cada token debe matchear como INICIO de palabra
            // dentro de "apellido nombre" — así "Man Adriel" encuentra a MAN ADRIEL pero
            // no a ROMAN PEREZ. El DNI sigue aceptando match por substring directo.
            $tokens = array_values(array_filter(
                array_map(
                    fn($t) => preg_replace('/[^\p{L}\p{N}]/u', '', $t),
                    preg_split('/\s+/u', $search)
                ),
                fn($t) => mb_strlen($t) > 0
            ));

            $query->where(function ($q) use ($tokens, $search) {
                $q->where('documento', 'like', '%' . $search . '%');

                if (!empty($tokens)) {
                    $q->orWhere(function ($qq) use ($tokens) {
                        foreach ($tokens as $token) {
                            $qq->whereRaw(
                                "LOWER(CONCAT(apellido, ' ', nombre)) REGEXP ?",
                                ['[[:<:]]' . mb_strtolower($token)]
                            );
                        }
                    });
                }
            });
        }

        $jugadores = $query->paginate(25);
        $clubes = session('admin_role') == 1 ? Club::orderBy('nombre')->get() : [];
        
        return view('admin.jugadores.index', compact('jugadores', 'search', 'clubes'));
    }

    public function jugadoresCreate(Request $request)
    {
        $this->checkGeneralAdmin($request);
        $clubes = Club::orderBy('nombre')->get();
        return view('admin.jugadores.form', ['jugador' => null, 'clubes' => $clubes]);
    }

    public function jugadoresStore(Request $request)
    {
        $this->checkGeneralAdmin($request);
        $data = $request->validate([
            'documento'        => 'required|string|max:20',
            'nombre'           => 'required|string|max:100',
            'apellido'         => 'required|string|max:100',
            'fecha_nacimiento' => 'required|date',
            'id_club_actual'   => 'nullable|integer|exists:clubes,id_club',
            'id_club_origen'   => 'required|integer|exists:clubes,id_club',
        ]);

        // Verificar si el DNI ya existe. withTrashed: el índice UNIQUE de la BD considera
        // también soft-deleted, así que la validación debe contemplarlos para no caer en error 1062.
        $existente = Jugador::withTrashed()->where('documento', $data['documento'])->first();
        if ($existente) {
            if ($existente->trashed()) {
                return back()->withInput()->withErrors([
                    'documento' => "Este DNI pertenece a un jugador que está en la papelera. Restauralo desde allí o contactá al superadmin."
                ]);
            }
            $clubNombre = $existente->clubActual ? $existente->clubActual->nombre : 'Sin Club';
            return back()->withInput()->withErrors([
                'documento' => "No se puede registrar al jugador dado que ya pertenece al club $clubNombre."
            ]);
        }
        
        if (session('admin_role') == 2) {
            $data['id_club_actual'] = session('admin_id_club');
            // Nota: id_club_origen podría ser el mismo o diferente, 
            // pero el Id del jugador siempre se basa en el de origen.
        }

        $data['nombre']   = strtoupper(trim($data['nombre']));
        $data['apellido'] = strtoupper(trim($data['apellido']));
        $data['activo'] = 0; // Se valida luego en /asociate
        if (isset($data['fecha_nacimiento'])) {
            $data['edad'] = \Carbon\Carbon::parse($data['fecha_nacimiento'])->age;
            // $data['categoria'] ya no se asigna, ahora es dinámica
        }

        $idClub = $data['id_club_origen'];
        $yearFull = \Carbon\Carbon::parse($data['fecha_nacimiento'])->format('Y');
        $data['id_jugador'] = $this->generarIdJugador($idClub, $yearFull);

        Jugador::create($data);
        return redirect()->route('admin.jugadores.index')->with('admin_msg', 'Jugador creado correctamente. Puede activarse en /asociate.');
    }

    private function generarIdJugador($idClub, $yearFull)
    {
        $yearShort = \Carbon\Carbon::parse($yearFull . '-01-01')->format('y');
        $prefix = $idClub . $yearShort;
        $secuencia = $this->obtenerSiguienteSecuencia($idClub, $yearFull, $prefix);

        return sprintf('%s%02d', $prefix, $secuencia);
    }

    private function obtenerSiguienteSecuencia($idClub, $yearFull, $prefix)
    {
        // withTrashed: incluir jugadores soft-deleted para no reusar IDs y chocar con la PK.
        $ultimoId = (string)Jugador::withTrashed()
            ->where('id_jugador', 'LIKE', $prefix . '%')
            ->whereRaw("id_jugador REGEXP '^[0-9]+$'")
            ->orderByRaw('CAST(id_jugador AS UNSIGNED) DESC')
            ->value('id_jugador');

        $secuencia = 1;
        if ($ultimoId && str_starts_with($ultimoId, $prefix)) {
            $secuenciaStr = substr($ultimoId, strlen($prefix));
            $secuencia = (int)$secuenciaStr + 1;
        }
        return $secuencia;
    }

    public function jugadoresImport(Request $request)
    {
        $this->checkGeneralAdmin($request);
        $request->validate([
            'csv_file' => 'required|file|mimes:csv,txt',
            'id_club'  => session('admin_role') == 1 ? 'nullable|integer' : 'nullable'
        ]);

        $file = $request->file('csv_file');
        $handle = fopen($file->getRealPath(), 'r');
        
        $successCount = 0;
        $omittedCount = 0;
        $errorCount = 0;
        $teamAssignedCount = 0;
        $errors = [];

        // Determinar Club Target
        $targetClubId = session('admin_role') == 2 ? session('admin_id_club') : ($request->input('id_club') ?? 99);

        $currentCategory = null;
        $formatType = 'legacy'; // legacy, cab, internal
        $localSequences = []; // Cache para evitar duplicados en el mismo loop

        // Leer primeras líneas para detectar formato (el CAB puede empezar con una categoría)
        $detectLines = [];
        for ($i=0; $i<5; $i++) {
            $l = fgets($handle);
            if ($l) $detectLines[] = $l;
        }
        rewind($handle);

        $fullContentSample = implode("\n", $detectLines);
        if (str_contains($fullContentSample, 'ESTADO LICENCIA') || str_contains($fullContentSample, 'TIPO') || str_contains($fullContentSample, 'JUGADOR')) {
            $formatType = 'cab';
        } elseif (str_contains($fullContentSample, 'DNI;Apellido;Nombre;Fecha Nacimiento')) {
            $formatType = 'internal';
        }

        while (($row = fgetcsv($handle, 1000, ";")) !== FALSE) {
            if (empty(array_filter($row))) continue;

            // --- DETECCION DE CATEGORIA (Solo CAB) ---
            // Si la primera columna tiene texto y el resto está vacío, es una categoría (ej: "PREMINI B;;;;;")
            if ($formatType == 'cab' && !empty($row[0]) && empty(array_filter(array_slice($row, 1)))) {
                 $currentCategory = trim($row[0]);
                 continue;
            }

            // Saltos de cabecera
            if ($formatType == 'cab' && $row[0] == 'ESTADO LICENCIA') continue;
            if ($formatType == 'internal' && $row[0] == 'DNI') continue;

            try {
                $dni = ''; $apellido = ''; $nombre = ''; $fechaNac = ''; 
                $idClubOrigen = $targetClubId;
                $idClubActual = $targetClubId;
                $isJugador = true;

                if ($formatType == 'cab') {
                    // Formato: ESTADO LICENCIA;NIF;NOMBRE;FECHA_ALTA;BAJA;TIPO;FECHA NACIMIENTO;NACIONALIDAD
                    if (count($row) < 7) continue;
                    $dni = trim($row[1]);
                    $fullName = trim($row[2]); // "APELLIDO, NOMBRE"
                    $tipo = strtoupper(trim($row[5]));
                    $fechaRaw = trim($row[6]); // d/m/Y

                    if ($tipo !== 'JUGADOR') {
                        $isJugador = false;
                    }

                    if ($isJugador) {
                        $parts = explode(',', $fullName);
                        $apellido = trim($parts[0]);
                        $nombre = isset($parts[1]) ? trim($parts[1]) : '';
                        
                        // Parsear d/m/Y
                        $dateParts = explode('/', $fechaRaw);
                        if (count($dateParts) == 3) {
                            $fechaNac = "{$dateParts[2]}-{$dateParts[1]}-{$dateParts[0]}";
                        }
                    }
                } elseif ($formatType == 'internal') {
                    // Formato: DNI; Apellido; Nombre; Fecha Nacimiento (d/m/Y); ID Club Origen; ID Club Actual; Categoria; Activo
                    $dni = trim($row[0]);
                    $apellido = trim($row[1]);
                    $nombre = trim($row[2]);
                    $fechaRaw = trim($row[3]);
                    $idClubOrigen = isset($row[4]) ? (int)trim($row[4]) : $targetClubId;
                    $idClubActual = isset($row[5]) ? (int)trim($row[5]) : $targetClubId;

                    $dateParts = explode('/', $fechaRaw);
                    if (count($dateParts) == 3) {
                        $fechaNac = "{$dateParts[2]}-{$dateParts[1]}-{$dateParts[0]}";
                    }
                } else {
                    // Formato Legado: DNI; Apellido; Nombre; ddmmaaaa; id_club_origen
                    if (count($row) < 4) continue;
                    $dni = trim($row[0]);

                    // SEGURIDAD: Si el DNI no es numérico en formato legado, probablemente sea una cabecera o ruido
                    if (!is_numeric($dni)) continue;

                    $apellido = trim($row[1]);
                    $nombre = trim($row[2]);
                    $fechaRaw = trim($row[3]); // ddmmaaaa
                    $idClubOrigen = isset($row[4]) ? (int)trim($row[4]) : $targetClubId;

                    if (strlen($fechaRaw) == 8) {
                        $fechaNac = substr($fechaRaw, 4, 4) . "-" . substr($fechaRaw, 2, 2) . "-" . substr($fechaRaw, 0, 2);
                    }
                }

                if (!$isJugador || !$dni || !$apellido || !$nombre || !$fechaNac) continue;

                // Verificar existencia
                $jugador = Jugador::where('documento', $dni)->first();
                $anioNac = date('Y', strtotime($fechaNac));

                $data = [
                    'documento'        => $dni,
                    'apellido'         => strtoupper(trim($apellido)),
                    'nombre'           => strtoupper(trim($nombre)),
                    'fecha_nacimiento' => $fechaNac,
                    'id_club_origen'   => $idClubOrigen,
                    'id_club_actual'   => $idClubActual,
                    'edad'             => \Carbon\Carbon::parse($fechaNac)->age,
                ];

                if (!$jugador) {
                    // Generar ID con cache local para evitar colisiones en el mismo loop
                    $prefix = $idClubOrigen . date('y', strtotime($fechaNac));
                    if (!isset($localSequences[$prefix])) {
                        // Obtener la base inicial de la BDD
                        $localSequences[$prefix] = $this->obtenerSiguienteSecuencia($idClubOrigen, $anioNac, $prefix);
                    } else {
                        $localSequences[$prefix]++;
                    }
                    
                    $data['id_jugador'] = sprintf('%s%02d', $prefix, $localSequences[$prefix]);
                    $data['activo'] = 0;
                    $jugador = Jugador::create($data);
                    $successCount++;
                } else {
                    // El usuario prefiere NO pisar datos si el jugador ya existe
                    $omittedCount++;
                }

                // --- MATCHING DE EQUIPO ---
                if ($currentCategory && $jugador) {
                    $equipo = Equipo::where('id_club', $idClubActual)
                        ->where('categoria', 'LIKE', $currentCategory)
                        ->first();
                    if ($equipo) {
                        // Evitar duplicados en pivot
                        if (!$equipo->jugadores()->where('jugador_equipo.id_jugador', $jugador->id_jugador)->exists()) {
                            $equipo->jugadores()->attach($jugador->id_jugador, ['fecha_alta' => now()]);
                            $teamAssignedCount++;
                        }
                    }
                }

            } catch (\Exception $e) {
                $errorCount++;
                $errors[] = "Error en fila DNI $dni: " . $e->getMessage();
            }
        }
        fclose($handle);

        $msg = "Importación finalizada ({$formatType}). $successCount nuevos creados, $omittedCount ya registrados (no modificados).";
        if ($teamAssignedCount > 0) $msg .= " $teamAssignedCount asignaciones a equipos realizadas.";

        if ($errorCount > 0) {
            return redirect()->route('admin.jugadores.index')->with('admin_msg', $msg)->with('admin_error', implode(" | ", array_slice($errors, 0, 5)));
        }

        return redirect()->route('admin.jugadores.index')->with('admin_msg', $msg);
    }

    public function jugadoresExport(Request $request)
    {
        $this->checkGeneralAdmin($request);

        $headers = [
            "Content-type"        => "text/csv; charset=UTF-8",
            "Content-Disposition" => "attachment; filename=jugadores_" . date('Ymd_His') . ".csv",
            "Pragma"              => "no-cache",
            "Cache-Control"       => "must-revalidate, post-check=0, pre-check=0",
            "Expires"             => "0"
        ];

        $callback = function() {
            $file = fopen('php://output', 'w');
            // Añadir BOM para Excel (UTF-8)
            fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));
            
            // Cabeceras
            fputcsv($file, [
                'DNI', 'Apellido', 'Nombre', 'Fecha Nacimiento', 'ID Club Origen', 'ID Club Actual', 'Categoria', 'Activo'
            ], ";");

            $query = Jugador::orderBy('apellido');

            if (session('admin_role') == 2) {
                $query->where('id_club_actual', session('admin_id_club'));
            }

            $jugadores = $query->get();

            foreach ($jugadores as $j) {
                fputcsv($file, [
                    $j->documento,
                    $j->apellido,
                    $j->nombre,
                    $j->fecha_nacimiento ? $j->fecha_nacimiento->format('d/m/Y') : '',
                    $j->id_club_origen ?? 99,
                    $j->id_club_actual ?? 99,
                    $j->categoria_calculada,
                    $j->activo ? 'SI' : 'NO'
                ], ";");
            }
            fclose($file);
        };

        return response()->stream($callback, 200, $headers);
    }

    public function jugadoresEdit(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $jugador = Jugador::findOrFail($id);
        
        if (session('admin_role') == 2 && $jugador->id_club_actual != session('admin_id_club')) {
            abort(403, 'No puedes editar un jugador que no pertenece a tu club.');
        }

        $clubes = session('admin_role') == 1 ? Club::orderBy('nombre')->get() : [];
        return view('admin.jugadores.form', compact('jugador', 'clubes'));
    }

    public function jugadoresUpdate(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $jugador = Jugador::findOrFail($id);

        if (session('admin_role') == 2 && $jugador->id_club_actual != session('admin_id_club')) {
            abort(403, 'No puedes editar un jugador que no pertenece a tu club.');
        }

        $data = $request->validate([
            'documento'        => 'required|string|max:20|unique:jugadores,documento,' . $id . ',id_jugador',
            'nombre'           => 'required|string|max:100',
            'apellido'         => 'required|string|max:100',
            'fecha_nacimiento' => 'required|date',
            'id_club_actual'   => 'nullable|integer|exists:clubes,id_club',
            'id_club_origen'   => 'nullable|integer|exists:clubes,id_club',
        ]);
        
        if (session('admin_role') == 2) {
            $data['id_club_actual'] = session('admin_id_club'); // Forzamos a no cambiarlo
        }

        if (isset($data['fecha_nacimiento'])) {
            $data['edad'] = \Carbon\Carbon::parse($data['fecha_nacimiento'])->age;
        }

        $jugador->update($data);
        return redirect()->route('admin.jugadores.index')->with('admin_msg', 'Jugador actualizado correctamente.');
    }

    public function jugadoresDestroy(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $jugador = Jugador::findOrFail($id);
        
        if (session('admin_role') == 2 && $jugador->id_club_actual != session('admin_id_club')) {
            abort(403, 'No tienes permiso para eliminar este jugador.');
        }

        $jugador->delete();
        return redirect()->route('admin.jugadores.index')->with('admin_msg', 'Jugador eliminado correctamente.');
    }

    // ══════════════════════════════════
    //  EVENTOS
    // ══════════════════════════════════
    public function escanearQr(Request $request)
    {
        $this->checkGeneralAdmin($request);
        
        $query = Evento::orderByRaw("
            CAST(SUBSTRING_INDEX(nombre_evento, 'PARTIDO ', -1) AS UNSIGNED) ASC,
            nombre_evento ASC,
            fecha_evento ASC,
            hora_inicio ASC
        ");

        $ahora = \Carbon\Carbon::now();
        $query->where(function($q) use ($ahora) {
            $q->where(function($sub) {
                $sub->whereNull('marcador_local')
                    ->orWhereNull('marcador_visitante');
            })
            ->orWhere('fecha_evento', '>', $ahora->toDateString())
            ->orWhere(function($q2) use ($ahora) {
                $q2->where('fecha_evento', '=', $ahora->toDateString())
                   ->where('hora_fin', '>', $ahora->toTimeString());
            });
        });

        if (session('admin_role') == 2) {
            $idClub = session('admin_id_club');
            $query->where(function ($q) use ($idClub) {
                $q->whereHas('equipoLocal', function ($q2) use ($idClub) {
                    $q2->where('id_club', $idClub);
                })->orWhereHas('equipoVisitante', function ($q2) use ($idClub) {
                    $q2->where('id_club', $idClub);
                });
            });
        }

        $eventos = $query->get();
        return view('admin.escanear_qr', compact('eventos'));
    }

    public function eventosIndex(Request $request)
    {
        $this->checkGeneralAdmin($request);
        
        $estado = $request->get('estado', 'todos');
        $query = Evento::with(['equipoLocal.club', 'equipoVisitante.club'])->orderBy('fecha_evento', 'desc');
        
        $tz = 'America/Argentina/Buenos_Aires';
        $ahora = \Carbon\Carbon::now($tz);

        // Filtro por estado
        if ($estado == 'finalizados') {
            $query->whereNotNull('marcador_local')
                  ->whereNotNull('marcador_visitante')
                  ->where(function($q) use ($ahora) {
                      $q->where('fecha_evento', '<', $ahora->toDateString())
                        ->orWhere(function($q2) use ($ahora) {
                            $q2->where('fecha_evento', '=', $ahora->toDateString())
                               ->where('hora_fin', '<=', $ahora->toTimeString());
                        });
                  });
        } elseif ($estado == 'pendientes') {
            $query->where(function($q) use ($ahora) {
                $q->whereNull('marcador_local')
                  ->orWhereNull('marcador_visitante')
                  ->orWhere('fecha_evento', '>', $ahora->toDateString())
                  ->orWhere(function($q2) use ($ahora) {
                      $q2->where('fecha_evento', '=', $ahora->toDateString())
                         ->where('hora_fin', '>', $ahora->toTimeString());
                  });
            });
        }

        if (session('admin_role') == 2) {
            $idClub = session('admin_id_club');
            $query->where(function($q) use ($idClub) {
                $q->whereHas('equipoLocal', function ($q2) use ($idClub) {
                    $q2->where('id_club', $idClub);
                })->orWhereHas('equipoVisitante', function ($q2) use ($idClub) {
                    $q2->where('id_club', $idClub);
                });
            });
        }

        $eventos = $query->orderBy('id_evento', 'desc')->get();
        return view('admin.eventos.index', compact('eventos', 'estado'));
    }

    public function eventosCreate(Request $request)
    {
        $this->checkSuperAdmin($request);
        $equipos = Equipo::with('club')->get();
        $torneos = \App\Models\Torneo::orderBy('nombre')->get();
        $torneoEquipos = DB::table('torneo_equipo')->get();
        return view('admin.eventos.form', ['evento' => null, 'equipos' => $equipos, 'torneos' => $torneos, 'torneoEquipos' => $torneoEquipos]);
    }

    public function eventosStore(Request $request)
    {
        $this->checkSuperAdmin($request);
        $data = $request->validate([
            'nombre_evento'       => 'nullable|string|max:200',
            'id_torneo'           => 'nullable|integer|exists:torneos,id',
            'fecha_evento'        => 'required|date',
            'hora_inicio'         => 'required',
            'hora_fin'            => 'required',
            'sede'                => 'required|string|max:200',
            'id_equipo_local'     => 'required|integer|exists:equipos,id_equipo',
            'id_equipo_visitante' => 'required|integer|exists:equipos,id_equipo',
            'precio'              => 'nullable|numeric|min:0',
            'marcador_local'      => 'nullable|integer|min:0',
            'marcador_visitante'  => 'nullable|integer|min:0',
        ]);

        // Validaciones Deportivas
        $local = Equipo::findOrFail($data['id_equipo_local']);
        $visit = Equipo::findOrFail($data['id_equipo_visitante']);

        if ($local->categoria != $visit->categoria) {
            return back()->withInput()->withErrors(['id_equipo_visitante' => 'Los equipos deben pertenecer a la misma categoría.']);
        }

        if (!empty($data['id_torneo'])) {
            $torneo = \App\Models\Torneo::findOrFail($data['id_torneo']);
            $localInTorneo = $torneo->equipos()->where('equipos.id_equipo', $data['id_equipo_local'])->first();
            $visitInTorneo = $torneo->equipos()->where('equipos.id_equipo', $data['id_equipo_visitante'])->first();

            if (!$localInTorneo || !$visitInTorneo) {
                return back()->withInput()->withErrors(['id_torneo' => 'Uno o ambos equipos no están inscritos en este torneo.']);
            }

            if ($localInTorneo->pivot->grupo != $visitInTorneo->pivot->grupo) {
                return back()->withInput()->withErrors(['id_equipo_visitante' => 'Los equipos deben pertenecer al mismo grupo dentro del torneo.']);
            }
        }

        // Autogenerar ID
        $data['id_evento'] = bin2hex(random_bytes(4)); // 8 caracteres

        if (empty($data['nombre_evento'])) {
            $grupoName = 'General';
            if (!empty($data['id_torneo'])) {
                $rel = DB::table('torneo_equipo')
                    ->where('id_torneo', $data['id_torneo'])
                    ->where('id_equipo', $data['id_equipo_local'])
                    ->first();
                if ($rel) $grupoName = $rel->grupo ?? 'General';
            }
            $data['nombre_evento'] = ($local->club->nombre ?? 'Local') . ' vs ' . ($visit->club->nombre ?? 'Visitante') . " ({$grupoName})";
        }

        Evento::create($data);
        return redirect()->route('admin.eventos.index')->with('admin_msg', 'Evento creado correctamente.');
    }

    public function eventosEdit(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $evento = Evento::findOrFail($id);

        if (session('admin_role') == 2) {
            $idClub = session('admin_id_club');
            $isLocal = $evento->equipoLocal && $evento->equipoLocal->id_club == $idClub;
            $isVisitante = $evento->equipoVisitante && $evento->equipoVisitante->id_club == $idClub;
            if (!$isLocal && !$isVisitante) {
                abort(403, 'No tienes permiso para editar este evento.');
            }
        }
        $equipos = Equipo::with('club')->get();
        $torneos = \App\Models\Torneo::orderBy('nombre')->get();
        $torneoEquipos = DB::table('torneo_equipo')->get();
        return view('admin.eventos.form', compact('evento', 'equipos', 'torneos', 'torneoEquipos'));
    }

    public function eventosUpdate(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        $evento = Evento::findOrFail($id);

        if (session('admin_role') == 2) {
            $idClub = session('admin_id_club');
            $isLocal = $evento->equipoLocal && $evento->equipoLocal->id_club == $idClub;
            $isVisitante = $evento->equipoVisitante && $evento->equipoVisitante->id_club == $idClub;
            if (!$isLocal && !$isVisitante) {
                abort(403, 'No tienes permiso para editar este evento.');
            }

            // Club admins only edit limite_qr_jugador
            $data = $request->validate([
                'limite_qr_jugador' => 'required|integer|min:0',
            ]);
            $evento->update(['limite_qr_jugador' => $data['limite_qr_jugador']]);
            return redirect()->route('admin.eventos.index')->with('admin_msg', 'Pase de QRs para el evento actualizado.');
        }

        // Si es edición, restringimos los campos permitidos según solicitud del usuario
        if ($id) {
            $data = $request->validate([
                'fecha_evento'      => 'required|date',
                'hora_inicio'       => 'required',
                'hora_fin'          => 'required',
                'limite_qr_jugador' => 'nullable|integer|min:0',
                'marcador_local'    => 'nullable|integer|min:0',
                'marcador_visitante' => 'nullable|integer|min:0',
                'nombre_evento'      => 'nullable|string|max:200',
            ]);
            
            // Conservar valores que no deberían cambiar para que la validación posterior (deporte) no falle
            $data['id_torneo'] = $evento->id_torneo;
            $data['id_equipo_local'] = $evento->id_equipo_local;
            $data['id_equipo_visitante'] = $evento->id_equipo_visitante;
            
            // Si el nombre viene vacío en edición, también lo autogeneramos? 
            // El usuario pidió que se setee automáticamente al no poner nada.
            if (empty($data['nombre_evento'])) {
                $local = Equipo::findOrFail($data['id_equipo_local']);
                $visit = Equipo::findOrFail($data['id_equipo_visitante']);
                $grupoName = 'General';
                if (!empty($data['id_torneo'])) {
                    $rel = DB::table('torneo_equipo')
                        ->where('id_torneo', $data['id_torneo'])
                        ->where('id_equipo', $data['id_equipo_local'])
                        ->first();
                    if ($rel) $grupoName = $rel->grupo ?? 'General';
                }
                $data['nombre_evento'] = ($local->club->nombre ?? 'Local') . ' vs ' . ($visit->club->nombre ?? 'Visitante') . " ({$grupoName})";
            }

            $data['sede'] = $evento->sede;
            $data['precio'] = $evento->precio;
        } else {
            $data = $request->validate([
                'nombre_evento'       => 'nullable|string|max:200',
                'id_torneo'           => 'nullable|integer|exists:torneos,id',
                'fecha_evento'        => 'required|date',
                'hora_inicio'         => 'required',
                'hora_fin'            => 'required',
                'sede'                => 'nullable|string|max:200',
                'id_equipo_local'     => 'nullable|integer|exists:equipos,id_equipo',
                'id_equipo_visitante' => 'nullable|integer|exists:equipos,id_equipo',
                'precio'              => 'nullable|numeric|min:0',
                'limite_qr_jugador'   => 'nullable|integer|min:0',
                'marcador_local'      => 'nullable|integer|min:0',
                'marcador_visitante'  => 'nullable|integer|min:0',
            ]);
        }

        // Validaciones Deportivas
        if ($data['id_equipo_local'] && $data['id_equipo_visitante']) {
            $local = Equipo::findOrFail($data['id_equipo_local']);
            $visit = Equipo::findOrFail($data['id_equipo_visitante']);

            if ($local->categoria != $visit->categoria) {
                return back()->withInput()->withErrors(['id_equipo_visitante' => 'Los equipos deben pertenecer a la misma categoría.']);
            }

            if (!empty($data['id_torneo'])) {
                $torneo = \App\Models\Torneo::findOrFail($data['id_torneo']);
                $localInTorneo = $torneo->equipos()->where('equipos.id_equipo', $data['id_equipo_local'])->first();
                $visitInTorneo = $torneo->equipos()->where('equipos.id_equipo', $data['id_equipo_visitante'])->first();

                if (!$localInTorneo || !$visitInTorneo) {
                    return back()->withInput()->withErrors(['id_torneo' => 'Uno o ambos equipos no están inscritos en este torneo.']);
                }

                if ($localInTorneo->pivot->grupo != $visitInTorneo->pivot->grupo) {
                    return back()->withInput()->withErrors(['id_equipo_visitante' => 'Los equipos deben pertenecer al mismo grupo dentro del torneo.']);
                }
            }

            // Autogenerar Nombre si es nulo
            if (empty($data['nombre_evento'])) {
                $data['nombre_evento'] = ($local->club->nombre ?? 'Local') . ' vs ' . ($visit->club->nombre ?? 'Visitante');
            }
        }

        $evento->update($data);
        return redirect()->route('admin.eventos.index')->with('admin_msg', 'Evento actualizado correctamente.');
    }

    public function eventosDestroy(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $evento = Evento::findOrFail($id);
        
        // Al eliminar un evento, también eliminamos sus QRs para que no queden "huérfanos"
        $evento->qrCodes()->delete();
        
        $evento->delete();
        return redirect()->route('admin.eventos.index')->with('admin_msg', 'Evento eliminado correctamente.');
    }

    // ══════════════════════════════════
    //  PROMOCIONES / LUGARES
    // ══════════════════════════════════
    public function promocionesIndex(Request $request)
    {
        $this->checkSuperAdmin($request);
        $promociones = Promocion::withCount('promoQrs')->orderBy('id', 'desc')->get();
        return view('admin.promociones.index', compact('promociones'));
    }

    public function promocionesCreate(Request $request)
    {
        $this->checkSuperAdmin($request);
        return view('admin.promociones.form', ['promocion' => null]);
    }

    public function promocionesStore(Request $request)
    {
        $this->checkSuperAdmin($request);
        $data = $request->validate([
            'nombre'            => 'required|string|max:100',
            'direccion'         => 'required|string|max:150',
            'lat'               => 'nullable|numeric',
            'lng'               => 'nullable|numeric',
            'contacto'          => 'nullable|string|max:100',
            'descripcion'       => 'nullable',
            'descripcion_lugar' => 'nullable',
            'categoria'         => 'nullable|string|max:50',
            'imagen_file'       => 'nullable|image|max:2048',
        ]);

        if ($request->hasFile('imagen_file')) {
            $path = app(ImageOptimizer::class)->storeAndOptimize($request->file('imagen_file'), 'promos');
            $data['imagen'] = $path;
        }
        unset($data['imagen_file']);

        Promocion::create($data);
        return redirect()->route('admin.promociones.index')->with('admin_msg', 'Promoción creada correctamente.');
    }

    public function promocionesEdit(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $promocion = Promocion::findOrFail($id);
        return view('admin.promociones.form', compact('promocion'));
    }

    public function promocionesUpdate(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $promocion = Promocion::findOrFail($id);
        $data = $request->validate([
            'nombre'            => 'required|string|max:100',
            'direccion'         => 'required|string|max:150',
            'lat'               => 'nullable|numeric',
            'lng'               => 'nullable|numeric',
            'contacto'          => 'nullable|string|max:100',
            'descripcion'       => 'nullable',
            'descripcion_lugar' => 'nullable',
            'categoria'         => 'nullable|string|max:50',
            'imagen_file'       => 'nullable|image|max:2048',
        ]);

        if ($request->hasFile('imagen_file')) {
            $path = app(ImageOptimizer::class)->storeAndOptimize($request->file('imagen_file'), 'promos');
            $data['imagen'] = $path;
        }
        unset($data['imagen_file']);

        $promocion->update($data);
        return redirect()->route('admin.promociones.index')->with('admin_msg', 'Promoción actualizada correctamente.');
    }

    public function promocionesDestroy(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $promocion = Promocion::findOrFail($id);
        $promocion->delete();
        return redirect()->route('admin.promociones.index')->with('admin_msg', 'Promoción eliminada correctamente.');
    }

    // ══════════════════════════════════
    //  NOTICIAS
    // ══════════════════════════════════
    public function noticiasIndex(Request $request)
    {
        $this->checkSuperAdmin($request);
        $noticias = Noticia::orderBy('fecha', 'desc')->orderBy('id', 'desc')->get();
        return view('admin.noticias.index', compact('noticias'));
    }

    public function noticiasCreate(Request $request)
    {
        $this->checkSuperAdmin($request);
        $torneos = \App\Models\Torneo::orderBy('nombre')->get();
        return view('admin.noticias.form', ['noticia' => null, 'torneos' => $torneos]);
    }

    public function noticiasStore(Request $request)
    {
        $this->checkSuperAdmin($request);
        $data = $request->validate([
            'titulo'    => 'required|string|max:200',
            'contenido' => 'required',
            'imagen_file' => 'nullable|image|max:5120',
            'categoria' => 'nullable|string|max:50',
            'id_torneo' => 'nullable|integer|exists:torneos,id',
        ]);

        if ($request->hasFile('imagen_file')) {
            $path = app(ImageOptimizer::class)->storeAndOptimize($request->file('imagen_file'), 'noticias');
            $data['imagen'] = 'storage/' . $path;
        }
        unset($data['imagen_file']);

        $data['fecha'] = now();
        Noticia::create($data);
        return redirect()->route('admin.noticias.index')->with('admin_msg', 'Noticia creada correctamente.');
    }

    public function noticiasEdit(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $noticia = Noticia::findOrFail($id);
        $torneos = \App\Models\Torneo::orderBy('nombre')->get();
        return view('admin.noticias.form', compact('noticia', 'torneos'));
    }

    public function noticiasUpdate(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $noticia = Noticia::findOrFail($id);
        $data = $request->validate([
            'titulo'    => 'required|string|max:200',
            'contenido' => 'required',
            'imagen_file' => 'nullable|image|max:5120',
            'categoria' => 'nullable|string|max:50',
            'id_torneo' => 'nullable|integer|exists:torneos,id',
        ]);

        if ($request->hasFile('imagen_file')) {
            // Eliminar imagen anterior si existe
            if ($noticia->imagen && !str_starts_with($noticia->imagen, 'http') && file_exists(public_path($noticia->imagen))) {
                @unlink(public_path($noticia->imagen));
            }
            $path = app(ImageOptimizer::class)->storeAndOptimize($request->file('imagen_file'), 'noticias');
            $data['imagen'] = 'storage/' . $path;
        }
        unset($data['imagen_file']);

        $noticia->update($data);
        return redirect()->route('admin.noticias.index')->with('admin_msg', 'Noticia actualizada correctamente.');
    }

    public function noticiasDestroy(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $noticia = Noticia::findOrFail($id);
        $noticia->delete();
        return redirect()->route('admin.noticias.index')->with('admin_msg', 'Noticia eliminada correctamente.');
    }

    // ══════════════════════════════════
    //  ESCANEAR / VALIDAR QR
    // ══════════════════════════════════
    public function validarQr(Request $request)
    {
        $this->checkGeneralAdmin($request);
        $id_qr = $request->input('id_qr');
        $id_evento = $request->input('id_evento');

        if (!$id_qr) {
            return response()->json(['valid' => false, 'message' => 'QR inválido.']);
        }

        $qr = QrCode::with('evento', 'jugador', 'aficionado')->where('id_qr', $id_qr)->first();

        if (!$qr) {
            return response()->json(['valid' => false, 'message' => 'QR no encontrado.']);
        }

        // Si se seleccionó un evento, exigir que coincida
        if ($id_evento && (string)$qr->id_evento !== (string)$id_evento) {
            return response()->json(['valid' => false, 'message' => 'Este QR corresponde a otro evento.']);
        }

        // Verificar vigencia del evento
        if ($qr->evento) {
            $ahora = \Carbon\Carbon::now();

            $f = ($qr->evento->fecha_evento instanceof \Carbon\Carbon) ? $qr->evento->fecha_evento->format('Y-m-d') : substr($qr->evento->fecha_evento, 0, 10);
            $h1 = ($qr->evento->hora_inicio instanceof \Carbon\Carbon) ? $qr->evento->hora_inicio->format('H:i:s') : $qr->evento->hora_inicio;
            $h2 = ($qr->evento->hora_fin instanceof \Carbon\Carbon) ? $qr->evento->hora_fin->format('H:i:s') : $qr->evento->hora_fin;

            $inicio = \Carbon\Carbon::parse("$f $h1");
            $fin    = \Carbon\Carbon::parse("$f $h2");

            if ($fin->lessThanOrEqualTo($inicio)) {
                $fin->addDay();
            }

            if ($ahora < $inicio) {
                return response()->json(['valid' => false, 'message' => '⏳ El evento todavía no comenzó.']);
            }
            if ($ahora > $fin) {
                return response()->json(['valid' => false, 'message' => 'Evento finalizado, QR inválido.']);
            }
        }

        // Verificar escaneos restantes
        if ((int)$qr->escaneos_restantes <= 0) {
            return response()->json(['valid' => false, 'message' => '❌ QR ya utilizado.']);
        }

        // Decrementar escaneos (concurrency-safe via DB)
        $affected = \Illuminate\Support\Facades\DB::table('qr_codes')
            ->where('id_qr', $id_qr)
            ->where('escaneos_restantes', '>', 0)
            ->when($id_evento, function ($query) use ($id_evento) {
                return $query->where('id_evento', $id_evento);
            })
            ->decrement('escaneos_restantes');

        if ($affected === 0) {
            return response()->json(['valid' => false, 'message' => '❌ QR ya utilizado.']);
        }

        // Info del titular según tipo de QR
        $titular = '';
        $categoriaNombre = '';

        if ($qr->jugador) {
            $categoriaNombre = $qr->jugador->categoria_calculada;
        }

        if ($qr->tipo_qr === 'invitado' && $qr->jugador) {
            $titular = 'Jugador: ' . $qr->jugador->nombre . ' ' . $qr->jugador->apellido . ' (' . $categoriaNombre . ')';
        } elseif ($qr->tipo_qr === 'publico') {
            if ($qr->aficionado) {
                $titular = 'Aficionado: ' . $qr->aficionado->nombre . ' ' . $qr->aficionado->apellido;
            } else {
                $titular = 'QR de entrada pública (sin referencia)';
            }
        } else {
            $titular = $qr->jugador
                ? $qr->jugador->nombre . ' ' . $qr->jugador->apellido . ' (' . $categoriaNombre . ')'
                : ($qr->aficionado ? $qr->aficionado->nombre . ' ' . $qr->aficionado->apellido : 'Desconocido');
        }

        $mensajeValido = '✅ Acceso válido — Jugador del evento';
        
        if ($qr->tipo_qr === 'libre_50') {
            $mensajeValido = '✅ Tenés descuento en la entrada, 50% (Jugador Categoría Libre)';
        }

        return response()->json([
            'valid'   => true,
            'message' => $mensajeValido,
            'data'    => [
                'evento'    => $qr->evento ? $qr->evento->nombre_evento : $qr->id_evento,
                'titular'   => $titular,
                'categoria' => $categoriaNombre,
                'tipo'      => $qr->tipo_qr,
                'restantes' => ($qr->escaneos_restantes > 0 ? $qr->escaneos_restantes - 1 : 0),
            ],
        ]);
    }

    // ══════════════════════════════════
    //  CONFIGURACIÓN GENERAL
    // ══════════════════════════════════
    public function settingsIndex(Request $request)
    {
        $this->checkSuperAdmin($request);
        $diasExpiracion = \App\Models\Configuracion::get('dias_expiracion_eventos', 30);
        $backupFreq     = \App\Models\Configuracion::get('backup_frequency', 'daily');
        $emailReportes  = \App\Models\Configuracion::get('email_reportes', 'asociados@onapb.com');
        $lastRun        = \App\Models\Configuracion::get('last_scheduler_run', 'Nunca detectado');
        
        return view('admin.settings', compact('diasExpiracion', 'backupFreq', 'lastRun', 'emailReportes'));
    }

    public function settingsUpdate(Request $request)
    {
        $this->checkSuperAdmin($request);
        $data = $request->validate([
            'dias_expiracion_eventos' => 'required|integer|min:1',
            'backup_frequency'        => 'required|string|in:daily,weekly,monthly',
            'email_reportes'          => 'required|email',
        ]);

        \App\Models\Configuracion::set('dias_expiracion_eventos', $data['dias_expiracion_eventos'], 'Días de antigüedad para borrar eventos y QRs');
        \App\Models\Configuracion::set('backup_frequency', $data['backup_frequency'], 'Frecuencia de backups automáticos (daily, weekly, monthly)');
        \App\Models\Configuracion::set('email_reportes', $data['email_reportes'], 'Email principal para recibir reportes del sistema');

        return back()->with('admin_msg', 'Configuración actualizada correctamente.');
    }

    public function runManualTask(Request $request)
    {
        $this->checkSuperAdmin($request);
        $command = $request->input('command');

        try {
            switch ($command) {
                case 'cleanup':
                    \Illuminate\Support\Facades\Artisan::call('app:cleanup-old-events');
                    $msg = '✅ Tarea de limpieza ejecutada.';
                    break;
                case 'backup':
                    // Usamos backup:run para forzar un backup completo
                    \Illuminate\Support\Facades\Artisan::call('backup:run');
                    $msg = '✅ Proceso de backup iniciado.';
                    break;
                case 'report':
                    \Illuminate\Support\Facades\Artisan::call('reportes:semanal');
                    $msg = '✅ Informe semanal enviado.';
                    break;
                default:
                    throw new \Exception('Comando no reconocido.');
            }

            return back()->with('admin_msg', $msg . ' (Salida: ' . \Illuminate\Support\Facades\Artisan::output() . ')');
        } catch (\Exception $e) {
            \Illuminate\Support\Facades\Log::error("Error ejecutando comando manual: " . $e->getMessage());
            return back()->with('admin_error', 'Error al ejecutar tarea: ' . $e->getMessage());
        }
    }

    // ══════════════════════════════════
    //  SPONSORS
    // ══════════════════════════════════
    public function sponsorsIndex(Request $request)
    {
        $this->checkSuperAdmin($request);
        $sponsors = Sponsor::orderBy('orden')->latest()->get();
        return view('admin.sponsors.index', compact('sponsors'));
    }

    public function sponsorsCreate(Request $request)
    {
        $this->checkSuperAdmin($request);
        return view('admin.sponsors.form');
    }

    public function sponsorsStore(Request $request)
    {
        $this->checkSuperAdmin($request);
        $data = $request->validate([
            'nombre' => 'required|string|max:100',
            'imagen' => 'required|image|max:5120',
            'url'    => 'nullable|url|max:255',
            'activo' => 'nullable|boolean',
            'orden'  => 'nullable|integer',
        ]);

        if ($request->hasFile('imagen')) {
            $path = app(ImageOptimizer::class)->storeAndOptimize($request->file('imagen'), 'sponsors');
            $data['imagen'] = 'storage/' . $path;
        }

        $data['activo'] = $request->has('activo');
        $data['orden'] = $data['orden'] ?? 0;

        Sponsor::create($data);

        return redirect()->route('admin.sponsors.index')->with('admin_msg', 'Sponsor creado correctamente.');
    }

    public function sponsorsEdit(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $sponsor = Sponsor::findOrFail($id);
        return view('admin.sponsors.form', compact('sponsor'));
    }

    public function sponsorsUpdate(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $sponsor = Sponsor::findOrFail($id);

        $data = $request->validate([
            'nombre' => 'required|string|max:100',
            'imagen' => 'nullable|image|max:5120',
            'url'    => 'nullable|url|max:255',
            'activo' => 'nullable|boolean',
            'orden'  => 'nullable|integer',
        ]);

        if ($request->hasFile('imagen')) {
            // Eliminar imagen anterior si existe
            if ($sponsor->imagen && file_exists(public_path($sponsor->imagen))) {
                @unlink(public_path($sponsor->imagen));
            }

            $path = app(ImageOptimizer::class)->storeAndOptimize($request->file('imagen'), 'sponsors');
            $data['imagen'] = 'storage/' . $path;
        }

        $data['activo'] = $request->has('activo');
        $data['orden'] = $data['orden'] ?? 0;

        $sponsor->update($data);

        return redirect()->route('admin.sponsors.index')->with('admin_msg', 'Sponsor actualizado correctamente.');
    }

    public function sponsorsDestroy(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $sponsor = Sponsor::findOrFail($id);

        if ($sponsor->imagen && file_exists(public_path($sponsor->imagen))) {
            @unlink(public_path($sponsor->imagen));
        }

        $sponsor->delete();

        return redirect()->route('admin.sponsors.index')->with('admin_msg', 'Sponsor eliminado correctamente.');
    }

    // ══════════════════════════════════
    //  TORNEOS
    // ══════════════════════════════════
    public function torneosIndex(Request $request)
    {
        $this->checkSuperAdmin($request);
        $torneos = \App\Models\Torneo::withCount('equipos')->latest()->get();
        return view('admin.torneos.index', compact('torneos'));
    }

    public function torneosCreate(Request $request)
    {
        $this->checkSuperAdmin($request);
        return view('admin.torneos.form', ['torneo' => null]);
    }

    public function torneosStore(Request $request)
    {
        $this->checkSuperAdmin($request);
        $data = $request->validate([
            'nombre'       => 'required|string|max:100',
            'fecha_inicio' => 'nullable|date',
            'fecha_fin'    => 'nullable|date',
        ]);
        \App\Models\Torneo::create($data);
        return redirect()->route('admin.torneos.index')->with('admin_msg', 'Torneo creado correctamente.');
    }

    public function torneosEdit(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $torneo = \App\Models\Torneo::with('equipos.club')->findOrFail($id);
        $clubes = Club::with('equipos')->orderBy('nombre')->get();
        return view('admin.torneos.form', compact('torneo', 'clubes'));
    }

    public function torneosUpdate(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $torneo = \App\Models\Torneo::findOrFail($id);
        $data = $request->validate([
            'nombre'       => 'required|string|max:100',
            'fecha_inicio' => 'nullable|date',
            'fecha_fin'    => 'nullable|date',
        ]);
        $torneo->update($data);
        return redirect()->route('admin.torneos.index')->with('admin_msg', 'Torneo actualizado correctamente.');
    }

    public function torneosDestroy(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $torneo = \App\Models\Torneo::findOrFail($id);
        $torneo->delete();
        return redirect()->route('admin.torneos.index')->with('admin_msg', 'Torneo eliminado correctamente.');
    }

    public function torneoAddEquipo(Request $request, $id)
    {
        $this->checkSuperAdmin($request);
        $torneo = \App\Models\Torneo::findOrFail($id);
        $data = $request->validate([
            'id_equipo' => 'required|integer|exists:equipos,id_equipo',
            'grupo'     => 'nullable|string|max:50',
        ]);

        if ($torneo->equipos()->where('torneo_equipo.id_equipo', $data['id_equipo'])->exists()) {
            return back()->with('admin_error', 'El equipo ya está asignado a este torneo.');
        }

        $torneo->equipos()->attach($data['id_equipo'], ['grupo' => $data['grupo']]);
        return back()->with('admin_msg', 'Equipo asignado al torneo correctamente.');
    }

    public function torneoRemoveEquipo($id, $id_equipo)
    {
        $this->checkSuperAdmin(request());
        $torneo = \App\Models\Torneo::findOrFail($id);
        $torneo->equipos()->detach($id_equipo);
        return back()->with('admin_msg', 'Equipo removido del torneo.');
    }

    public function eventosStats($id)
    {
        $this->checkGeneralAdmin(request());
        $evento = Evento::with(['equipoLocal.jugadores', 'equipoVisitante.jugadores'])->findOrFail($id);
        
        // Restricción para Administradores de Club: Solo partidos donde participa su club
        if (session('admin_role') == 2) {
            $idClub = session('admin_id_club');
            if ($evento->equipoLocal->id_club != $idClub && $evento->equipoVisitante->id_club != $idClub) {
                abort(403, 'No tienes permiso para gestionar estadísticas de este partido.');
            }
        }

        $stats = \App\Models\EventoJugador::where('id_evento', $id)->get()->keyBy('id_jugador');
        return view('admin.eventos.stats', compact('evento', 'stats'));
    }

    public function eventosStatsStore(Request $request, $id)
    {
        $this->checkGeneralAdmin($request);
        
        // Validación de seguridad para Edición
        if (session('admin_role') == 2) {
            $evento = Evento::findOrFail($id);
            $idClub = session('admin_id_club');
            if ($evento->equipoLocal->id_club != $idClub && $evento->equipoVisitante->id_club != $idClub) {
                abort(403, 'No tienes permiso para editar estadísticas de este partido.');
            }
        }

        $request->validate([
            'stats' => 'required|array',
            'stats.*.puntos' => 'required|integer|min:0',
            'stats.*.faltas' => 'required|integer|min:0|max:5',
        ]);

        foreach ($request->stats as $id_jugador => $vals) {
            // Si es Admin de Club, solo puede guardar sus propios jugadores
            if (session('admin_role') == 2) {
                $jugador = Jugador::find($id_jugador);
                if (!$jugador || $jugador->id_club_actual != session('admin_id_club')) {
                    continue;
                }
            }

            \App\Models\EventoJugador::updateOrCreate(
                ['id_evento' => $id, 'id_jugador' => $id_jugador],
                ['puntos' => $vals['puntos'], 'faltas' => $vals['faltas']]
            );
        }

        return redirect()->route('admin.eventos.index')->with('admin_msg', 'Estadísticas guardadas correctamente.');
    }
}
