<?php

namespace App\Http\Controllers;

use App\Models\Deposito;
use App\Models\Retiro;
use App\Models\Usuario;
use App\Models\AuditoriaAdmin;
use App\Models\Notificacion;
use App\Services\FirebaseService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class AdminTransaccionesController extends Controller
{
    protected FirebaseService $firebaseService;

    public function __construct(FirebaseService $firebaseService)
    {
        $this->firebaseService = $firebaseService;
    }

    public function index(Request $request)
    {
        $depositoEstado = $request->query('deposito_estado', 'todos');
        $retiroEstado = $request->query('retiro_estado', 'todos');
        $busqueda = trim((string) $request->query('q', ''));
        $metodo = $request->query('metodo', 'todos');
        $orden = $request->query('orden', 'recientes');
        $desde = $request->query('desde');
        $hasta = $request->query('hasta');

        $depositosQuery = Deposito::query()
            ->with(['usuario:id,nombre,email', 'aprobador:id,nombre'])
            ->when($depositoEstado !== 'todos', function (Builder $query) use ($depositoEstado) {
                $query->where('estado', $depositoEstado);
            })
            ->when($metodo !== 'todos', function (Builder $query) use ($metodo) {
                $query->where('metodo_pago', $metodo);
            })
            ->when($busqueda !== '', function (Builder $query) use ($busqueda) {
                $query->where(function (Builder $subQuery) use ($busqueda) {
                    $subQuery->where('referencia_pago', 'like', "%{$busqueda}%")
                             ->orWhereHas('usuario', function (Builder $usuarioQuery) use ($busqueda) {
                                 $usuarioQuery->where('nombre', 'like', "%{$busqueda}%")
                                               ->orWhere('email', 'like', "%{$busqueda}%");
                             });
                });
            })
            ->when($desde, function (Builder $query) use ($desde) {
                $query->whereDate('created_at', '>=', $desde);
            })
            ->when($hasta, function (Builder $query) use ($hasta) {
                $query->whereDate('created_at', '<=', $hasta);
            });

        $depositosQuery->when($orden, function (Builder $query) use ($orden) {
            return match ($orden) {
                'monto_desc' => $query->orderByDesc('monto'),
                'monto_asc' => $query->orderBy('monto'),
                'antiguedad' => $query->orderBy('created_at'),
                default => $query->orderByDesc('created_at'),
            };
        });

        $retirosQuery = Retiro::query()
            ->with(['usuario:id,nombre,email,banco,tipo_cuenta,numero_cuenta', 'procesador:id,nombre'])
            ->when($retiroEstado !== 'todos', function (Builder $query) use ($retiroEstado) {
                $query->where('estado', $retiroEstado);
            })
            ->when($busqueda !== '', function (Builder $query) use ($busqueda) {
                $query->where(function (Builder $subQuery) use ($busqueda) {
                    $subQuery->where('referencia_transaccion', 'like', "%{$busqueda}%")
                             ->orWhereHas('usuario', function (Builder $usuarioQuery) use ($busqueda) {
                                 $usuarioQuery->where('nombre', 'like', "%{$busqueda}%")
                                               ->orWhere('email', 'like', "%{$busqueda}%");
                             });
                });
            })
            ->when($desde, function (Builder $query) use ($desde) {
                $query->whereDate('created_at', '>=', $desde);
            })
            ->when($hasta, function (Builder $query) use ($hasta) {
                $query->whereDate('created_at', '<=', $hasta);
            });

        $retirosQuery->when($orden, function (Builder $query) use ($orden) {
            return match ($orden) {
                'monto_desc' => $query->orderByDesc('monto'),
                'monto_asc' => $query->orderBy('monto'),
                'antiguedad' => $query->orderBy('created_at'),
                default => $query->orderByDesc('created_at'),
            };
        });

        $depositos = $depositosQuery->paginate((int) $request->query('por_pagina_depositos', 15) ?: 15)->withQueryString();
        $retiros = $retirosQuery->paginate((int) $request->query('por_pagina_retiros', 15) ?: 15)->withQueryString();

        $inicioMes = Carbon::now()->startOfMonth();
        $stats = [
            'depositos_pendientes' => Deposito::where('estado', 'pendiente')->count(),
            'depositos_pendientes_monto' => $this->formatCurrency(Deposito::where('estado', 'pendiente')->sum('monto')),
            'depositos_mes' => $this->formatCurrency(
                Deposito::where('estado', 'aprobado')->whereBetween('fecha_aprobacion', [$inicioMes, Carbon::now()])->sum('monto')
            ),
            'retiros_pendientes' => Retiro::where('estado', 'pendiente')->count(),
            'retiros_pendientes_monto' => $this->formatCurrency(Retiro::where('estado', 'pendiente')->sum('monto')),
            'retiros_mes' => $this->formatCurrency(
                Retiro::where('estado', 'completado')->whereBetween('fecha_procesado', [$inicioMes, Carbon::now()])->sum('monto')
            ),
        ];

        $resumenEstadosDepositos = Deposito::select('estado', DB::raw('COUNT(*) as total'), DB::raw('SUM(monto) as monto'))
            ->groupBy('estado')
            ->get()
            ->map(function ($row) {
                return [
                    'estado' => $row->estado ?? 'sin_estado',
                    'total' => (int) $row->total,
                    'monto' => $this->formatCurrency($row->monto),
                ];
            });

        $resumenEstadosRetiros = Retiro::select('estado', DB::raw('COUNT(*) as total'), DB::raw('SUM(monto) as monto'))
            ->groupBy('estado')
            ->get()
            ->map(function ($row) {
                return [
                    'estado' => $row->estado ?? 'sin_estado',
                    'total' => (int) $row->total,
                    'monto' => $this->formatCurrency($row->monto),
                ];
            });

        $metodos = Deposito::select('metodo_pago')
            ->whereNotNull('metodo_pago')
            ->distinct()
            ->orderBy('metodo_pago')
            ->pluck('metodo_pago');

        $admins = Usuario::query()
            ->whereRaw('LOWER(estado) = ?', ['admin'])
            ->orderBy('nombre')
            ->get();

        $currentUser = $request->user();
        if ($currentUser instanceof Usuario && $currentUser->esAdministrador() && !$admins->contains('id', $currentUser->id)) {
            $admins->prepend($currentUser);
        }

        $admins = $admins->unique('id')->values();

        return view('admin.transacciones.index', [
            'depositos' => $depositos,
            'retiros' => $retiros,
            'stats' => $stats,
            'resumenEstadosDepositos' => $resumenEstadosDepositos,
            'resumenEstadosRetiros' => $resumenEstadosRetiros,
            'metodos' => $metodos,
            'admins' => $admins,
            'filtros' => [
                'deposito_estado' => $depositoEstado,
                'retiro_estado' => $retiroEstado,
                'metodo' => $metodo,
                'orden' => $orden,
                'q' => $busqueda,
                'desde' => $desde,
                'hasta' => $hasta,
            ],
        ]);
    }

    public function aprobarDeposito(Request $request, Deposito $deposito)
    {
        $validated = $request->validate([
            'admin_id' => ['required', 'exists:usuarios,id'],
            'notas_admin' => ['nullable', 'string', 'max:500'],
        ]);

        if ($deposito->estado === 'aprobado') {
            return redirect()->back()->with('status', 'El depósito ya estaba aprobado previamente.');
        }

        $deposito->loadMissing('usuario');

        try {
            DB::transaction(function () use ($deposito, $validated) {
                $deposito->aprobar($validated['admin_id'], $validated['notas_admin'] ?? null);
                $deposito->refresh()->load('usuario');

                AuditoriaAdmin::registrar(
                    $validated['admin_id'],
                    'aprobar_deposito',
                    'transacciones',
                    sprintf(
                        'Depósito #%d aprobado por %s para usuario ID %d',
                        $deposito->id,
                        $this->formatCurrency($deposito->monto),
                        $deposito->usuario_id
                    ),
                    'Deposito',
                    $deposito->id
                );

                if ($deposito->usuario) {
                    $mensaje = sprintf(
                        'Tu depósito de %s ha sido aprobado y acreditado.',
                        $this->formatCurrency($deposito->monto)
                    );

                    $this->crearNotificacionConPush(
                        $deposito->usuario,
                        'success',
                        'Depósito aprobado',
                        $mensaje,
                        [
                            'tipo' => 'deposito',
                            'deposito_id' => $deposito->id,
                            'monto' => $deposito->monto,
                        ]
                    );
                }
            });
        } catch (\Throwable $throwable) {
            Log::error('Error al aprobar depósito desde panel web', [
                'deposito_id' => $deposito->id,
                'exception' => $throwable->getMessage(),
            ]);

            return redirect()->back()->with('error', 'No se pudo aprobar el depósito. Revisa la información e inténtalo de nuevo.');
        }

        return redirect()->back()->with('status', 'Depósito aprobado correctamente.');
    }

    public function rechazarDeposito(Request $request, Deposito $deposito)
    {
        $validated = $request->validate([
            'admin_id' => ['required', 'exists:usuarios,id'],
            'motivo' => ['required', 'string', 'max:500'],
        ]);

        if ($deposito->estado === 'rechazado') {
            return redirect()->back()->with('status', 'El depósito ya estaba marcado como rechazado.');
        }

        $deposito->loadMissing('usuario');

        try {
            DB::transaction(function () use ($deposito, $validated) {
                $deposito->rechazar($validated['admin_id'], $validated['motivo']);
                $deposito->refresh()->load('usuario');

                AuditoriaAdmin::registrar(
                    $validated['admin_id'],
                    'rechazar_deposito',
                    'transacciones',
                    sprintf(
                        'Depósito #%d rechazado. Motivo: %s',
                        $deposito->id,
                        $validated['motivo']
                    ),
                    'Deposito',
                    $deposito->id
                );

                if ($deposito->usuario) {
                    $mensaje = sprintf(
                        'Tu depósito de %s fue rechazado. Motivo: %s',
                        $this->formatCurrency($deposito->monto),
                        $validated['motivo']
                    );

                    $this->crearNotificacionConPush(
                        $deposito->usuario,
                        'error',
                        'Depósito rechazado',
                        $mensaje,
                        [
                            'tipo' => 'deposito',
                            'deposito_id' => $deposito->id,
                        ]
                    );
                }
            });
        } catch (\Throwable $throwable) {
            Log::error('Error al rechazar depósito desde panel web', [
                'deposito_id' => $deposito->id,
                'exception' => $throwable->getMessage(),
            ]);

            return redirect()->back()->with('error', 'No se pudo registrar el rechazo del depósito. Intenta nuevamente.');
        }

        return redirect()->back()->with('status', 'Depósito actualizado como rechazado.');
    }

    public function marcarRetiroEnProceso(Request $request, Retiro $retiro)
    {
        $validated = $request->validate([
            'admin_id' => ['required', 'exists:usuarios,id'],
            'notas_admin' => ['nullable', 'string', 'max:500'],
        ]);

        if ($retiro->estado !== 'pendiente' && $retiro->estado !== 'en_proceso') {
            return redirect()->back()->with('error', 'Solo puedes mover a proceso los retiros que siguen pendientes.');
        }

        $retiro->loadMissing('usuario');

        try {
            DB::transaction(function () use ($retiro, $validated) {
                $retiro->estado = 'en_proceso';
                $retiro->procesado_por = $validated['admin_id'];
                $retiro->notas_admin = $validated['notas_admin'] ?? null;
                $retiro->save();

                $retiro->refresh()->load('usuario');

                $adminNombre = Usuario::find($validated['admin_id'])?->nombre;

                AuditoriaAdmin::registrar(
                    $validated['admin_id'],
                    'marcar_retiro_en_proceso',
                    'transacciones',
                    sprintf(
                        'Retiro #%d marcado en proceso%s.',
                        $retiro->id,
                        $adminNombre ? " por {$adminNombre}" : ''
                    ),
                    'Retiro',
                    $retiro->id
                );

                if ($retiro->usuario) {
                    $mensaje = sprintf(
                        'Tu retiro por %s está siendo gestionado por el equipo administrativo.',
                        $this->formatCurrency($retiro->monto)
                    );

                    $this->crearNotificacionConPush(
                        $retiro->usuario,
                        'info',
                        'Retiro en proceso',
                        $mensaje,
                        [
                            'tipo' => 'retiro',
                            'retiro_id' => $retiro->id,
                        ]
                    );
                }
            });
        } catch (\Throwable $throwable) {
            Log::error('Error al marcar retiro en proceso desde panel web', [
                'retiro_id' => $retiro->id,
                'exception' => $throwable->getMessage(),
            ]);

            return redirect()->back()->with('error', 'No se pudo actualizar el estado del retiro a en proceso.');
        }

        return redirect()->back()->with('status', 'Retiro actualizado a "en proceso".');
    }

    public function aprobarRetiro(Request $request, Retiro $retiro)
    {
        $validated = $request->validate([
            'admin_id' => ['required', 'exists:usuarios,id'],
            'referencia_transaccion' => ['required', 'string', 'max:255'],
            'notas_admin' => ['nullable', 'string', 'max:500'],
        ]);

        if ($retiro->estado === 'completado') {
            return redirect()->back()->with('status', 'El retiro ya estaba marcado como completado.');
        }

        $retiro->loadMissing('usuario');

        try {
            DB::transaction(function () use ($retiro, $validated) {
                $retiro->aprobar(
                    $validated['admin_id'],
                    $validated['referencia_transaccion'],
                    $validated['notas_admin'] ?? null
                );

                $retiro->refresh()->load('usuario');

                AuditoriaAdmin::registrar(
                    $validated['admin_id'],
                    'aprobar_retiro',
                    'transacciones',
                    sprintf(
                        'Retiro #%d aprobado por %s. Referencia: %s',
                        $retiro->id,
                        $this->formatCurrency($retiro->monto),
                        $validated['referencia_transaccion']
                    ),
                    'Retiro',
                    $retiro->id
                );

                if ($retiro->usuario) {
                    $mensaje = sprintf(
                        'Tu retiro de %s ha sido procesado. Referencia: %s',
                        $this->formatCurrency($retiro->monto),
                        $validated['referencia_transaccion']
                    );

                    $this->crearNotificacionConPush(
                        $retiro->usuario,
                        'success',
                        'Retiro completado',
                        $mensaje,
                        [
                            'tipo' => 'retiro',
                            'retiro_id' => $retiro->id,
                            'monto' => $retiro->monto,
                            'referencia' => $validated['referencia_transaccion'],
                        ]
                    );
                }
            });
        } catch (\Throwable $throwable) {
            Log::error('Error al aprobar retiro desde panel web', [
                'retiro_id' => $retiro->id,
                'exception' => $throwable->getMessage(),
            ]);

            return redirect()->back()->with('error', 'No se pudo completar el retiro. Verifica el saldo pendiente e inténtalo nuevamente.');
        }

        return redirect()->back()->with('status', 'Retiro procesado correctamente.');
    }

    public function rechazarRetiro(Request $request, Retiro $retiro)
    {
        $validated = $request->validate([
            'admin_id' => ['required', 'exists:usuarios,id'],
            'motivo' => ['required', 'string', 'max:500'],
        ]);

        if ($retiro->estado === 'rechazado') {
            return redirect()->back()->with('status', 'El retiro ya estaba marcado como rechazado.');
        }

        $retiro->loadMissing('usuario');

        try {
            DB::transaction(function () use ($retiro, $validated) {
                $retiro->rechazar($validated['admin_id'], $validated['motivo']);
                $retiro->refresh()->load('usuario');

                AuditoriaAdmin::registrar(
                    $validated['admin_id'],
                    'rechazar_retiro',
                    'transacciones',
                    sprintf(
                        'Retiro #%d rechazado. Motivo: %s',
                        $retiro->id,
                        $validated['motivo']
                    ),
                    'Retiro',
                    $retiro->id
                );

                if ($retiro->usuario) {
                    $mensaje = sprintf(
                        'Tu solicitud de retiro por %s fue rechazada. Motivo: %s',
                        $this->formatCurrency($retiro->monto),
                        $validated['motivo']
                    );

                    $this->crearNotificacionConPush(
                        $retiro->usuario,
                        'warning',
                        'Retiro rechazado',
                        $mensaje,
                        [
                            'tipo' => 'retiro',
                            'retiro_id' => $retiro->id,
                        ]
                    );
                }
            });
        } catch (\Throwable $throwable) {
            Log::error('Error al rechazar retiro desde panel web', [
                'retiro_id' => $retiro->id,
                'exception' => $throwable->getMessage(),
            ]);

            return redirect()->back()->with('error', 'No se pudo registrar el rechazo del retiro.');
        }

        return redirect()->back()->with('status', 'Retiro actualizado como rechazado. El saldo fue reintegrado.');
    }

    private function formatCurrency($value): string
    {
        $numeric = $value !== null ? (float) $value : 0.0;
        return '$' . number_format($numeric, 0, ',', '.');
    }

    private function crearNotificacionConPush(Usuario $usuario, string $tipo, string $titulo, string $mensaje, array $datosAdicionales = []): void
    {
        try {
            $notificacion = Notificacion::create([
                'usuario_id' => $usuario->id,
                'tipo' => $tipo,
                'titulo' => $titulo,
                'mensaje' => $mensaje,
                'prioridad' => $tipo === 'error' ? 'alta' : 'media',
                'icono' => $this->obtenerIconoPorTipo($tipo),
                'datos_adicionales' => $datosAdicionales,
            ]);
        } catch (\Throwable $throwable) {
            Log::error('No se pudo registrar notificación administrativa', [
                'usuario_id' => $usuario->id,
                'exception' => $throwable->getMessage(),
            ]);

            return;
        }

        if (empty($usuario->fcm_token)) {
            return;
        }

        try {
            $this->firebaseService->enviarNotificacion(
                $usuario->fcm_token,
                $titulo,
                $mensaje,
                array_merge($datosAdicionales, [
                    'tipo' => $tipo,
                    'usuario_id' => $usuario->id,
                    'notificacion_id' => $notificacion->id,
                ])
            );
        } catch (\Throwable $throwable) {
            Log::warning('Notificación creada pero no se pudo enviar el push FCM', [
                'usuario_id' => $usuario->id,
                'exception' => $throwable->getMessage(),
            ]);
        }
    }

    private function obtenerIconoPorTipo(string $tipo): string
    {
        return match ($tipo) {
            'success' => 'check_circle',
            'error' => 'cancel',
            'warning' => 'warning',
            'info' => 'info',
            default => 'notifications',
        };
    }
}
