<?php

namespace App\Http\Controllers;

use App\Models\AccionLog;
use App\Models\CredencialCliente;
use App\Models\CredencialProfesional;
use App\Models\LogSeguridad;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;

class AuthController extends Controller
{
    public function loginClienteWeb(Request $request): RedirectResponse
    {
        $request->validate([
            'correo' => ['required', 'string'],
            'contra' => ['required', 'string'],
            'website' => ['nullable', 'string', 'max:255'],
        ]);

        if (!$this->honeypotValido($request)) {
            return back()
                ->withInput($request->except(['contra', 'website']))
                ->withErrors(['login_error' => 'No se pudo procesar el inicio de sesión.']);
        }

        $correo = trim((string) $request->input('correo'));
        $contra = (string) $request->input('contra');

        $credencial = CredencialCliente::with('cliente.persona.telefonos')->where('correo', $correo)->first();
        if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
            return back()
                ->withInput($request->except('contra'))
                ->with('login_error', 'Usuario o contraseña incorrectos.');
        }

        if ((int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
            return back()
                ->withInput($request->except('contra'))
                ->with('login_error', 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral');
        }

        $token = Str::random(64);
        $credencial->token = $token;
        $credencial->fecha_hora = now();
        $credencial->save();

        $personaCliente = $credencial->cliente?->persona;
        $nombreCliente = trim((string) ($personaCliente?->nombre ?? ''));
        $apellidoCliente = trim((string) ($personaCliente?->apellido ?? ''));
        $celularCliente = trim((string) ($personaCliente?->telefonos?->first()?->telefono ?? ''));

        $request->session()->regenerate();
        $request->session()->regenerateToken();

        $request->session()->put([
            'cliente_auth' => true,
            'cliente_id' => (int) ($credencial->cliente?->id ?? 0),
            'cliente_token' => $token,
            'cliente_correo' => (string) $credencial->correo,
            'cliente_nombre' => $nombreCliente !== '' ? $nombreCliente : (string) $credencial->correo,
            'cliente_apellido' => $apellidoCliente,
            'cliente_celular' => $celularCliente,
        ]);

        $this->registrarAccionSeguridad(
            (int) ($credencial->cliente?->persona_id ?? 0),
            'CLIENTE',
            (string) $request->ip(),
            'Inició sesión',
            'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
        );

        $this->limpiarThrottle($request, 'login-cliente-web');

        return redirect('/cliente/dashboard')->with('login_success', 'Login exitoso.');
    }

    public function loginPersonalWeb(Request $request): RedirectResponse
    {
        $request->validate([
            'usuario' => ['required', 'string'],
            'contra' => ['required', 'string'],
            'website' => ['nullable', 'string', 'max:255'],
        ]);

        if (!$this->honeypotValido($request)) {
            return back()
                ->withInput($request->except(['contra', 'website']))
                ->withErrors(['login_error' => 'No se pudo procesar el inicio de sesión.']);
        }

        $usuario = trim((string) $request->input('usuario'));
        $contra = (string) $request->input('contra');

        $credencial = CredencialProfesional::with(['administrador.persona', 'profesional.persona'])
            ->where('usuario', $usuario)
            ->first();
        if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
            return back()
                ->withInput($request->except('contra'))
                ->with('login_error', 'Usuario o contraseña incorrectos.');
        }

        if (strtoupper((string) $credencial->rol) !== 'ADMIN' && (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
            return back()
                ->withInput($request->except('contra'))
                ->with('login_error', 'Usted está dado de baja. Comuníquese con el administrador');
        }

        $token = Str::random(64);
        $credencial->token = $token;
        $credencial->fecha_hora = now();
        $credencial->save();

        $rol = strtoupper((string) $credencial->rol);
        $personaProfesional = $credencial->profesional?->persona;
        $nombreProfesional = trim((string) ($personaProfesional?->nombre ?? '') . ' ' . (string) ($personaProfesional?->apellido ?? ''));
        $personaAdmin = $credencial->administrador?->persona;
        $nombreAdmin = trim((string) ($personaAdmin?->nombre ?? '') . ' ' . (string) ($personaAdmin?->apellido ?? ''));
        $nombreSesion = $rol === 'ADMIN' ? $nombreAdmin : $nombreProfesional;

        $request->session()->regenerate();
        $request->session()->regenerateToken();

        $request->session()->put([
            'personal_auth' => true,
            'personal_token' => $token,
            'personal_usuario' => (string) $credencial->usuario,
            'personal_nombre' => $nombreSesion !== '' ? $nombreSesion : (string) $credencial->usuario,
            'personal_apellido' => $rol === 'ADMIN'
                ? ($personaAdmin?->apellido ?? '')
                : ($personaProfesional?->apellido ?? ''),
            'personal_rol' => $rol,
            'personal_credencial_id' => (int) $credencial->id,
        ]);

        $this->registrarAccionSeguridad(
            $rol === 'ADMIN'
                ? (int) ($credencial->administrador?->persona_id ?? 0)
                : (int) ($credencial->profesional?->persona_id ?? 0),
            $rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
            (string) $request->ip(),
            'Inició sesión',
            ($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
        );

        $this->limpiarThrottle($request, 'login-personal-web');

        $redirectPath = $rol === 'ADMIN' ? '/administrador/dashboard' : '/profesional/dashboard';

        return redirect($redirectPath)->with('login_success', 'Login exitoso.');
    }

    public function loginCliente(Request $request): JsonResponse
    {
        $request->validate([
            'correo' => ['required', 'string'],
            'contra' => ['required', 'string'],
        ]);

        $correo = trim((string) $request->input('correo'));
        $contra = (string) $request->input('contra');

        $credencial = CredencialCliente::with('cliente')->where('correo', $correo)->first();
        if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
            return response()->json([
                'success' => false,
                'message' => 'Credenciales invalidas',
            ], 401);
        }

        if ((int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
            return response()->json([
                'success' => false,
                'message' => 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral',
            ], 403);
        }

        $token = Str::random(64);
        $credencial->token = $token;
        $credencial->fecha_hora = now();
        $credencial->save();

        $this->registrarAccionSeguridad(
            (int) ($credencial->cliente?->persona_id ?? 0),
            'CLIENTE',
            (string) $request->ip(),
            'Inició sesión',
            'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
        );

        $this->limpiarThrottle($request, 'login-cliente-api');

        return response()->json([
            'success' => true,
            'data' => [
                'tipo' => 'cliente',
                'id_credencial' => $credencial->id,
                'token' => $token,
            ],
            'message' => 'Login de cliente exitoso',
        ], 200);
    }

    public function loginPersonal(Request $request): JsonResponse
    {
        $request->validate([
            'usuario' => ['required', 'string'],
            'contra' => ['required', 'string'],
        ]);

        $usuario = trim((string) $request->input('usuario'));
        $contra = (string) $request->input('contra');

        $credencial = CredencialProfesional::with(['administrador', 'profesional'])->where('usuario', $usuario)->first();
        if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
            return response()->json([
                'success' => false,
                'message' => 'Credenciales invalidas',
            ], 401);
        }

        if (strtoupper((string) $credencial->rol) !== 'ADMIN' && (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
            return response()->json([
                'success' => false,
                'message' => 'Usted está dado de baja. Comuníquese con el administrador',
            ], 403);
        }

        $token = Str::random(64);
        $credencial->token = $token;
        $credencial->fecha_hora = now();
        $credencial->save();

        $rol = strtoupper((string) $credencial->rol);

        $this->registrarAccionSeguridad(
            $rol === 'ADMIN'
                ? (int) ($credencial->administrador?->persona_id ?? 0)
                : (int) ($credencial->profesional?->persona_id ?? 0),
            $rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
            (string) $request->ip(),
            'Inició sesión',
            ($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
        );

        $this->limpiarThrottle($request, 'login-personal-api');

        return response()->json([
            'success' => true,
            'data' => [
                'tipo' => 'personal',
                'rol' => $credencial->rol,
                'id_credencial' => $credencial->id,
                'token' => $token,
            ],
            'message' => 'Login de personal exitoso',
        ], 200);
    }

    public function login(Request $request): JsonResponse
    {
        $request->validate([
            'identificador' => ['required', 'string'],
            'contra' => ['required', 'string'],
            'tipo' => ['nullable', 'in:cliente,profesional'],
        ]);

        $identificador = trim((string) $request->input('identificador'));
        $contra = (string) $request->input('contra');
        $tipo = $request->input('tipo');

        $credencial = null;
        $tipoDetectado = null;

        if ($tipo === 'cliente') {
            $credencial = CredencialCliente::with('cliente')->where('correo', $identificador)->first();
            $tipoDetectado = 'cliente';
        } elseif ($tipo === 'profesional') {
            $credencial = CredencialProfesional::where('usuario', $identificador)->first();
            $tipoDetectado = 'personal';
        } else {
            $credencial = CredencialCliente::with('cliente')->where('correo', $identificador)->first();
            $tipoDetectado = $credencial ? 'cliente' : null;

            if (!$credencial) {
                $credencial = CredencialProfesional::where('usuario', $identificador)->first();
                $tipoDetectado = $credencial ? 'personal' : null;
            }
        }

        if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
            return response()->json([
                'success' => false,
                'message' => 'Credenciales invalidas',
            ], 401);
        }

        if ($credencial instanceof CredencialCliente
            && (int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
            return response()->json([
                'success' => false,
                'message' => 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral',
            ], 403);
        }

        if ($credencial instanceof CredencialProfesional
            && strtoupper((string) $credencial->rol) !== 'ADMIN'
            && (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
            return response()->json([
                'success' => false,
                'message' => 'Usted está dado de baja. Comuníquese con el administrador',
            ], 403);
        }

        $token = Str::random(64);
        $credencial->token = $token;
        $credencial->fecha_hora = now();
        $credencial->save();

        if ($credencial instanceof CredencialCliente) {
            $this->registrarAccionSeguridad(
                (int) ($credencial->cliente?->persona_id ?? 0),
                'CLIENTE',
                (string) $request->ip(),
                'Inició sesión',
                'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
            );
        } elseif ($credencial instanceof CredencialProfesional) {
            $rolDescripcion = strtoupper((string) $credencial->rol) === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL';

            $this->registrarAccionSeguridad(
                $rolDescripcion === 'ADMIN'
                    ? (int) ($credencial->administrador?->persona_id ?? 0)
                    : (int) ($credencial->profesional?->persona_id ?? 0),
                $rolDescripcion,
                (string) $request->ip(),
                'Inició sesión',
                ($rolDescripcion === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
            );
        }

        $this->limpiarThrottle($request, 'login-api-general');

        return response()->json([
            'success' => true,
            'data' => [
                'tipo' => $tipoDetectado,
                'rol' => $credencial instanceof CredencialProfesional ? $credencial->rol : null,
                'id_credencial' => $credencial->id,
                'token' => $token,
            ],
            'message' => 'Login exitoso',
        ], 200);
    }

    private function honeypotValido(Request $request): bool
    {
        return trim((string) $request->input('website', '')) === '';
    }

    public function logout(Request $request): JsonResponse
    {
        $token = (string) $request->input('token', '');
        if ($token === '') {
            return response()->json([
                'success' => false,
                'message' => 'Token requerido',
            ], 422);
        }

        $credencialCliente = CredencialCliente::with('cliente')->where('token', $token)->first();
        if ($credencialCliente) {
            $this->registrarAccionSeguridad(
                (int) ($credencialCliente->cliente?->persona_id ?? 0),
                'CLIENTE',
                (string) $request->ip(),
                'Cerró sesión',
                'El cliente ID ' . (int) ($credencialCliente->cliente?->id ?? 0) . ' cerró sesión.'
            );

            $credencialCliente->token = null;
            $credencialCliente->fecha_hora = now();
            $credencialCliente->save();

            return response()->json([
                'success' => true,
                'message' => 'Logout exitoso',
            ], 200);
        }

        $credencialProfesional = CredencialProfesional::with(['administrador', 'profesional'])->where('token', $token)->first();
        if ($credencialProfesional) {
            $rol = strtoupper((string) $credencialProfesional->rol);

            $this->registrarAccionSeguridad(
                $rol === 'ADMIN'
                    ? (int) ($credencialProfesional->administrador?->persona_id ?? 0)
                    : (int) ($credencialProfesional->profesional?->persona_id ?? 0),
                $rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
                (string) $request->ip(),
                'Cerró sesión',
                ($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencialProfesional->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencialProfesional->profesional?->id ?? 0)) . ' cerró sesión.'
            );

            $credencialProfesional->token = null;
            $credencialProfesional->fecha_hora = now();
            $credencialProfesional->save();

            return response()->json([
                'success' => true,
                'message' => 'Logout exitoso',
            ], 200);
        }

        return response()->json([
            'success' => false,
            'message' => 'Token invalido',
        ], 401);
    }

    private function credencialValida(string $contraIngresada, string $contraGuardada): bool
    {
        if ($contraIngresada === $contraGuardada) {
            return true;
        }

        return Hash::check($contraIngresada, $contraGuardada);
    }

    private function limpiarThrottle(Request $request, string $prefijo): void
    {
        RateLimiter::clear(md5($prefijo . $request->ip()));
    }

    private function registrarAccionSeguridad(?int $personaId, string $rol, string $ipOrigen, string $accionDescripcion, string $descripcion): void
    {
        if (!$personaId) {
            return;
        }

        $accion = AccionLog::query()->firstWhere('descripcion', $accionDescripcion);

        if (!$accion) {
            return;
        }

        LogSeguridad::create([
            'descripcion' => $descripcion,
            'fechahora' => now(),
            'IPorigen' => $ipOrigen,
            'rol' => $rol,
            'persona_id' => (int) $personaId,
            'accion_id' => (int) $accion->id,
        ]);
    }
}
