<?php

namespace App\Services;

use App\Models\{CajaSesion, CajaMovimiento};
use Illuminate\Support\Facades\DB;

class CajaService
{
    /**
     * Abre una nueva sesión de caja.
     *
     * @param int $sucursalId
     * @param float $montoApertura
     * @param int $userId
     * @return CajaSesion
     * @throws \Exception
     */
    public function abrirSesion(int $sucursalId, float $montoApertura, int $userId): CajaSesion
    {
        // Verificar que no haya otra caja abierta
        $abierta = CajaSesion::where('sucursal_id', $sucursalId)->abiertas()->exists();

        if ($abierta) {
            throw new \Exception('Ya existe una sesión de caja abierta para esta sucursal.');
        }

        return CajaSesion::create([
            'sucursal_id'    => $sucursalId,
            'user_id'        => $userId,
            'monto_apertura' => $montoApertura,
            'estado'         => 'abierta',
            'total_ingresos' => 0,
            'total_egresos'  => 0,
        ]);
    }

    /**
     * Registra un movimiento en la sesión actual.
     *
     * @param CajaSesion $sesion
     * @param array $data
     * @return CajaMovimiento
     */
    public function registrarMovimiento(CajaSesion $sesion, array $data): CajaMovimiento
    {
        return DB::transaction(function () use ($sesion, $data) {
            $data['sesion_id']   = $sesion->id;
            $data['sucursal_id'] = $sesion->sucursal_id;
            $data['user_id']     = auth()->id();
            
            // subtotal e igv base... (simplificado, asume monto total sin desglosar si no es factura)
            if ($data['comprobante_tipo'] === 'factura' && isset($data['monto'])) {
                $subtotal = round($data['monto'] / 1.18, 2);
                $data['subtotal'] = $subtotal;
                $data['igv']      = round($data['monto'] - $subtotal, 2);
            } else {
                $data['subtotal'] = data_get($data, 'monto', 0);
                $data['igv']      = 0;
            }

            $movimiento = CajaMovimiento::create($data);

            // Actualizar totales de la sesión
            if ($data['tipo'] === 'ingreso') {
                $sesion->increment('total_ingresos', $movimiento->monto);
            } else {
                $sesion->increment('total_egresos', $movimiento->monto);
            }

            return $movimiento;
        });
    }

    /**
     * Cierra la sesión de caja actual calculando la diferencia.
     *
     * @param CajaSesion $sesion
     * @param float $montoCierre (monto contado físico)
     * @param string|null $notas
     * @return CajaSesion
     */
    public function cerrarSesion(CajaSesion $sesion, float $montoCierre, ?string $notas = null): CajaSesion
    {
        return DB::transaction(function () use ($sesion, $montoCierre, $notas) {
            // Recalcular saldo esperado
            $saldoEsperado = $sesion->monto_apertura + $sesion->total_ingresos - $sesion->total_egresos;
            
            // Diferencia: > 0 sobrante, < 0 faltante, 0 cuadre perfecto
            $diferencia = $montoCierre - $saldoEsperado;

            $sesion->update([
                'estado'       => 'cerrada',
                'monto_cierre' => $montoCierre,
                'diferencia'   => $diferencia,
                'notas_cierre' => $notas,
                'cerrada_at'   => now(),
                'cerrada_por'  => auth()->id(),
            ]);

            return $sesion;
        });
    }

    /**
     * Anula un movimiento existente y recalcula.
     *
     * @param CajaMovimiento $movimiento
     * @param string $motivo
     * @return bool
     * @throws \Exception
     */
    public function anularMovimiento(CajaMovimiento $movimiento, string $motivo): bool
    {
        if ($movimiento->anulado) {
            throw new \Exception('El movimiento ya se encuentra anulado.');
        }

        if ($movimiento->sesion->estado === 'cerrada') {
            throw new \Exception('No se puede anular un movimiento de una sesión cerrada.');
        }

        return DB::transaction(function () use ($movimiento, $motivo) {
            $movimiento->update([
                'anulado'          => true,
                'anulado_por'      => auth()->id(),
                'anulado_at'       => now(),
                'motivo_anulacion' => $motivo,
            ]);

            // Revertir totales de la sesión
            $sesion = $movimiento->sesion;
            if ($movimiento->tipo === 'ingreso') {
                $sesion->decrement('total_ingresos', $movimiento->monto);
            } else {
                $sesion->decrement('total_egresos', $movimiento->monto);
            }

            return true;
        });
    }
}
