<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\EmailVerificationToken;
use App\Models\Rol;
use App\Models\Usuario;
use App\Notifications\VerifyEmailNotification;
use App\Support\HistorialLogger;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;

class UsuarioController extends Controller
{
    public function index(Request $request)
    {
        $query = Usuario::query()->select([
            'id_usuario',
            'nombres',
            'apellidos',
            'correo',
            'rol',
            'id_rol',
            'estado',
            'email_verificado_en',
            'fecha_creacion',
            'fecha_actualizacion',
            'ultimo_acceso',
        ]);

        if ($request->filled('estado')) {
            $query->where('estado', $request->string('estado'));
        }

        if ($request->filled('rol')) {
            $query->where('rol', $request->string('rol'));
        }

        if ($request->filled('q')) {
            $q = trim((string) $request->string('q'));
            if ($q !== '') {
                $query->where(function ($sub) use ($q) {
                    $sub
                        ->where('nombres', 'like', '%'.$q.'%')
                        ->orWhere('apellidos', 'like', '%'.$q.'%')
                        ->orWhere('correo', 'like', '%'.$q.'%');
                });
            }
        }

        $perPage = (int) $request->integer('per_page', 20);
        if ($perPage < 1) {
            $perPage = 1;
        }
        if ($perPage > 100) {
            $perPage = 100;
        }

        $paginator = $query->orderBy('nombres')->orderBy('apellidos')->paginate($perPage);

        return response()->json([
            'data' => $paginator->items(),
            'meta' => [
                'current_page' => $paginator->currentPage(),
                'per_page' => $paginator->perPage(),
                'last_page' => $paginator->lastPage(),
                'total' => $paginator->total(),
            ],
        ]);
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'nombres' => ['required', 'string', 'max:200'],
            'apellidos' => ['required', 'string', 'max:200'],
            'correo' => ['required', 'email', 'max:150', 'unique:usuarios,correo'],
            'password' => ['required', 'string', 'min:8'],
            'rol' => ['nullable', 'string', 'max:100'],
            'id_rol' => ['nullable', 'integer', 'exists:roles,id_rol'],
            'estado' => ['nullable', 'in:activo,inactivo,suspendido'],
        ]);

        $data['password'] = Hash::make($data['password']);
        if (array_key_exists('id_rol', $data) && $data['id_rol']) {
            $rol = Rol::query()->find((int) $data['id_rol']);
            if ($rol) {
                $data['rol'] = $rol->slug;
            }
        } else {
            $slug = (string) ($data['rol'] ?? 'visor');
            $rol = Rol::query()->where('slug', $slug)->first();
            if ($rol) {
                $data['id_rol'] = (int) $rol->getKey();
                $data['rol'] = $rol->slug;
            }
        }

        $usuario = Usuario::create($data);

        HistorialLogger::log(
            $request->user()?->getKey(),
            'usuario_creado',
            'usuarios',
            (string) $usuario->getKey(),
            'Creación de usuario por administrador',
            null,
            $usuario->toArray()
        );

        if (! $usuario->email_verificado_en) {
            if ($usuario->estado === 'activo') {
                $usuario->forceFill(['estado' => 'inactivo'])->save();
            }

            $token = $this->issueEmailVerificationToken($usuario, $request);
            $this->sendEmailVerification($usuario, $token, $request);

            HistorialLogger::log(
                $request->user()?->getKey(),
                'email_verificacion_enviada',
                'usuarios',
                (string) $usuario->getKey(),
                'Envío de verificación de correo (admin)',
                null,
                null
            );
        }

        return response()->json($usuario, 201);
    }

    public function show(Usuario $usuario)
    {
        return response()->json($usuario->load('historial'));
    }

    public function update(Request $request, Usuario $usuario)
    {
        $antes = $usuario->toArray();

        $data = $request->validate([
            'nombres' => ['sometimes', 'required', 'string', 'max:200'],
            'apellidos' => ['sometimes', 'required', 'string', 'max:200'],
            'correo' => ['sometimes', 'required', 'email', 'max:150', 'unique:usuarios,correo,'.$usuario->getKey().',id_usuario'],
            'password' => ['sometimes', 'required', 'string', 'min:8'],
            'rol' => ['sometimes', 'nullable', 'string', 'max:100'],
            'id_rol' => ['sometimes', 'nullable', 'integer', 'exists:roles,id_rol'],
            'estado' => ['sometimes', 'nullable', 'in:activo,inactivo,suspendido'],
            'ultimo_acceso' => ['sometimes', 'nullable', 'date'],
            'email_verificado_en' => ['sometimes', 'nullable', 'date'],
        ]);

        if (array_key_exists('password', $data)) {
            $data['password'] = Hash::make($data['password']);
        }
        if (array_key_exists('id_rol', $data) && $data['id_rol']) {
            $rol = Rol::query()->find((int) $data['id_rol']);
            if ($rol) {
                $data['rol'] = $rol->slug;
            }
        } elseif (array_key_exists('rol', $data) && $data['rol']) {
            $rol = Rol::query()->where('slug', (string) $data['rol'])->first();
            if ($rol) {
                $data['id_rol'] = (int) $rol->getKey();
                $data['rol'] = $rol->slug;
            }
        }

        $usuario->fill($data)->save();

        HistorialLogger::log(
            $request->user()?->getKey(),
            'usuario_actualizado',
            'usuarios',
            (string) $usuario->getKey(),
            'Actualización de usuario',
            $antes,
            $usuario->toArray()
        );

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

    public function destroy(Usuario $usuario)
    {
        $antes = $usuario->toArray();
        $usuario->forceFill(['estado' => 'inactivo'])->save();

        HistorialLogger::log(
            request()->user()?->getKey(),
            'usuario_desactivado',
            'usuarios',
            (string) $usuario->getKey(),
            'Desactivación de usuario',
            $antes,
            $usuario->toArray()
        );

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

    public function stats()
    {
        $countsByEstado = Usuario::query()
            ->selectRaw('estado, COUNT(*) as c')
            ->groupBy('estado')
            ->pluck('c', 'estado');

        $total = (int) ($countsByEstado['activo'] ?? 0)
            + (int) ($countsByEstado['inactivo'] ?? 0)
            + (int) ($countsByEstado['suspendido'] ?? 0);

        $verificados = (int) Usuario::query()->whereNotNull('email_verificado_en')->count();

        return response()->json([
            'total' => $total,
            'activos' => (int) ($countsByEstado['activo'] ?? 0),
            'inactivos' => (int) ($countsByEstado['inactivo'] ?? 0),
            'suspendidos' => (int) ($countsByEstado['suspendido'] ?? 0),
            'verificados' => $verificados,
        ]);
    }

    public function export(Request $request)
    {
        $query = Usuario::query()->select([
            'nombres',
            'apellidos',
            'correo',
            'rol',
            'estado',
            'email_verificado_en',
            'ultimo_acceso',
        ]);

        if ($request->filled('estado')) {
            $query->where('estado', $request->string('estado'));
        }

        if ($request->filled('rol')) {
            $query->where('rol', $request->string('rol'));
        }

        if ($request->filled('q')) {
            $q = trim((string) $request->string('q'));
            if ($q !== '') {
                $query->where(function ($sub) use ($q) {
                    $sub
                        ->where('nombres', 'like', '%'.$q.'%')
                        ->orWhere('apellidos', 'like', '%'.$q.'%')
                        ->orWhere('correo', 'like', '%'.$q.'%');
                });
            }
        }

        $fileName = 'usuarios.xlsx';

        return response()->streamDownload(function () use ($query) {
            $writer = new XLSXWriter();
            $writer->openToFile('php://output');
            $writer->getCurrentSheet()->setName('Usuarios');

            $headerStyle = (new Style())->setFontBold();
            $writer->addRow(Row::fromValues([
                'Nombres',
                'Apellidos',
                'Correo',
                'Rol',
                'Estado',
                'Email verificado',
                'Último acceso',
            ], $headerStyle));

            $query->orderBy('nombres')->orderBy('apellidos')->chunk(500, function ($rows) use ($writer) {
                foreach ($rows as $row) {
                    $writer->addRow(Row::fromValues([
                        (string) ($row->nombres ?? ''),
                        (string) ($row->apellidos ?? ''),
                        (string) ($row->correo ?? ''),
                        (string) ($row->rol ?? ''),
                        (string) ($row->estado ?? ''),
                        $row->email_verificado_en ? (string) $row->email_verificado_en : '',
                        $row->ultimo_acceso ? (string) $row->ultimo_acceso : '',
                    ]));
                }
            });

            $writer->close();
        }, $fileName, [
            'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        ]);
    }

    public function import(Request $request)
    {
        $data = $request->validate([
            'archivo' => ['required', 'file', 'max:5120', 'mimetypes:text/plain,text/csv,application/csv,application/vnd.ms-excel'],
        ]);

        /** @var UploadedFile $file */
        $file = $data['archivo'];
        $path = $file->getRealPath();
        if (! is_string($path) || $path === '') {
            return response()->json(['message' => 'Archivo inválido.'], 422);
        }

        $handle = fopen($path, 'rb');
        if ($handle === false) {
            return response()->json(['message' => 'No se pudo leer el archivo.'], 422);
        }

        $firstLine = fgets($handle);
        if ($firstLine === false) {
            fclose($handle);

            return response()->json(['message' => 'El archivo está vacío.'], 422);
        }

        $delimiter = ',';
        $header = str_getcsv($firstLine, $delimiter);
        if (is_array($header) && count($header) <= 1) {
            $delimiter = ';';
            $header = str_getcsv($firstLine, $delimiter);
        }

        $header = array_map(fn ($h) => strtolower(trim((string) $h)), is_array($header) ? $header : []);
        $idxNombres = array_search('nombres', $header, true);
        $idxApellidos = array_search('apellidos', $header, true);
        $idxCorreo = array_search('correo', $header, true);
        $idxPassword = array_search('password', $header, true);
        $idxRol = array_search('rol', $header, true);
        $idxEstado = array_search('estado', $header, true);
        $idxEmailVerificadoEn = array_search('email_verificado_en', $header, true);

        if ($idxNombres === false || $idxCorreo === false) {
            fclose($handle);

            return response()->json(['message' => 'CSV debe incluir las columnas nombres y correo.'], 422);
        }

        $created = 0;
        $updated = 0;
        $errors = [];
        $rowsProcessed = 0;

        DB::beginTransaction();
        try {
            while (($row = fgetcsv($handle, 0, $delimiter)) !== false) {
                $rowsProcessed++;
                if ($rowsProcessed > 2000) {
                    $errors[] = 'Se superó el límite de 2000 filas.';
                    break;
                }

                $nombres = isset($row[$idxNombres]) ? trim((string) $row[$idxNombres]) : '';
                $correo = isset($row[$idxCorreo]) ? trim((string) $row[$idxCorreo]) : '';

                if ($nombres === '' || $correo === '') {
                    continue;
                }

                $apellidos = $idxApellidos !== false && isset($row[$idxApellidos]) ? trim((string) $row[$idxApellidos]) : '';
                $rolSlug = $idxRol !== false && isset($row[$idxRol]) ? trim((string) $row[$idxRol]) : '';
                if ($rolSlug === '') {
                    $rolSlug = 'visor';
                }

                $estado = 'inactivo';
                if ($idxEstado !== false && isset($row[$idxEstado])) {
                    $v = strtolower(trim((string) $row[$idxEstado]));
                    if (in_array($v, ['activo', 'inactivo', 'suspendido'], true)) {
                        $estado = $v;
                    }
                }

                $emailVerificadoEn = null;
                if ($idxEmailVerificadoEn !== false && isset($row[$idxEmailVerificadoEn])) {
                    $raw = trim((string) $row[$idxEmailVerificadoEn]);
                    if ($raw !== '') {
                        $ts = strtotime($raw);
                        if ($ts !== false) {
                            $emailVerificadoEn = date('Y-m-d H:i:s', $ts);
                        }
                    }
                }

                if ($emailVerificadoEn === null && $estado === 'activo') {
                    $estado = 'inactivo';
                }

                $rol = Rol::query()->where('slug', $rolSlug)->first();
                $idRol = $rol ? (int) $rol->getKey() : null;

                $password = null;
                if ($idxPassword !== false && isset($row[$idxPassword])) {
                    $p = (string) $row[$idxPassword];
                    $p = trim($p);
                    if ($p !== '') {
                        $password = $p;
                    }
                }

                $existing = Usuario::query()->where('correo', $correo)->first();
                if ($existing) {
                    $payload = [
                        'nombres' => $nombres,
                        'apellidos' => $apellidos,
                        'rol' => $rol ? $rol->slug : $rolSlug,
                        'id_rol' => $idRol,
                        'estado' => $estado,
                        'email_verificado_en' => $emailVerificadoEn,
                    ];
                    if ($password !== null && strlen($password) >= 8) {
                        $payload['password'] = Hash::make($password);
                    }
                    $existing->forceFill($payload)->save();
                    $updated++;
                } else {
                    if ($password === null || strlen($password) < 8) {
                        $errors[] = "Password inválido para: {$correo}";

                        continue;
                    }

                    Usuario::create([
                        'nombres' => $nombres,
                        'apellidos' => $apellidos,
                        'correo' => $correo,
                        'password' => Hash::make($password),
                        'rol' => $rol ? $rol->slug : $rolSlug,
                        'id_rol' => $idRol,
                        'estado' => $estado,
                        'email_verificado_en' => $emailVerificadoEn,
                    ]);
                    $created++;
                }
            }

            fclose($handle);

            HistorialLogger::log(
                $request->user()?->getKey(),
                'usuarios_importados',
                'usuarios',
                'bulk',
                'Importación de usuarios',
                null,
                ['creados' => $created, 'actualizados' => $updated, 'errores' => array_slice($errors, 0, 10)]
            );

            DB::commit();
        } catch (\Throwable) {
            fclose($handle);
            DB::rollBack();

            return response()->json(['message' => 'No se pudo importar el archivo.'], 500);
        }

        return response()->json([
            'ok' => true,
            'creados' => $created,
            'actualizados' => $updated,
            'errores' => array_slice($errors, 0, 20),
        ]);
    }

    private function emailVerificationExpiresMinutes(): int
    {
        return (int) env('ASUP_EMAIL_VERIFICATION_EXPIRES_MINUTES', 60);
    }

    private function issueEmailVerificationToken(Usuario $usuario, Request $request): string
    {
        $token = Str::random(80);

        EmailVerificationToken::create([
            'id_usuario' => $usuario->getKey(),
            'token_hash' => hash('sha256', $token),
            'expira_en' => now()->addMinutes($this->emailVerificationExpiresMinutes()),
            'ip_address' => $request->ip(),
            'user_agent' => substr((string) $request->userAgent(), 0, 4000),
        ]);

        return $token;
    }

    private function sendEmailVerification(Usuario $usuario, string $token, Request $request): void
    {
        $frontendBase = $this->resolveFrontendBase($request);
        if ($frontendBase !== '') {
            $url = $frontendBase.'/verify-email?token='.urlencode($token);
            $usuario->notify(new VerifyEmailNotification($url, $this->emailVerificationExpiresMinutes()));

            return;
        }

        $apiBase = rtrim((string) env('ASUP_API_PUBLIC_URL', ''), '/');
        if ($apiBase === '') {
            if (app()->environment('local') || (bool) config('app.debug')) {
                $apiBase = rtrim($request->getSchemeAndHttpHost(), '/').'/public/api';
            } else {
                throw new \RuntimeException('Configura ASUP_FRONTEND_URL o ASUP_API_PUBLIC_URL para generar enlaces de verificación.');
            }
        }

        $url = rtrim((string) $apiBase, '/').'/auth/verify-email?token='.urlencode($token);
        $usuario->notify(new VerifyEmailNotification($url, $this->emailVerificationExpiresMinutes()));
    }

    private function resolveFrontendBase(Request $request): string
    {
        $fromEnv = rtrim((string) env('ASUP_FRONTEND_URL', ''), '/');
        if ($fromEnv === '') {
            return '';
        }

        if (! preg_match('#^https?://#i', $fromEnv)) {
            throw new \RuntimeException('ASUP_FRONTEND_URL must be a valid HTTP/HTTPS URL.');
        }

        return $fromEnv;
    }
}
