<?php

namespace App\Http\Controllers;

use App\Http\Requests\AbrirCajaRequest;
use App\Http\Requests\CorteCajaRequest;
use App\Http\Requests\StoreCajaMovimientoRequest;
use App\Models\Caja;
use App\Models\CajaMovimiento;
use App\Services\CajaCorteService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class CajaController extends Controller
{
    public function __construct(
        private readonly CajaCorteService $cajaCorte,
    ) {}

    /**
     * Estado de caja del usuario: si tiene una abierta, la devuelve.
     */
    public function estado(): JsonResponse
    {
        $caja = Caja::cajaAbiertaDelUsuario(Auth::id());

        if (! $caja) {
            return response()->json([
                'data' => null,
                'mensaje' => 'No tiene una caja abierta.',
                'meta' => null,
                'errors' => null,
            ], JsonResponse::HTTP_OK);
        }

        return response()->json([
            'data' => [
                'id' => $caja->id,
                'nombre' => $caja->nombre,
                'estatus' => $caja->estatus,
                'saldo_inicial' => (float) $caja->saldo_inicial,
                'abierto_at' => $caja->abierto_at?->toIso8601String(),
            ],
            'meta' => null,
            'errors' => null,
        ], JsonResponse::HTTP_OK);
    }

    /**
     * Abrir caja. El usuario solo puede tener una caja abierta a la vez.
     */
    public function abrir(AbrirCajaRequest $request): JsonResponse
    {
        $yaAbierta = Caja::cajaAbiertaDelUsuario(Auth::id());
        if ($yaAbierta) {
            return response()->json([
                'data' => null,
                'errors' => ['caja' => ['Ya tiene una caja abierta. Realice el corte antes de abrir otra.']],
                'meta' => null,
            ], JsonResponse::HTTP_UNPROCESSABLE_ENTITY);
        }

        $data = $request->validated();

        $caja = Caja::create([
            'nombre' => $data['nombre'] ?? 'Caja '.Auth::user()->name,
            'estatus' => Caja::ESTATUS_ABIERTA,
            'saldo_inicial' => (float) ($data['saldo_inicial'] ?? 0),
            'usuario_id' => Auth::id(),
            'abierto_at' => now(),
        ]);

        Log::channel('single')->info('caja.abierta', [
            'usuario_id' => Auth::id(),
            'caja_id' => $caja->id,
            'saldo_inicial' => (float) $caja->saldo_inicial,
            'abierto_at' => $caja->abierto_at?->toIso8601String(),
        ]);

        return response()->json([
            'data' => [
                'id' => $caja->id,
                'nombre' => $caja->nombre,
                'estatus' => $caja->estatus,
                'saldo_inicial' => (float) $caja->saldo_inicial,
                'abierto_at' => $caja->abierto_at->toIso8601String(),
            ],
            'meta' => null,
            'errors' => null,
        ], JsonResponse::HTTP_CREATED);
    }

    /**
     * Movimientos de la sesión de caja abierta (ingresos/egresos manuales y enlazados).
     */
    public function movimientos(): JsonResponse
    {
        $caja = Caja::cajaAbiertaDelUsuario(Auth::id());
        if (! $caja) {
            return response()->json([
                'data' => [],
                'mensaje' => 'No tiene una caja abierta.',
                'meta' => null,
                'errors' => null,
            ], JsonResponse::HTTP_OK);
        }

        $desde = $caja->abierto_at;
        $hasta = now();

        $rows = CajaMovimiento::query()
            ->where('caja_id', $caja->id)
            ->whereBetween('created_at', [$desde, $hasta])
            ->orderByDesc('id')
            ->limit(200)
            ->get()
            ->map(function (CajaMovimiento $m) {
                return [
                    'id' => $m->id,
                    'tipo' => $m->tipo,
                    'monto' => (float) $m->monto,
                    'motivo' => $m->motivo,
                    'venta_id' => $m->venta_id,
                    'user_id' => $m->user_id,
                    'created_at' => $m->created_at?->toIso8601String(),
                ];
            });

        return response()->json([
            'data' => $rows,
            'meta' => null,
            'errors' => null,
        ], JsonResponse::HTTP_OK);
    }

    /**
     * Retiro o entrada de efectivo no ligado a una venta (caja abierta del usuario).
     */
    public function storeMovimiento(StoreCajaMovimientoRequest $request): JsonResponse
    {
        $caja = Caja::cajaAbiertaDelUsuario(Auth::id());
        if (! $caja) {
            return response()->json([
                'data' => null,
                'message' => 'No tiene una caja abierta para registrar movimientos.',
                'meta' => null,
                'errors' => null,
            ], JsonResponse::HTTP_UNPROCESSABLE_ENTITY);
        }

        $data = $request->validated();

        $mov = CajaMovimiento::create([
            'caja_id' => $caja->id,
            'user_id' => Auth::id(),
            'tipo' => $data['tipo'],
            'monto' => $data['monto'],
            'motivo' => $data['motivo'] ?? null,
            'venta_id' => null,
        ]);

        return response()->json([
            'data' => [
                'id' => $mov->id,
                'tipo' => $mov->tipo,
                'monto' => (float) $mov->monto,
                'motivo' => $mov->motivo,
                'created_at' => $mov->created_at?->toIso8601String(),
            ],
            'meta' => null,
            'errors' => null,
        ], JsonResponse::HTTP_CREATED);
    }

    /**
     * Corte de caja: resumen, arqueo opcional, PDF y cierre.
     *
     * Política: solo el usuario que abrió la caja puede cortarla, salvo rol Administrador
     * (puede indicar `caja_id` de una caja abierta de otro usuario). `cerrado_por_user_id` registra quién ejecutó el cierre.
     */
    public function cortar(CorteCajaRequest $request): JsonResponse|\Illuminate\Http\Response
    {
        $validated = $request->validated();

        if (! empty($validated['caja_id'])) {
            $caja = Caja::query()
                ->whereKey((int) $validated['caja_id'])
                ->where('estatus', Caja::ESTATUS_ABIERTA)
                ->first();
            if (! $caja) {
                return response()->json([
                    'data' => null,
                    'message' => 'No hay una caja abierta con ese identificador.',
                    'meta' => null,
                    'errors' => ['caja_id' => ['La caja no existe o ya está cerrada.']],
                ], JsonResponse::HTTP_UNPROCESSABLE_ENTITY);
            }
        } else {
            $caja = Caja::cajaAbiertaDelUsuario(Auth::id());
            if (! $caja) {
                return response()->json([
                    'data' => null,
                    'message' => 'No tiene una caja abierta para cortar.',
                    'meta' => null,
                    'errors' => null,
                ], JsonResponse::HTTP_UNPROCESSABLE_ENTITY);
            }
        }

        $this->authorize('cortar', $caja);

        $caja->load('usuario');

        $hasta = now();
        $resumen = $this->cajaCorte->resumenParaCierre($caja, $hasta);

        unset($validated['caja_id']);
        $efectivoContado = array_key_exists('efectivo_contado', $validated) && $validated['efectivo_contado'] !== null
            ? round((float) $validated['efectivo_contado'], 2)
            : null;
        $notasCierre = $validated['notas_cierre'] ?? null;

        $diferenciaArqueo = null;
        if ($efectivoContado !== null) {
            $diferenciaArqueo = round($efectivoContado - $resumen['esperado_efectivo'], 2);
        }

        $resumen['efectivo_contado'] = $efectivoContado;
        $resumen['diferencia_arqueo'] = $diferenciaArqueo;
        $resumen['notas_cierre'] = $notasCierre;

        $porMetodoPago = $resumen['por_metodo_pago'];
        $saldoFinal = $resumen['saldo_final'];
        $esperadoEfectivo = $resumen['esperado_efectivo'];

        DB::transaction(function () use (
            $caja,
            $saldoFinal,
            $hasta,
            $esperadoEfectivo,
            $efectivoContado,
            $diferenciaArqueo,
            $notasCierre
        ): void {
            $caja->update([
                'estatus' => Caja::ESTATUS_CERRADA,
                'saldo_final' => $saldoFinal,
                'cerrado_at' => $hasta,
                'cerrado_por_user_id' => Auth::id(),
                'esperado_efectivo' => $esperadoEfectivo,
                'efectivo_contado' => $efectivoContado,
                'diferencia_arqueo' => $diferenciaArqueo,
                'notas_cierre' => $notasCierre,
            ]);
        });

        if ($request->boolean('solo_json')) {
            return response()->json([
                'data' => $resumen,
                'message' => 'Caja cerrada correctamente.',
                'meta' => null,
                'errors' => null,
            ], JsonResponse::HTTP_OK);
        }

        try {
            $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('pdf.corte_caja', [
                'resumen' => $resumen,
                'porMetodoPago' => $porMetodoPago,
            ])->setPaper('letter');

            $nombreArchivo = 'corte-caja-'.$caja->id.'-'.now()->format('Y-m-d-His').'.pdf';

            return $pdf->download($nombreArchivo);
        } catch (\Throwable $e) {
            return response()->json([
                'data' => $resumen,
                'message' => 'Caja cerrada correctamente. PDF no disponible (instale barryvdh/laravel-dompdf): '.$e->getMessage(),
                'meta' => null,
                'errors' => null,
            ], JsonResponse::HTTP_OK);
        }
    }
}
