<?php

namespace App\Http\Controllers;

use App\Models\ConfiguracionEmpresa;
use App\Models\Factura;
use App\Models\FacturaDetalle;
use App\Models\NumeracionDocumento;
use App\Models\ResolucionDian;
use App\Models\Venta;
use App\Models\VentaPago;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;

class VentaController extends Controller
{
    public function index(Request $request): View
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $tenantSucursalId = tenant_sucursal_id();

        $perPage = (int) $request->integer('per_page', 20);
        if (! in_array($perPage, [10, 20, 50, 100], true)) {
            $perPage = 20;
        }

        $page = max(1, (int) $request->integer('page', 1));
        $fechaDesde = trim((string) $request->query('fecha_desde', ''));
        $fechaHasta = trim((string) $request->query('fecha_hasta', ''));

        $mes = trim((string) $request->query('mes', ''));
        if ($mes !== '' && preg_match('/^\d{4}-\d{2}$/', $mes) === 1) {
            $fechaDesde = $mes.'-01';
            $fechaHasta = date('Y-m-t', strtotime($fechaDesde));
        }

        if ($fechaDesde === '' && $fechaHasta === '') {
            $fechaDesde = date('Y-m-01');
            $fechaHasta = date('Y-m-d');
        }

        $baseQuery = Venta::query()
            ->where('empresa_id', $empresaId)
            ->when($tenantSucursalId, static function ($query, int $sucursalId): void {
                $query->where(static function ($nested) use ($sucursalId): void {
                    $nested->whereNull('sucursal_id')
                        ->orWhere('sucursal_id', $sucursalId);
                });
            });

        if ($fechaDesde !== '' && strtotime($fechaDesde) !== false) {
            $baseQuery->whereDate('fecha', '>=', $fechaDesde);
        }

        if ($fechaHasta !== '' && strtotime($fechaHasta) !== false) {
            $baseQuery->whereDate('fecha', '<=', $fechaHasta);
        }

        $total = (clone $baseQuery)->count();
        $pages = max(1, (int) ceil($total / $perPage));
        $page = min($page, $pages);

        $saleIds = (clone $baseQuery)
            ->orderByDesc('id')
            ->forPage($page, $perPage)
            ->pluck('id')
            ->map(static fn ($id): int => (int) $id)
            ->all();

        $salesById = Venta::query()
            ->with([
                'cliente:id,nombre',
                'usuario:id,nombre',
                'sucursal:id,nombre',
            ])
            ->where('empresa_id', $empresaId)
            ->whereIn('id', $saleIds)
            ->get()
            ->keyBy('id');

        $mainDocuments = Factura::query()
            ->where('empresa_id', $empresaId)
            ->whereIn('venta_id', $saleIds)
            ->where('tipo_documento', 'factura')
            ->orderByDesc('id')
            ->get(['venta_id', 'numero']);

        $mainDocumentBySale = [];
        foreach ($mainDocuments as $document) {
            $ventaId = (int) $document->venta_id;
            if (isset($mainDocumentBySale[$ventaId])) {
                continue;
            }
            $mainDocumentBySale[$ventaId] = (string) ($document->numero ?? '');
        }

        $paymentMap = $this->buildPaymentMap($empresaId, $saleIds);

        $sales = [];
        foreach ($saleIds as $saleId) {
            /** @var Venta|null $sale */
            $sale = $salesById->get($saleId);
            if (! $sale) {
                continue;
            }

            $summary = $paymentMap[$saleId] ?? [
                'pagado' => 0.0,
                'metodos_pago' => '',
            ];

            $sales[] = $this->buildSalePresentation($sale, $summary, $mainDocumentBySale[$saleId] ?? null);
        }

        return view('modules.ventas.index', [
            'title' => 'Ventas',
            'sales' => $sales,
            'filters' => [
                'fecha_desde' => $fechaDesde,
                'fecha_hasta' => $fechaHasta,
            ],
            'page' => $page,
            'pages' => $pages,
            'total' => $total,
            'per_page' => $perPage,
        ]);
    }

    public function show(Venta $venta): View
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $tenantSucursalId = tenant_sucursal_id();
        $sale = $this->resolveSaleForTenant((int) $venta->id, $empresaId, $tenantSucursalId);
        if (! $sale) {
            abort(404, 'La venta no pertenece al tenant activo.');
        }

        $sale->load([
            'cliente:id,nombre,documento,email,telefono',
            'usuario:id,nombre',
            'sucursal:id,nombre',
        ]);

        $paymentMap = $this->buildPaymentMap($empresaId, [(int) $sale->id]);
        $mainDocumentNumber = Factura::query()
            ->where('empresa_id', $empresaId)
            ->where('venta_id', (int) $sale->id)
            ->where('tipo_documento', 'factura')
            ->orderByDesc('id')
            ->value('numero');

        $salePayload = $this->buildSalePresentation(
            sale: $sale,
            paymentSummary: $paymentMap[(int) $sale->id] ?? ['pagado' => 0.0, 'metodos_pago' => ''],
            mainDocumentNumber: is_string($mainDocumentNumber) ? $mainDocumentNumber : null
        );

        $documentsCollection = Factura::query()
            ->where('empresa_id', $empresaId)
            ->where('venta_id', (int) $sale->id)
            ->orderBy('id')
            ->get();

        $numberById = $documentsCollection
            ->pluck('numero', 'id')
            ->map(static fn ($value): string => (string) $value)
            ->all();

        $documents = $documentsCollection
            ->map(static function (Factura $document) use ($numberById): array {
                $referenceId = is_numeric($document->documento_referencia_id)
                    ? (int) $document->documento_referencia_id
                    : 0;

                return [
                    'id' => (int) $document->id,
                    'venta_id' => (int) $document->venta_id,
                    'tipo_documento' => (string) $document->tipo_documento,
                    'numero' => (string) $document->numero,
                    'subtotal' => (float) $document->subtotal,
                    'impuestos' => (float) $document->impuestos,
                    'total' => (float) $document->total,
                    'estado' => (string) $document->estado,
                    'documento_referencia_id' => $referenceId > 0 ? $referenceId : null,
                    'documento_referencia_numero' => $referenceId > 0 ? (string) ($numberById[$referenceId] ?? '') : '',
                    'motivo_documento' => (string) ($document->motivo_documento ?? ''),
                    'anulado_motivo' => (string) ($document->anulado_motivo ?? ''),
                    'fecha_emision' => optional($document->fecha_emision)->format('Y-m-d H:i:s') ?: (string) $document->fecha_emision,
                ];
            })
            ->all();

        return view('modules.ventas.show', [
            'title' => 'Venta #'.(int) $sale->id,
            'sale' => $salePayload,
            'documents' => $documents,
            'trail_totals' => $this->buildTrailTotals($documentsCollection),
        ]);
    }

    public function annul(Request $request, Venta $venta): RedirectResponse
    {
        /** @var PosController $posController */
        $posController = app(PosController::class);
        $posController->annul($request, $venta);

        return redirect()->route('ventas.index')
            ->with('status', 'Venta anulada y reversada correctamente (inventario + caja).');
    }

    public function creditNote(Request $request, Venta $venta): RedirectResponse
    {
        return $this->createNote($request, $venta, 'nota_credito');
    }

    public function debitNote(Request $request, Venta $venta): RedirectResponse
    {
        return $this->createNote($request, $venta, 'nota_debito');
    }

    public function cancelDocument(Request $request, Venta $venta, Factura $factura): RedirectResponse
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $tenantSucursalId = tenant_sucursal_id();
        $sale = $this->resolveSaleForTenant((int) $venta->id, $empresaId, $tenantSucursalId);
        if (! $sale) {
            abort(404, 'La venta no pertenece al tenant activo.');
        }

        $validated = $request->validate([
            'motivo_anulacion' => ['required', 'string', 'max:255'],
        ]);

        $usuarioId = (int) (auth_user()?->id ?? 0);
        abort_if($usuarioId <= 0, 403, 'Usuario inválido para anular documento.');

        DB::transaction(function () use ($empresaId, $sale, $factura, $validated, $usuarioId): void {
            $document = Factura::query()
                ->where('empresa_id', $empresaId)
                ->where('venta_id', (int) $sale->id)
                ->whereKey((int) $factura->id)
                ->lockForUpdate()
                ->first();

            if (! $document) {
                throw ValidationException::withMessages([
                    'documento' => 'El documento no pertenece a la venta seleccionada.',
                ]);
            }

            if ((string) $document->estado === 'anulada') {
                throw ValidationException::withMessages([
                    'documento' => 'El documento ya se encuentra anulado.',
                ]);
            }

            $hasDependent = Factura::query()
                ->where('empresa_id', $empresaId)
                ->where('documento_referencia_id', (int) $document->id)
                ->where('estado', '<>', 'anulada')
                ->exists();

            if ($hasDependent) {
                throw ValidationException::withMessages([
                    'documento' => 'No se puede anular porque tiene documentos dependientes activos.',
                ]);
            }

            $document->fill([
                'estado' => 'anulada',
                'anulado_por_usuario_id' => $usuarioId,
                'anulado_motivo' => trim((string) $validated['motivo_anulacion']),
                'anulado_at' => now(),
            ]);
            $document->save();
        });

        return redirect()->route('ventas.show', $sale)
            ->with('status', 'Documento anulado correctamente.');
    }

    public function printInvoice(Venta $venta): View|RedirectResponse
    {
        $document = $this->resolveMainDocumentForSale($venta);
        if (! $document) {
            return redirect()->route('ventas.index')->with('error', 'La venta no tiene factura principal emitida.');
        }

        return $this->renderDocumentView($document, false);
    }

    public function downloadInvoicePdf(Venta $venta): View|RedirectResponse
    {
        $document = $this->resolveMainDocumentForSale($venta);
        if (! $document) {
            return redirect()->route('ventas.index')->with('error', 'La venta no tiene factura principal emitida.');
        }

        return $this->renderDocumentView($document, true);
    }

    public function printTicket(Venta $venta): View
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $tenantSucursalId = tenant_sucursal_id();
        $sale = $this->resolveSaleForTenant((int) $venta->id, $empresaId, $tenantSucursalId, false);
        if (! $sale) {
            abort(404, 'La venta no pertenece al tenant activo.');
        }

        $sale->load([
            'empresa:id,nombre,nit,email,telefono,direccion,logo',
            'sucursal:id,nombre',
            'cliente:id,nombre,documento,email,telefono',
            'usuario:id,nombre',
            'detalles.producto:id,nombre',
            'pagos',
        ]);

        // Try to find an emitted Factura for this sale; if absent, build a virtual document from the sale.
        $factura = Factura::query()
            ->where('empresa_id', $empresaId)
            ->where('venta_id', (int) $sale->id)
            ->where('tipo_documento', 'factura')
            ->orderByDesc('id')
            ->first();

        $details = [];
        if ($factura) {
            $details = FacturaDetalle::query()
                ->where('factura_id', (int) $factura->id)
                ->orderBy('id')
                ->get()
                ->map(static fn (FacturaDetalle $d): array => [
                    'descripcion' => (string) ($d->descripcion ?? ''),
                    'cantidad' => (float) ($d->cantidad ?? 0),
                    'precio_unitario' => (float) ($d->precio_unitario ?? 0),
                    'impuesto' => (float) ($d->impuesto ?? 0),
                    'total_linea' => (float) ($d->total_linea ?? 0),
                ])
                ->all();
        } else {
            foreach ($sale->detalles as $d) {
                $details[] = [
                    'descripcion' => (string) ($d->producto?->nombre ?? ('Producto #'.$d->producto_id)),
                    'cantidad' => (float) ($d->cantidad ?? 0),
                    'precio_unitario' => (float) ($d->precio_unitario ?? 0),
                    'impuesto' => (float) ($d->impuesto ?? 0),
                    'total_linea' => (float) ($d->total_linea ?? 0),
                ];
            }
        }

        $paymentMethods = $sale->pagos
            ->pluck('metodo_pago')
            ->map(static fn ($m): string => strtolower(trim((string) $m)))
            ->filter()
            ->unique()
            ->values()
            ->implode(', ');
        $totalPagado = (float) $sale->pagos->sum(static fn ($p): float => (float) ($p->monto ?? 0));

        $document = [
            'venta_id' => (int) $sale->id,
            'tipo_documento' => (string) ($factura?->tipo_documento ?? 'factura'),
            'numero' => (string) ($factura?->numero ?? ('POS-'.$sale->id)),
            'subtotal' => (float) ($factura?->subtotal ?? $sale->subtotal ?? 0),
            'venta_descuento' => (float) ($sale->descuento ?? 0),
            'impuestos' => (float) ($factura?->impuestos ?? $sale->impuestos ?? 0),
            'total' => (float) ($factura?->total ?? $sale->total ?? 0),
            'estado' => (string) ($factura?->estado ?? $sale->estado ?? 'emitida'),
            'fecha_emision' => optional($factura?->fecha_emision)->format('Y-m-d H:i:s')
                ?: (optional($sale->fecha)->format('Y-m-d H:i:s') ?: (string) $sale->fecha),
            'cufe' => (string) ($factura?->cufe ?? ''),
            'qr_path' => (string) ($factura?->qr_path ?? ''),
            'total_pagado' => $totalPagado,
            'metodos_pago' => $paymentMethods,
            'cajero' => (string) ($sale->usuario?->nombre ?? ''),
            'cliente_nombre' => (string) ($sale->cliente?->nombre ?? 'Consumidor final'),
            'cliente_documento' => (string) ($sale->cliente?->documento ?? ''),
            'sucursal_nombre' => (string) ($sale->sucursal?->nombre ?? 'General'),
            'empresa_nombre' => (string) ($sale->empresa?->nombre ?? 'Veltrix POS'),
            'empresa_nit' => (string) ($sale->empresa?->nit ?? ''),
            'empresa_telefono' => (string) ($sale->empresa?->telefono ?? ''),
            'empresa_direccion' => (string) ($sale->empresa?->direccion ?? ''),
        ];

        // Fiscal settings + DIAN resolution from configuracion_empresa.
        $fiscalKeys = [
            'regimen_fiscal',
            'responsable_iva',
            'facturacion_electronica',
            'actividad_economica',
            'resolucion_dian',
            'fe_proveedor_tecnologico',
            'fe_proveedor_nit',
            'fe_software_nombre',
            'fe_fabricante_nombre',
            'fe_fabricante_nit',
        ];
        $fiscalRows = ConfiguracionEmpresa::query()
            ->where('empresa_id', $empresaId)
            ->whereIn('clave', $fiscalKeys)
            ->pluck('valor', 'clave')
            ->all();
        $fiscalSettings = [];
        foreach ($fiscalKeys as $k) {
            $fiscalSettings[$k] = (string) ($fiscalRows[$k] ?? '');
        }

        $dianResolution = ResolucionDian::query()
            ->where('empresa_id', $empresaId)
            ->where('activo', 1)
            ->when($sale->sucursal_id, static function ($q, $sucId): void {
                $q->where(static function ($n) use ($sucId): void {
                    $n->whereNull('sucursal_id')->orWhere('sucursal_id', $sucId);
                });
            })
            ->orderByDesc('id')
            ->first();

        $dianResolutionData = [];
        if ($dianResolution) {
            $dianResolutionData = [
                'numero_resolucion' => (string) ($dianResolution->numero_resolucion ?? ''),
                'prefijo' => (string) ($dianResolution->prefijo ?? ''),
                'rango_desde' => (string) ($dianResolution->rango_desde ?? ''),
                'rango_hasta' => (string) ($dianResolution->rango_hasta ?? ''),
                'fecha_desde' => optional($dianResolution->fecha_desde)->format('Y-m-d') ?: (string) $dianResolution->fecha_desde,
                'fecha_hasta' => optional($dianResolution->fecha_hasta)->format('Y-m-d') ?: (string) $dianResolution->fecha_hasta,
            ];
        }

        $factusConfigured = trim((string) ($fiscalRows['facturacion_electronica'] ?? '0')) === '1';

        $ticketSize = (string) (ConfiguracionEmpresa::query()
            ->where('empresa_id', $empresaId)
            ->where('clave', 'pos_ticket_size')
            ->value('valor') ?? '80mm');
        if (! in_array($ticketSize, ['58mm', '80mm'], true)) {
            $ticketSize = '80mm';
        }

        return view('modules.pos.ticket', [
            'sale' => $sale,
            'document' => $document,
            'details' => $details,
            'fiscalSettings' => $fiscalSettings,
            'dianResolution' => $dianResolutionData,
            'factusConfigured' => $factusConfigured,
            'ticketSize' => $ticketSize,
        ]);
    }

    public function printDocument(Venta $venta, Factura $factura): View|RedirectResponse
    {
        $document = $this->resolveDocumentForSale($venta, $factura);
        if (! $document) {
            return redirect()->route('ventas.show', $venta)->with('error', 'El documento no pertenece a la venta seleccionada.');
        }

        return $this->renderDocumentView($document, false);
    }

    public function downloadDocumentPdf(Venta $venta, Factura $factura): View|RedirectResponse
    {
        $document = $this->resolveDocumentForSale($venta, $factura);
        if (! $document) {
            return redirect()->route('ventas.show', $venta)->with('error', 'El documento no pertenece a la venta seleccionada.');
        }

        return $this->renderDocumentView($document, true);
    }

    private function createNote(Request $request, Venta $venta, string $type): RedirectResponse
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $tenantSucursalId = tenant_sucursal_id();
        $sale = $this->resolveSaleForTenant((int) $venta->id, $empresaId, $tenantSucursalId, true);
        if (! $sale) {
            abort(404, 'La venta no pertenece al tenant activo.');
        }

        $validated = $request->validate([
            'monto' => ['required', 'numeric', 'gt:0'],
            'motivo' => ['required', 'string', 'max:255'],
        ]);

        $amount = round((float) $validated['monto'], 2);
        $reason = trim((string) $validated['motivo']);

        DB::transaction(function () use ($empresaId, $sale, $type, $amount, $reason): void {
            if ((string) $sale->estado === 'anulada') {
                throw ValidationException::withMessages([
                    'venta' => 'No se pueden emitir notas para una venta anulada.',
                ]);
            }

            $mainInvoice = Factura::query()
                ->where('empresa_id', $empresaId)
                ->where('venta_id', (int) $sale->id)
                ->where('tipo_documento', 'factura')
                ->where('estado', '<>', 'anulada')
                ->orderByDesc('id')
                ->lockForUpdate()
                ->first();

            if (! $mainInvoice) {
                throw ValidationException::withMessages([
                    'venta' => 'La venta no tiene factura base para emitir notas.',
                ]);
            }

            $trail = $this->buildTrailTotals(
                Factura::query()
                    ->where('empresa_id', $empresaId)
                    ->where('venta_id', (int) $sale->id)
                    ->lockForUpdate()
                    ->get()
            );

            $netoDocumental = (float) ($trail['total_neto_documental'] ?? 0);
            if ($type === 'nota_credito' && $amount > ($netoDocumental + 0.0001)) {
                throw ValidationException::withMessages([
                    'monto' => 'La nota crédito no puede exceder el saldo documental vigente.',
                ]);
            }

            $documentNumber = $this->reserveDocumentNumber(
                companyId: $empresaId,
                branchId: is_numeric($sale->sucursal_id) ? (int) $sale->sucursal_id : null,
                type: $type,
                saleId: (int) $sale->id
            );

            $note = Factura::query()->create([
                'empresa_id' => $empresaId,
                'sucursal_id' => is_numeric($sale->sucursal_id) ? (int) $sale->sucursal_id : null,
                'venta_id' => (int) $sale->id,
                'cliente_id' => is_numeric($sale->cliente_id) ? (int) $sale->cliente_id : null,
                'tipo_documento' => $type,
                'numero' => $documentNumber,
                'subtotal' => $amount,
                'impuestos' => 0,
                'total' => $amount,
                'estado' => 'emitida',
                'documento_referencia_id' => (int) $mainInvoice->id,
                'motivo_documento' => $reason,
                'fecha_emision' => now(),
            ]);

            if (Schema::hasTable('factura_detalles')) {
                FacturaDetalle::query()->create([
                    'factura_id' => (int) $note->id,
                    'producto_id' => null,
                    'descripcion' => strtoupper(str_replace('_', ' ', $type)).': '.$reason,
                    'cantidad' => 1,
                    'precio_unitario' => $amount,
                    'impuesto' => 0,
                    'total_linea' => $amount,
                ]);
            }
        });

        $message = $type === 'nota_credito'
            ? 'Nota crédito emitida correctamente.'
            : 'Nota débito emitida correctamente.';

        return redirect()->route('ventas.show', $sale)
            ->with('status', $message);
    }

    private function reserveDocumentNumber(int $companyId, ?int $branchId, string $type, int $saleId): string
    {
        if (! Schema::hasTable('numeraciones_documentos')) {
            return $this->fallbackDocumentNumber($type, $saleId);
        }

        $numberings = NumeracionDocumento::query()
            ->where('empresa_id', $companyId)
            ->where('tipo_documento', $type)
            ->where('activo', 1)
            ->where(static function ($query) use ($branchId): void {
                if ($branchId) {
                    $query->where('sucursal_id', $branchId)
                        ->orWhereNull('sucursal_id');
                    return;
                }

                $query->whereNull('sucursal_id');
            })
            ->lockForUpdate()
            ->get();

        if ($numberings->isEmpty()) {
            return $this->fallbackDocumentNumber($type, $saleId);
        }

        $numbering = $numberings
            ->sortBy(static fn (NumeracionDocumento $row): int => ($branchId && (int) $row->sucursal_id === $branchId) ? 0 : 1)
            ->first();

        if (! $numbering) {
            return $this->fallbackDocumentNumber($type, $saleId);
        }

        $current = (int) ($numbering->consecutivo_actual ?? 0);
        $final = is_numeric($numbering->consecutivo_final) ? (int) $numbering->consecutivo_final : null;
        if ($current <= 0) {
            throw ValidationException::withMessages([
                'numeracion' => 'La numeración activa de documentos tiene consecutivo inválido.',
            ]);
        }

        if ($final !== null && $current > $final) {
            throw ValidationException::withMessages([
                'numeracion' => 'La numeración activa ya alcanzó su consecutivo final.',
            ]);
        }

        $numbering->consecutivo_actual = $current + 1;
        $numbering->save();

        $prefix = trim((string) ($numbering->prefijo ?? ''));

        return substr($prefix.(string) $current, 0, 80);
    }

    private function fallbackDocumentNumber(string $type, int $saleId): string
    {
        $prefix = match ($type) {
            'nota_credito' => 'NC-',
            'nota_debito' => 'ND-',
            default => 'DOC-',
        };

        return $prefix.$saleId.'-'.date('His');
    }

    private function buildPaymentMap(int $empresaId, array $saleIds): array
    {
        if ($saleIds === []) {
            return [];
        }

        $rows = VentaPago::query()
            ->where('empresa_id', $empresaId)
            ->whereIn('venta_id', $saleIds)
            ->get(['venta_id', 'monto', 'metodo_pago']);

        $map = [];
        foreach ($rows as $row) {
            $saleId = (int) $row->venta_id;
            if (! isset($map[$saleId])) {
                $map[$saleId] = [
                    'pagado' => 0.0,
                    'metodos' => [],
                ];
            }

            $map[$saleId]['pagado'] += (float) ($row->monto ?? 0);
            $method = strtolower(trim((string) ($row->metodo_pago ?? '')));
            if ($method !== '') {
                $map[$saleId]['metodos'][$method] = true;
            }
        }

        foreach ($map as $saleId => $summary) {
            $methods = array_keys((array) ($summary['metodos'] ?? []));
            sort($methods, SORT_NATURAL);

            $map[$saleId] = [
                'pagado' => round((float) ($summary['pagado'] ?? 0), 2),
                'metodos_pago' => implode(', ', $methods),
            ];
        }

        return $map;
    }

    private function buildSalePresentation(Venta $sale, array $paymentSummary, ?string $mainDocumentNumber): array
    {
        $total = (float) $sale->total;
        $pagado = (float) ($paymentSummary['pagado'] ?? 0);
        $estadoVenta = strtolower(trim((string) $sale->estado));

        $saldo = $estadoVenta === 'anulada'
            ? 0.0
            : max(0, round($total - $pagado, 2));

        $estadoPago = $estadoVenta === 'anulada'
            ? 'anulada'
            : ($pagado <= 0
                ? 'pendiente'
                : ($pagado < $total ? 'parcial' : 'pagada'));

        return [
            'id' => (int) $sale->id,
            'fecha' => optional($sale->fecha)->format('Y-m-d H:i:s') ?: (string) $sale->fecha,
            'total' => $total,
            'estado' => $estadoVenta !== '' ? $estadoVenta : 'pendiente',
            'cliente' => trim((string) ($sale->cliente?->nombre ?? 'Consumidor final')),
            'cajero' => trim((string) ($sale->usuario?->nombre ?? 'Sin cajero')),
            'sucursal' => trim((string) ($sale->sucursal?->nombre ?? 'Sin sucursal')),
            'documento_numero' => trim((string) ($mainDocumentNumber ?? '')) !== ''
                ? trim((string) $mainDocumentNumber)
                : 'POS-'.(string) $sale->id,
            'metodos_pago' => (string) ($paymentSummary['metodos_pago'] ?? ''),
            'pagado' => $pagado,
            'saldo' => $saldo,
            'estado_pago' => $estadoPago,
        ];
    }

    private function buildTrailTotals(Collection $documents): array
    {
        $active = $documents->filter(static fn (Factura $doc): bool => (string) $doc->estado !== 'anulada');

        $facturado = (float) $active
            ->where('tipo_documento', 'factura')
            ->sum(static fn (Factura $doc): float => (float) $doc->total);

        $credito = (float) $active
            ->where('tipo_documento', 'nota_credito')
            ->sum(static fn (Factura $doc): float => (float) $doc->total);

        $debito = (float) $active
            ->where('tipo_documento', 'nota_debito')
            ->sum(static fn (Factura $doc): float => (float) $doc->total);

        return [
            'total_facturado' => round($facturado, 2),
            'total_notas_credito' => round($credito, 2),
            'total_notas_debito' => round($debito, 2),
            'total_neto_documental' => round($facturado + $debito - $credito, 2),
        ];
    }

    private function resolveMainDocumentForSale(Venta $venta): ?Factura
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $tenantSucursalId = tenant_sucursal_id();
        $sale = $this->resolveSaleForTenant((int) $venta->id, $empresaId, $tenantSucursalId);
        if (! $sale) {
            abort(404, 'La venta no pertenece al tenant activo.');
        }

        return Factura::query()
            ->where('empresa_id', $empresaId)
            ->where('venta_id', (int) $sale->id)
            ->where('tipo_documento', 'factura')
            ->orderByDesc('id')
            ->first();
    }

    private function resolveDocumentForSale(Venta $venta, Factura $factura): ?Factura
    {
        $empresaId = tenant_empresa_id();
        abort_if(! $empresaId, 403, 'No hay empresa activa.');

        $tenantSucursalId = tenant_sucursal_id();
        $sale = $this->resolveSaleForTenant((int) $venta->id, $empresaId, $tenantSucursalId);
        if (! $sale) {
            abort(404, 'La venta no pertenece al tenant activo.');
        }

        return Factura::query()
            ->where('empresa_id', $empresaId)
            ->where('venta_id', (int) $sale->id)
            ->whereKey((int) $factura->id)
            ->first();
    }

    private function renderDocumentView(Factura $document, bool $pdfFallback): View
    {
        $document->load([
            'empresa:id,nombre,nit,email,telefono,direccion,logo',
            'sucursal:id,nombre',
            'cliente:id,nombre,documento,email,telefono',
            'venta:id,empresa_id,sucursal_id,usuario_id,cliente_id,subtotal,descuento,impuestos,total,fecha',
        ]);

        $documentReferenceNumber = '';
        if (is_numeric($document->documento_referencia_id) && (int) $document->documento_referencia_id > 0) {
            $documentReferenceNumber = (string) (Factura::query()->whereKey((int) $document->documento_referencia_id)->value('numero') ?? '');
        }

        $sale = $document->venta;
        $sale?->load('usuario:id,nombre');

        $paymentRows = VentaPago::query()
            ->where('empresa_id', (int) $document->empresa_id)
            ->where('venta_id', (int) ($document->venta_id ?? 0))
            ->get(['metodo_pago', 'monto']);

        $paymentMethods = $paymentRows
            ->pluck('metodo_pago')
            ->map(static fn ($method): string => strtolower(trim((string) $method)))
            ->filter()
            ->unique()
            ->values()
            ->implode(', ');

        $totalPagado = (float) $paymentRows->sum(static fn (VentaPago $row): float => (float) ($row->monto ?? 0));

        $details = FacturaDetalle::query()
            ->where('factura_id', (int) $document->id)
            ->orderBy('id')
            ->get()
            ->map(static fn (FacturaDetalle $detail): array => [
                'id' => (int) $detail->id,
                'producto_id' => is_numeric($detail->producto_id) ? (int) $detail->producto_id : null,
                'descripcion' => (string) ($detail->descripcion ?? ''),
                'cantidad' => (float) ($detail->cantidad ?? 0),
                'precio_unitario' => (float) ($detail->precio_unitario ?? 0),
                'impuesto' => (float) ($detail->impuesto ?? 0),
                'total_linea' => (float) ($detail->total_linea ?? 0),
            ])
            ->all();

        $payload = [
            'id' => (int) $document->id,
            'venta_id' => (int) ($document->venta_id ?? 0),
            'tipo_documento' => (string) ($document->tipo_documento ?? 'factura'),
            'numero' => (string) ($document->numero ?? ''),
            'subtotal' => (float) ($document->subtotal ?? 0),
            'impuestos' => (float) ($document->impuestos ?? 0),
            'total' => (float) ($document->total ?? 0),
            'estado' => (string) ($document->estado ?? 'emitida'),
            'documento_referencia_numero' => $documentReferenceNumber,
            'motivo_documento' => (string) ($document->motivo_documento ?? ''),
            'anulado_motivo' => (string) ($document->anulado_motivo ?? ''),
            'anulado_at' => optional($document->anulado_at)->format('Y-m-d H:i:s') ?: (string) $document->anulado_at,
            'fecha_emision' => optional($document->fecha_emision)->format('Y-m-d H:i:s') ?: (string) $document->fecha_emision,
            'cufe' => (string) ($document->cufe ?? ''),
            'qr_path' => (string) ($document->qr_path ?? ''),
            'cliente_nombre' => (string) ($document->cliente?->nombre ?? 'Consumidor final'),
            'cliente_documento' => (string) ($document->cliente?->documento ?? ''),
            'cliente_email' => (string) ($document->cliente?->email ?? ''),
            'cliente_telefono' => (string) ($document->cliente?->telefono ?? ''),
            'sucursal_nombre' => (string) ($document->sucursal?->nombre ?? 'General'),
            'empresa_nombre' => (string) ($document->empresa?->nombre ?? ''),
            'empresa_nit' => (string) ($document->empresa?->nit ?? ''),
            'empresa_email' => (string) ($document->empresa?->email ?? ''),
            'empresa_telefono' => (string) ($document->empresa?->telefono ?? ''),
            'empresa_direccion' => (string) ($document->empresa?->direccion ?? ''),
            'empresa_logo' => (string) ($document->empresa?->logo ?? ''),
            'venta_fecha' => optional($sale?->fecha)->format('Y-m-d H:i:s') ?: (string) ($sale?->fecha ?? ''),
            'venta_subtotal' => (float) ($sale?->subtotal ?? 0),
            'venta_descuento' => (float) ($sale?->descuento ?? 0),
            'venta_impuestos' => (float) ($sale?->impuestos ?? 0),
            'venta_total' => (float) ($sale?->total ?? 0),
            'metodos_pago' => $paymentMethods,
            'total_pagado' => $totalPagado,
            'cajero' => (string) ($sale?->usuario?->nombre ?? ''),
            'anulado_por_nombre' => '',
        ];

        return view('modules.facturacion.print', [
            'title' => 'Documento '.$payload['numero'],
            'document' => $payload,
            'invoice' => $payload,
            'details' => $details,
            'pdf_fallback' => $pdfFallback,
        ]);
    }

    private function resolveSaleForTenant(int $ventaId, int $empresaId, ?int $tenantSucursalId, bool $forUpdate = false): ?Venta
    {
        $query = Venta::query()
            ->where('empresa_id', $empresaId)
            ->whereKey($ventaId)
            ->when($tenantSucursalId, static function ($builder, int $sucursalId): void {
                $builder->where(static function ($nested) use ($sucursalId): void {
                    $nested->whereNull('sucursal_id')
                        ->orWhere('sucursal_id', $sucursalId);
                });
            });

        if ($forUpdate) {
            $query->lockForUpdate();
        }

        return $query->first();
    }
}
