<?php

namespace App\Http\Controllers\Invoicing;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

use App\Http\Requests\Invoicing\InvoiceIssueRequest;
use App\Http\Requests\Invoicing\InvoiceDraftRequest;
use App\Http\Requests\Invoicing\InvoiceCancellationRequest;
use App\Http\Requests\Invoicing\UpdateClientFiscalDataRequest;

use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\MessageBag;
use Illuminate\Http\Request;

//? Resource Generators
use Barryvdh\DomPDF\Facade\Pdf as PDF;
use SimpleSoftwareIO\QrCode\Facades\QrCode;

use Illuminate\Support\Facades\Mail;
use App\Mail\Invoices\EmailCFDI;

//? Models
use App\Models\User;
use App\Models\Environment\Business;
use App\Models\Invoicing\TaxBranch;
use App\Models\Invoicing\TaxFormat;
use App\Models\Invoicing\TaxInvoice;
use App\Models\Invoicing\TaxPayment;
use App\Models\Invoicing\TaxInvoiceItem;
use App\Models\Static\SAT\CatalogItem;

//? Support
use App\Enums\Invoicing\BillableService;
use App\Enums\Invoicing\InvoiceCancellationStatus;
use App\Enums\Invoicing\InvoiceStatus;

use App\Helpers\Database\PolymorphicResolveHelper;
use App\Helpers\Environment\DeviceManagementHelper;
use App\Helpers\Environment\FacturapiHelper;
use App\Helpers\Environment\SystemEnvHelper;
use App\Helpers\Environment\SystemLimitsHelper;
use App\Helpers\Environment\SystemFeaturesHelper;
use App\Helpers\Environment\SystemModulesHelper;
use App\Helpers\Formats\QuantityConverterHelper;
use App\Helpers\Support\PdfResourcesHelper;
use App\Helpers\Support\SequenceControlHelper;
use App\Helpers\Support\SegmentInvoiceTaxesHelper;

//? Services
use App\Services\Facturapi\FacturapiInvoiceService;
use App\Services\Facturapi\FacturapiInvoiceSynchronizer;

//? Class
class TaxInvoiceController extends Controller
{
  //* Constructor
    public function __construct(
        protected FacturapiInvoiceService $facturapi_invoices,
        protected FacturapiInvoiceSynchronizer $invoiceSynchronizer
    ) {
        $this->middleware(['auth', 'role:administrador,gerente,recepcionista']);
    }

  //* Shared Data
    protected array $invoice_form_requirements = [];

  //* Basic CRUD
    // Shared
    public function index() {
      //- Avoid Module on Other Countries
        if (!SystemEnvHelper::areCurrentCountries(['MEX']) || !FacturapiHelper::isEnabled())
            return view('errors.503-modular');

      //- View Data
        $this->loadInvoiceFormData();
        $tax_branches = TaxBranch::all();

        $recent_issued_invoices = TaxInvoice::whereNotNull('sat_uuid')
            ->whereNotNull('certification_date')
            ->orderBy('certification_date', 'desc')->limit(5)
            ->get(['id', 'sat_uuid as text']);

        $stamping_limit_status = SystemLimitsHelper::getStampingLimitStatus();

        // Get last request filter on Session
        $stored_filters = session()->get('invoice_filters', []);

        return view('invoicing.invoices.index', [
            "cancellation_reasons" => $this->invoice_form_requirements['cancellation_reasons'],
            "cfdi_uses" => $this->invoice_form_requirements['physical_cfdi_uses'],
            "tax_branches" => $tax_branches,
            "tax_currencies" => $this->invoice_form_requirements['tax_currencies'],
            "tax_formats" => $this->invoice_form_requirements['tax_formats'],
            "tax_payment_forms" => $this->invoice_form_requirements['tax_payment_forms'],
            "tax_payment_methods" => $this->invoice_form_requirements['tax_payment_methods'],
            "tax_systems" => $this->invoice_form_requirements['tax_systems'],
            "filter_invoice_types" => $this->invoice_form_requirements['filter_invoice_types'],

            "establishment" => $this->invoice_form_requirements['establishment'],
            "recent_issued_invoices" => $recent_issued_invoices,
            "stamping_limit_status" => $stamping_limit_status,
        ], compact('stored_filters'));
    }

    public function create($service_type = null, $service_id = null) {
      //- Avoid Module on Other Countries
        if (!SystemEnvHelper::areCurrentCountries(['MEX'])) return view('errors.503-modular');

      //- Ensure Establishment TaxData
        if (!SystemModulesHelper::isEstablishmentTaxDataComplete())
            return view('invoicing.invoices.errors.tax-data-not-configured');

      //- Support Data
        $this->loadInvoiceFormData();
        $tax_branches = TaxBranch::all();

      //- Return view
        return view('invoicing.invoices.create', [...$this->invoice_form_requirements, "tax_branches" => $tax_branches, "service_type" => $service_type, "service_id" => $service_id]);
    }

    public function destroy($id) {
      // Search Record
        $data = TaxInvoice::findOrFail($id);

      // Remove Logo
        if (!in_array($data->status,[InvoiceStatus::Draft, InvoiceStatus::Canceled])) {
            $errors = new MessageBag([
                'Taxes' => [
                    __('common_responses_and_messages')['rejected_action'],
                    __('entity_forms')['tax_invoice']['messages']['delete_not_allowed'],
                ]
            ]);
            throw ValidationException::withMessages($errors->toArray());
        }

      // Delete Record
        $data->delete();
    }

    public function filterTable(Request $request) {
        // Put request filter on Session
        session()->put('invoice_filters', $request->all());

        if ($request->ajax()) {
          //* Load Data
            // Invoices
            $invoices = $this->buildInvoiceQuery($request)->with(['receptor', 'taxFormat', 'taxPaymentDocs'])
                ->take(500)->get()->map(fn ($invoice) => (object)[
                    'doc_type' => 'invoice',
                    'id'       => $invoice->id,
                    'model'    => $invoice,
                    'date'     => $invoice->date,
                ])->toBase();

            // Payments
            $payments = $this->buildPaymentQuery($request)->with(['receptor', 'taxFormat', 'taxPaymentDoc'])
                ->take(500)->get()->map(fn ($tax_payment) => (object)[
                    'doc_type' => 'payment',
                    'id'       => $tax_payment->id,
                    'model'    => $tax_payment,
                    'date'     => $tax_payment->certification_date,
                ])->toBase();

            // Totals
            $general_totals = $this->computeGeneralTotals($request);

          //* Merge Data
            $rows = $invoices->merge($payments)
                ->sortByDesc(fn ($row) => $row->date)->take(500)->values();

          //* Build Table
            $neutral_worlds = __('neutral_words');

            return datatables()->collection($rows)
              // Identification Data
                ->addColumn('date', fn($row) => Carbon::parse($row->date)->format("Y-m-d H:i:s"))
                ->addColumn('series', function ($row) use ($neutral_worlds) {
                    $m = $row->model;

                    // Drafts
                        if ($m->status === InvoiceStatus::Draft) return $neutral_worlds['draft'];
                    // Tax Payments
                        if ($row->doc_type === 'payment') return ($m->taxFormat?->serie ?? 'P');
                    // Invoices
                        return ($m?->taxFormat?->serie ?? $m->invoice_type);
                })
                ->addColumn('folio', function ($row) {
                    $m = $row->model;

                    // Drafts
                        if ($m->status === InvoiceStatus::Draft) return $m->id;
                    // Tax Payments
                        if ($row->doc_type === 'payment') return $m->serial_consecutive;
                    // Invoices
                        return $m->serial_consecutive;
                })
                ->addColumn('receptor', function($row){
                  // Control Data
                    $receptor = $row->model->receptor;
                    $reduce_invoices = SystemFeaturesHelper::getFeatureStatus("reduced_invoices");

                  // Return
                    return $reduce_invoices ? $receptor?->tax_id : $receptor?->legal_name;
                })

              // Control Data
                ->addColumn('payment_method', function($row) {
                    $m = $row->model;
                        if ($row->doc_type === 'payment') return "PUE";

                    return $m->payment_method;
                })
                ->addColumn('invoice_type', function($row) {
                    $m = $row->model;
                        if ($row->doc_type === 'payment') return "P";

                    return $m->invoice_type;
                })
                ->addColumn('status', function($row) use ($neutral_worlds) {
                    $m = $row->model;

                    // When cancellation workflow exists, it has priority for display
                    if (
                        $m->cancellation_status !== InvoiceCancellationStatus::None
                        || $m->status === InvoiceStatus::Canceled
                    ) {
                        $label = $m->cancellation_status === InvoiceCancellationStatus::None
                            ? $neutral_worlds['direct'] : $neutral_worlds[$m->cancellation_status->value];

                        return mb_strtoupper($neutral_worlds['cancellation']) . " (<i>" . mb_strtoupper($label) . "</i>)";
                    }

                    return mb_strtoupper($neutral_worlds[$m->status->value]);
                })
                ->addColumn('currency', function($row) {
                    $m = $row->model;
                    $exchange_rate = QuantityConverterHelper::removeUnnecessaryDecimal($m->exchange_rate);

                    return "{$m->currency}<br>({$exchange_rate})";
                })

              // Payment Control
                ->addColumn('subtotal', function($row) {
                    $m = $row->model;
                        if ($row->doc_type === 'payment' || $this->ensureCancelInvoice($m)) return "0.00";

                    $rate = (float) ($m->exchange_rate ?? 1);
                    $subtotal = (float) ($m->subtotal ?? 0) * $rate;

                    return number_format($subtotal, 2, '.', ',');;
                })
                ->addColumn('total_taxes', function($row) {
                  //* Control Data
                    $m = $row->model;

                  //* Tax Payments or Cancelled TaxDocs
                    if ($row->doc_type === 'payment' || $this->ensureCancelInvoice($m)) return "0.00";

                    $rate = (float) ($m->exchange_rate ?? 1);
                    $taxes = (float) (($m->taxes_total + $m->local_taxes_total) ?? 0) * $rate;

                  //* Invoices
                    return number_format($taxes, 2, '.', ',');
                })
                ->addColumn('total', function($row) {
                    $m = $row->model;
                        if ($row->doc_type === 'payment' || $this->ensureCancelInvoice($m)) return "0.00";

                    $rate = (float) ($m->exchange_rate ?? 1);
                    $total = (float) ($m->total ?? 0) * $rate;

                    return number_format($total, 2, '.', ',');
                })
                ->addColumn('amount_paid', function ($row) {
                  //* Control Data
                    $m = $row->model;
                    $total = (float) ($m->total ?? 0.00); // Invoice Total - Payment Mask

                  //* Define Structure
                    // Tax Payment
                    if ($row->doc_type === 'payment') {
                        $paid = 0.00; $dot_color = 'bg-dark';

                    // Cancelled TaxDocs
                    } elseif ($this->ensureCancelInvoice($m)) {
                        $paid = 0.00; $dot_color = 'bg-secondary';

                    // Tax Invoices
                    } elseif (($m->payment_method ?? null) === 'PUE') {
                        $paid = $total; $dot_color = 'bg-dark';
                    } else {  // PPD
                        $remaining_balance = (float) ($m?->latestTaxPaymentDoc?->remaining_balance ?? $total);
                        $paid = $total - $remaining_balance;

                        $dot_color = ($remaining_balance <= 0) ? 'harsi-background' : 'bg-danger';
                    }

                  // Render Payment Status
                    $dot = "<span class='mini-legend-dot {$dot_color}'></span>";
                    $rate = (float) ($m->exchange_rate ?? 1);
                    $paid = $paid * $rate;
                    $paid = number_format($paid, 2, '.', ',');

                    return "{$dot}{$paid}";
                })

              // Raw totals
                ->addColumn('subtotal_raw', function($row) {
                    $m = $row->model;
                    return ($row->doc_type === 'payment' || $this->ensureDraftInvoice($m) || $this->ensureCancelInvoice($m))
                        ? 0.0 : (float) ($m->subtotal ?? 0);
                })
                ->addColumn('total_taxes_raw', function($row) {
                    $m = $row->model;
                    return ($row->doc_type === 'payment' || $this->ensureDraftInvoice($m) || $this->ensureCancelInvoice($m))
                        ? 0.0 : (float) (($m->taxes_total + $m->local_taxes_total) ?? 0);
                })
                ->addColumn('total_raw', function($row) {
                    $m = $row->model;
                    return ($row->doc_type === 'payment' || $this->ensureDraftInvoice($m) || $this->ensureCancelInvoice($m))
                        ? 0.0 : (float) ($m->total ?? 0);
                })
                ->addColumn('amount_paid_raw', function ($row) {
                  //* Control Data
                    $m = $row->model;
                    $total = (float) ($m->total ?? 0.0);

                    // Tax Payment
                    if (($row->doc_type === 'payment' || $this->ensureDraftInvoice($m) || $this->ensureCancelInvoice($m))) return 0.0;

                    // PUE Invoice
                    if (($m->payment_method ?? null) === 'PUE') return $total;

                    // PPD Invoices
                    $remaining = (float) ($m?->latestTaxPaymentDoc?->remaining_balance ?? $total);
                    return $total - $remaining;
                })

              // Acciones
                ->addColumn('action', function($row) {
                    $m = $row->model;
                        if ($row->doc_type === 'payment') return $this->renderPaymentActions($m);

                    // Invoices
                    return $this->renderInvoiceActions($m);
                })

              // Render
                ->rawColumns(['action', 'amount_paid', 'currency', 'status'])
                ->with([
                    'is_mobile' => DeviceManagementHelper::isMobile($request),
                    'general_totals' => $general_totals,
                ])
                ->toJson();
        }

        return response()->json([
            'message' => 'Ajax Request is required'
        ], 405)->header('Content-Type', 'application/json');
    }

    public function clearSessionFilter(){
        session()->forget('invoice_filters');

        return response()->json([
            'success' => true
        ]);
    }

    public function searchBySatUuid(Request $request) {
        $value = trim((string) $request->input('q', '')); // Valor consultado
        $page = max(1, (int) $request->input('page', 1)); // Cantidad de paginas a mostrar
        $perPage = 20; // Limite de resultados por pagina

        // Se cachea temporalmente la consulta para evitar re-consultas iguales o similares
        $cacheKey = "tax_invoice_uuid:q={$value}:page={$page}:per={$perPage}";
        $data = Cache::remember($cacheKey, now()->addMinutes(5), function () use ($value, $page, $perPage) {
            $query = TaxInvoice::query(); // Query Builder

            if ($value !== '') {
                // Busca por SAT UUID (exacta primero o por prefijo)
                $query->where(function ($w) use ($value) {
                    $w->where('sat_uuid', 'like', $value.'%')
                        ->where('status', InvoiceStatus::Valid->value);
                });
            }

            $total = (clone $query)->count(); // Cantidad de registros retornados
            $rows = $query->orderByRaw("sat_uuid = ? desc", [$value]) // Prioriza match exacto
                            ->orderBy('sat_uuid')
                            ->skip(($page - 1) * $perPage)
                            ->take($perPage)
                            ->get(['sat_uuid', 'id']);

            return ['total' => $total, 'rows'  => $rows];
        });

        // Retornar en Formato util para Select2
        return response()->json([
            'results' => $data['rows']->map(fn ($r) => [
                'id'   => $r->id,
                'text' => $r->sat_uuid,
            ]),
            'pagination' => ['more' => ($page * $perPage) < $data['total']],
        ]);
    }

    // Certified Invoices
    public function certify(InvoiceIssueRequest $request, $id = null) {
      // Validate Stamp Limit
        SystemLimitsHelper::abortIfStampingLimitReached();

      // Validate Data
        $request->validated();

      // Validate Record exist (if we are updating)
        if ($id && !TaxInvoice::find($id)) {
            throw ValidationException::withMessages([
                'on_save.invoice' => ['El borrador a timbrar no existe.']
            ]);
        }

      // Start Transaction
        try {
            $result = DB::transaction(function () use ($request, $id) {
              // Update Client data
                try {
                    $this->updateSelectedReceiver($request->input('receiver', []));
                } catch (ValidationException $ve) {
                    $messages = collect($ve->errors())
                        ->mapWithKeys(fn($msgs, $key) => ["on_save.receiver.$key" => $msgs])
                        ->toArray();

                    throw ValidationException::withMessages($messages);
                }

              // Create Certified Invoice | Certify Draft
                $complementary_data = (object) [
                    "is_draft"             => false,
                    "certification_date"   => null,
                    "sat_uuid"             => null,
                    "cfdi_seal"            => null,
                    "sat_seal"             => null,
                    "sat_cert_number"      => null,
                    "tfd_original_string"  => null,
                    "facturapi_invoice_id" => null,
                    "verification_url"     => null,
                    "xml_path"             => null,
                    "request_payload"      => null,
                    "response_payload"     => null,
                    "last_error_returned"  => null,
                ];
                $form_data = $this->parseInvoiceData($request, $complementary_data);

                // Save in Facturapi
                $items_data = array_map(fn($item) =>
                    (object) $this->upsertInvoiceItem($item, 0, 0, true),
                    $request->input('items_summary', [])
                );
                $facturapi_response = $this->facturapi_invoices->createWithXml($form_data, $items_data);

                $invoice = TaxInvoice::updateOrCreate( ['id' => $id], $form_data );
                    if (!$invoice || empty($invoice->id)) {
                        throw ValidationException::withMessages([
                            'on_save.invoice' => [
                                $id
                                    ? 'No se pudo timbrar el borrador de la factura.'
                                    : 'No se pudo generar la factura'
                            ]
                        ]);
                    }

                $this->mergeFacturapiData($invoice, $facturapi_response);

                // Increase Stamp Counter
                SystemLimitsHelper::incrementStampLimitCounter('stamped');

              // Create/Update InvoiceItems
                $concepts = $request->input('items_summary', []);

                    // Remove deleted Concepts
                    try {
                        $this->syncInvoiceItems($concepts, $invoice->id);
                    } catch (ValidationException $ve) {
                        throw ValidationException::withMessages([
                            'on_save.items' => ['No se pudo actualizar los conceptos de la factura.']
                        ]);
                    }

                    // Create/Update InvoiceItems
                    foreach ($concepts as $idx => $concept) {
                        try {
                            $next_bd_line = (int) TaxInvoiceItem::where('invoice_id', $invoice->id)->max('line_number');
                            $line = empty($concept['line_number']) ? (++$next_bd_line) : $concept['line_number'];

                            $this->upsertInvoiceItem($concept, $invoice->id, $line);
                        } catch (ValidationException $ve) {
                            $messages = collect($ve->errors())
                                ->mapWithKeys(fn($msgs, $key) => ["on_save.items.$idx" => $msgs])->toArray();

                            throw ValidationException::withMessages($messages);
                        }
                    }

                return [
                    'invoice_id'  => $invoice->id,
                    'message'  => '¡Factura Timbrada con Éxito!'
                ];
            }, 3); // In case of a DeadLock (or loss of connection), please try again up to three times.

            return response()->json([
                'success' => $result['message'],
                'invoice_id' => $result['invoice_id'],
            ]);

        } catch (ValidationException $ve) {
            throw $ve;
        }
    }

    public function duplicateAsDraft($id) {
      // Get Invoice base Data
        $invoice = TaxInvoice::with('taxItems')->findOrFail($id);

        if( !in_array($invoice->status, [InvoiceStatus::Canceled, InvoiceStatus::Valid, InvoiceStatus::Draft]) )
            return redirect()->route('invoices.index');

      // Duplicate Data in new Draft
        $draft = DB::transaction(function () use ($invoice) {

          // Duplicate record
            $draft = $invoice->replicate([ // Exclude important fields
                "certification_date", "serial_consecutive", "facturapi_invoice_id", "sat_uuid", "cancel_motive",
                "cfdi_seal", "sat_seal", "sat_cert_number", "tfd_original_string", "credit_days", "due_date",
                "related_documents", "verification_url", "status", "cancellation_status", "substitution_invoice_id",

                "xml_path", "request_payload", "response_payload", "last_error_returned",
            ]);

            // Reset control fields
            $draft->date = Carbon::now()->format('Y-m-d\TH:i:s');
            $draft->status = InvoiceStatus::Draft;
            $draft->cancellation_status = InvoiceCancellationStatus::None;

            // Save as Draft
            $draft->save();

          // Duplicated concepts
            foreach ($invoice->taxItems as $index => $item) {
                // Duplicate record
                $concept = $item->replicate();

                // Reset control fields
                $concept->invoice_id = $draft->id;
                $concept->line_number = $index + 1;

                // Save concept
                $concept->save();
            }

          // Return draft data
            return $draft;
        });

        // Redirect to form
        return redirect()->route('invoices.drafts.edit', $draft->id);
    }

    public function show(Request $request, $id) {
      // Define Data
        $invoice = TaxInvoice::with('receptor', 'taxItems', 'taxFormat', 'taxBranch')->findOrFail($id);

        if(!in_array($invoice->status, [InvoiceStatus::Valid, InvoiceStatus::Canceled]))
            return redirect()->route('invoices.index');

      // Complementary Data
        $issuer = Business::first();

        $plane_date = Carbon::parse($invoice->date)->format('YmdHis');
        $invoice_format_serie = ($invoice->taxFormat?->serie ?? $invoice->invoice_type)."-{$invoice->serial_consecutive}";
        $doc_name = "{$invoice->receptor->tax_id}-{$invoice_format_serie}-{$plane_date}";

      // Create QR
        $qrBase64 = base64_encode(QrCode::format('png')->size(150)->generate($invoice->verification_url));
        $qr_stamp = "data:image/png;base64,$qrBase64";

      // Build PDF
        $fallback_url = public_path('images/harsi_logo.png');
        $img_path  = $invoice->taxFormat?->url_logo ?? optional(User::first())->profile_photo;

        $tax_logo = PdfResourcesHelper::domPDFImageRender($img_path, $fallback_url);
        $view = SystemFeaturesHelper::getFeatureStatus("sat_format_invoices")
            ? 'invoicing.invoices.pdf.standard.show'
            : 'invoicing.invoices.pdf.show';

        // Support Data
        $document_relations = CatalogItem::where('catalog_code', 'c_TipoDeRelaciones')->get();

        $pdf = PDF::loadView($view, [
            'invoice' => $invoice,
            'issuer' => $issuer,
            'invoice_format_serie' => $invoice_format_serie,
            'document_relations' => $document_relations,

            'title' => $doc_name,
            'logo_src' => $tax_logo,
            'qr_stamp' => $qr_stamp,
        ])->setPaper('letter')->setOptions([
            'defaultFont' => 'sans-serif',
            'isHtml5ParserEnabled' => true,
            'isRemoteEnabled'      => false
        ]);

      // Return PDF
        if ((bool) $request->query('download'))
            return $pdf->download("$doc_name.pdf", ['Attachment' => false]);
        else
            return $pdf->stream("$doc_name.pdf", ['Attachment' => false]);
    }

    public function downloadXml($id) {
        $invoice = TaxInvoice::with('taxItems')->findOrFail($id);

        if( !in_array($invoice->status, [InvoiceStatus::Canceled, InvoiceStatus::Valid]) )
            return redirect()->route('invoices.index');

        if (!$invoice->xml_path || !Storage::disk('public')->exists($invoice->xml_path))
            abort(404, 'XML not Found.');

        // Leer archivo real
        $xml_content = Storage::disk('public')->get($invoice->xml_path);

        // Nombre de archivo para descarga
        $file_name = basename($invoice->xml_path);

        // Forzar descarga
        return response()->streamDownload(function () use ($xml_content) {
            echo $xml_content;
        }, $file_name, [
            'Content-Type' => 'application/xml',
        ]);
    }

    public function sendCfdiByEmail($id, Request $request) {
        $validated = $request->validate([
            'emails.to'    => ['required', 'email:rfc'],
            'emails.cc'    => ['array'],
            'emails.cc.*'  => ['nullable', 'email:rfc'],
        ]);

        try {
          // Validate Invoice Status
            $invoice = TaxInvoice::findOrFail($id);

            if($invoice->status != InvoiceStatus::Valid)
                return redirect()->route('invoices.index');

          // Complementary Data
            $business = Business::first();

          // Emails
            $to = $validated['emails']['to'];
            $cc = array_filter($validated['emails']['cc'] ?? []);

            Mail::to($to)->cc($cc)->send(new EmailCFDI($invoice, $business));

            return response()->json([
                'success' => true,
                'message' => __('common_responses_and_messages')['email_sent_successfully'],
            ]);
        } catch (\Exception $e) {
            Log::error("Error al el CFDI de la factura #$id: " . $e->getMessage());

            return response()->json([
                'success' => false,
                'message' => __('common_responses_and_messages')['email_not_sent'],
            ], 500);
        }
    }

    public function cancel(InvoiceCancellationRequest $request, $id) {
      // Validate Stamp Limit
        SystemLimitsHelper::abortIfStampingLimitReached();

      // Validate Request
        $validated = $request->validated();
        $invoice = TaxInvoice::findOrFail($id);

      // Validate Invoice Data
        if ($invoice->status !== InvoiceStatus::Valid)
            return redirect()->route('invoices.index');

        if (!$invoice->facturapi_invoice_id)
            throw ValidationException::withMessages([
                'invoice' => [__('entity_forms')['tax_invoice']['messages']['cancel_cfdi_not_allowed']],
            ]);

      // Cancel Invoice
        try {
            // Save in Facturapi
            $response = $this->facturapi_invoices->cancel(
                $invoice->facturapi_invoice_id,
                array_filter([
                    'motive' => $validated['cancel_motive'],

                    // Required in Motivo 01
                    'substitution' => array_key_exists("substitution_invoice_id", $validated)
                        ? TaxInvoice::find($validated['substitution_invoice_id'])?->facturapi_invoice_id
                        : null,
                ], fn ($value) => $value !== null) // Retrieves only non-null fields
            );

            // Increment Stamped Counter
            SystemLimitsHelper::incrementStampLimitCounter('cancelled');

            Log::channel('facturapi_webhook')->info('Solicitud de cancelación enviada',[
                'invoice_id' => $invoice->id,
                'facturapi_status' => data_get($response, 'status'),
                'facturapi_cancellation_status' => data_get($response, 'cancellation_status'),
            ]);

            // Dave Local Data
            $result = DB::transaction(function () use ($invoice, $validated, $response) {
              //Recarga el modelo con los datos desde BD para evitar discrepancias
                $invoice->refresh();

              // Save user intent data
                $invoice->cancel_motive = $validated['cancel_motive'];
                $invoice->substitution_invoice_id = $validated['substitution_invoice_id'] ?? null;

              // Save response for audit
                $this->invoiceSynchronizer->sync($invoice, $response);
                $invoice->response_payload = $response;
                $invoice->save();

                return [
                    'status' => $invoice->status->value,
                    'cancellation_status' => $invoice->cancellation_status->value,
                ];
            });

            // Return response
            return response()->json([
                'success' => true,
                // 'data'    => $result,
                'message' => $result['cancellation_status'] === InvoiceCancellationStatus::Pending->value
                    ? __('entity_forms')['tax_invoice']['messages']['cancel_cfdi_pending']
                    : __('common_responses_and_messages')['updated_successfully'],
                // stamped status
                'stamping_limit_status' => SystemLimitsHelper::getStampingLimitStatus(),
            ]);
        } catch (ValidationException $ve) {
            throw $ve;
        } catch(\Exception $e){
            Log::channel('facturapi_webhook')->error("Cancelación fallida", [
                'invoice_id' => $id,
                'message' => $e->getMessage()
            ]);

            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 500);
        }
    }

    // Drafts
    public function upsertDraft(InvoiceDraftRequest $request, $id = null) {
      // Validate Data
        $request->validated();

      // Validate Record exist (if we are updating)
        if ($id && !TaxInvoice::find($id)) {
            throw ValidationException::withMessages([
                'on_save.invoice' => ['El borrador a actualizar no existe.']
            ]);
        }

      // Start Transaction
        try {
            $result = DB::transaction(function () use ($request, $id) {
              // Create/Update Draft
                $complementary_data = (object) [
                    "is_draft"             => true,
                    "certification_date"   => null,
                    "sat_uuid"             => null,
                    "cfdi_seal"            => null,
                    "sat_seal"             => null,
                    "sat_cert_number"      => null,
                    "tfd_original_string"  => null,
                    "facturapi_invoice_id" => null,
                    "verification_url"     => null,
                    "xml_path"             => null,
                    "request_payload"      => null,
                    "response_payload"     => null,
                    "last_error_returned"  => null,
                ];
                $form_data = $this->parseInvoiceData($request, $complementary_data);

                $invoice = TaxInvoice::updateOrCreate( ['id' => $id], $form_data );
                    if (!$invoice || empty($invoice->id)) {
                        throw ValidationException::withMessages([
                            'on_save.invoice' => ['No se pudo crear/actualizar el borrador de la factura.']
                        ]);
                    }

                // Create/Update InvoiceItems
                $concepts = $request->input('items_summary', []);

                    // Remove deleted Concepts
                    try {
                        $this->syncInvoiceItems($concepts, $invoice->id);
                    } catch (ValidationException $ve) {
                        throw ValidationException::withMessages([
                            'on_save.items' => ['No se pudo actualizar los conceptos del borrador.']
                        ]);
                    }

                    // Create/Update InvoiceItems
                    foreach ($concepts as $idx => $concept) {
                        try {
                            $next_bd_line = (int) TaxInvoiceItem::where('invoice_id', $invoice->id)->max('line_number');
                            $line = empty($concept['line_number']) ? (++$next_bd_line) : $concept['line_number'];

                            $this->upsertInvoiceItem($concept, $invoice->id, $line);
                        } catch (ValidationException $ve) {
                            $messages = collect($ve->errors())
                                ->mapWithKeys(fn($msgs, $key) => ["on_save.items.$idx" => $msgs])->toArray();

                            throw ValidationException::withMessages($messages);
                        }
                    }

              // Client data
                try {
                    $this->updateSelectedReceiver($request->input('receiver', []));
                } catch (ValidationException $ve) {
                    $messages = collect($ve->errors())
                        ->mapWithKeys(fn($msgs, $key) => ["on_save.receiver.$key" => $msgs])
                        ->toArray();

                    throw ValidationException::withMessages($messages);
                }

                return [
                    'message'       => $id ? 'Actualizado con éxito' : 'Registrado con éxito',
                    'invoice_id'    => $invoice->id ?? null
                ];
            }, 3); // In case of a DeadLock (or loss of connection), please try again up to three times.

            return response()->json([
                'success' => $result['message'],
                'invoice_id' => $result['invoice_id'],
            ]);

        } catch (ValidationException $ve) {
            throw $ve;
        }
    }

    public function editDraft($id) {
      //- Avoid Module on Other Countries
        if (!SystemEnvHelper::areCurrentCountries(['MEX'])) return view('errors.503-modular');

      //- Ensure Establishment TaxData
        if (!SystemModulesHelper::isEstablishmentTaxDataComplete())
            return view('invoicing.invoices.errors.tax-data-not-configured');

      //- Support Data
        $this->loadInvoiceFormData();
        $draft = TaxInvoice::with('receptor', 'taxItems')->findOrFail($id);
        $tax_branches = TaxBranch::all();

        // Avoid Concluded invoices
        if(in_array($draft->status, [InvoiceStatus::Canceled, InvoiceStatus::Valid]))
            return redirect()->route('invoices.index');

        // Load Secondary Services
        $service_type = null;
        $service_id   = null;
            if ($draft->system_service_type && $draft->system_service_id) {
                $enum = BillableService::fromModelClass($draft->system_service_type);

                $service_type = $enum->value;
                $service_id   = $draft->system_service_id;
            }

      //- Return view
        return view('invoicing.invoices.edit', [
            ...$this->invoice_form_requirements,
            "draft" => $draft,
            "tax_branches" => $tax_branches,
            'service_type' => $service_type,
            'service_id'   => $service_id,
        ]);
    }

    public function preview(InvoiceDraftRequest $request, $id = null) {
      // Validate Data
        /**
         * Cases:
         * - If the query is made from the index, the information stored in the database is used.
         * - If the query is made from the form, the information from the form is used.
        */
        if ($request->isMethod('post'))
            $request->validated();

      // Define Data
        $invoice = null;
        if($id) {
            $invoice = TaxInvoice::with('receptor', 'taxItems', 'taxFormat', 'taxBranch')->findOrFail($id);

        } else {
          // Parse Invoice Data
            $invoice_data = $this->parseInvoiceData($request);

          // Parse Items Data
            $items_data = [];
            foreach ( $request->input('items_summary', []) as $concept) {
                $data = $this->upsertInvoiceItem($concept, 0, 0, true);
                array_push($items_data, (object) $data);
            }

          // Parse Receiver Data
            // Validate Receiver existence
            $receiver = $request->input('receiver', []);
            if (empty($receiver) || empty($receiver['id'])) {
                throw ValidationException::withMessages([
                    'on_save.receiver.missing' => ['Debes seleccionar un receptor válido.']
                ]);
            }

            // Simplify Accesses
            $receiver = (object) $receiver;
            $receiver_address = isset($receiver->address) ? (object) $receiver->address : (object)[];

            // Requested Data
            $tax_country = $receiver_address->country ?? "MEX";
            $requested_tax_id = $receiver->tax_identifier ?? null;

            // Final Data
            $tax_id = $requested_tax_id;
            $tax_system = $receiver->tax_system ?? null;
            $zip = $receiver->zip ?? null;
            $cfdi_use = $invoice_data->cfdi_use ?? null;

            // Conditionals
            $is_generic_customer = $requested_tax_id === 'XAXX010101000';
            $is_mex_customer = $tax_country === 'MEX';

            // Default Data
            $generic_mex_zip = optional(Business::first())->zip ?? "00000";

            /**
             *  Forced Rules:
             * 1) Generic Mex RFC => MEX + 616 + zip negocio + S01 + tax_id XAXX...
             * 2) Foreign (country != MEX) => XEXX... + 616 + 00000 + S01
             * 3) Common Mex RFC => Requested Data
             */
            if ($is_generic_customer) {
                $tax_country = 'MEX';
                $tax_id = 'XAXX010101000';
                $tax_system = '616';
                $zip = $generic_mex_zip;
                $cfdi_use = 'S01';
            } elseif (!$is_mex_customer) {
                $tax_id = 'XEXX010101000';
                $tax_system = '616';
                $zip = '00000';
                $cfdi_use = 'S01';
            }

            $is_mex_customer = $tax_country === 'MEX'; // Revalidate assigned country

            $receiver_data = (object) [
                "id" => $receiver->id,

                "client_phone" => $receiver->phone,
                "is_moral" => filter_var($receiver->is_moral_person ?? false, FILTER_VALIDATE_BOOL),
                "legal_name" => $receiver->legal_name,
                "tax_system" => $tax_system,
                "tax_id" => $tax_id,
                "cfdi_use" => $cfdi_use,
                "email_billing" => $receiver->email,

                "zip" => $zip,
                "tax_address" => (object) [
                    "street" => $receiver_address->street ?? null,
                    "exterior" => $receiver_address->ext_number ?? null,
                    "interior" => $receiver_address->int_number ?? null,
                    "neighborhood" => $receiver_address->neighborhood ?? null,
                    "city" => $receiver_address->city ?? null,
                    "municipality" => $receiver_address->municipality ?? null,
                    "state" => $receiver_address->state ?? null,
                    "country" => $receiver_address->country ?? null,
                ]
            ];

          // Tax format
            $format_data = is_null($invoice_data['tax_format_id'])
                ? ["serie" => $invoice_data['invoice_type'], "base_color" => "#0D6889", "url_logo" => null]
                : TaxFormat::findOrFail($invoice_data['tax_format_id']);

          // Tax Branch
            $branch_data = is_null($invoice_data['tax_branch_id'])
                ? TaxBranch::findOrFail(1)
                : TaxBranch::findOrFail($invoice_data['tax_branch_id']);

          // Merge Data
            $array_data = [
                ...$invoice_data,
                "receptor" => $receiver_data,
                "taxItems" => $items_data,
                "taxFormat" => $format_data,
                "taxBranch" => $branch_data ,
            ];

          // Parse Data
            $invoice = json_decode(json_encode($array_data));
            $invoice->taxItems = collect($invoice->taxItems);
        }

      // Complementary Data
        $issuer = Business::first();

        $plane_date = Carbon::parse($invoice->date)->format('YmdHis');
        $doc_name = "preview-{$invoice->receptor->tax_id}-{$plane_date}";

      // Create QR
        $qrBase64 = base64_encode(QrCode::format('png')->size(150)->generate("$doc_name: Ash nazg durbatuluk, ash nazg gimbatul, ash nazg thrakatuluk agh burzum-ishi krimpatul."));
        $qr_stamp = "data:image/png;base64,$qrBase64";

      // Build PDF
        $fallback_url = public_path('images/harsi_logo.png');
        $img_path  = $invoice->taxFormat?->url_logo ?? optional(User::first())->profile_photo;

        $tax_logo = PdfResourcesHelper::domPDFImageRender($img_path, $fallback_url);
        $view = SystemFeaturesHelper::getFeatureStatus("sat_format_invoices")
            ? 'invoicing.invoices.pdf.standard.preview'
            : 'invoicing.invoices.pdf.preview';

      // Support Data
        $document_relations = CatalogItem::where('catalog_code', 'c_TipoDeRelaciones')->get();

        $pdf = PDF::loadView($view, [
            'invoice' => $invoice,
            'issuer' => $issuer,

            'title' => $doc_name,
            'logo_src' => $tax_logo,
            'qr_stamp' => $qr_stamp,
            'document_relations' => $document_relations,
        ])->setPaper('letter')->setOptions([
            'defaultFont' => 'sans-serif',
            'isHtml5ParserEnabled' => true,
            'isRemoteEnabled'      => false
        ]);

      // Return PDF
        // Origin: Form
        if ($request->ajax()) {
            return response($pdf->output(), 200)
                ->header('Content-Type', 'application/pdf')
                ->header('Content-Disposition', 'attachment; filename="'.$doc_name.'.pdf"');
                // ->header('Content-Disposition', 'inline; filename="'.$doc_name.'.pdf"');
        }

        // Origin: Index list
        return $pdf->stream("$doc_name.pdf", ['Attachment' => false]);
    }

  //* Support Functions
    // Table
    private function buildInvoiceQuery(Request $request) {
      //* Build Filters
        // Config Data
        $tz = config('app.timezone');

        // Control Data
        $filter_invoice_type         = (array) $request->input('filter_invoice_type', []);
        $filter_payment_status       = (array) $request->input('filter_payment_status', []);
        $filter_payment_method       = (array) $request->input('filter_payment_method', []);
        $filter_invoice_status       = (array) $request->input('filter_invoice_status', []);
        $filter_cancellation_status  = (array) $request->input('filter_cancellation_status', []);

        $from = $request->filled('from')
            ? Carbon::createFromFormat('Y-m-d', $request->from, $tz)->startOfDay()
            : now($tz)->startOfMonth()->startOfDay();

        $to = $request->filled('to')
            ? Carbon::createFromFormat('Y-m-d', $request->to, $tz)->endOfDay()
            : now($tz)->endOfMonth()->endOfDay();

        // Filter By Dates
        $q = TaxInvoice::with('latestTaxPaymentDoc', 'taxItems')
            ->whereBetween('date', [$from, $to]);

        // Filter by Receptor
        if ($request->filled('filter_receiver')){
            $receptor = $request->filter_receiver;

            $q->where(function ($q) use ($receptor){
                if(is_numeric($receptor)){
                    $q->where('client_id', $receptor);
                }else{
                    $q->whereHas('receptor', function ($qq) use ($receptor) {
                        $qq->where('legal_name', 'like', "%{$receptor}%")
                        ->orWhere('tax_id', 'like', "%{$receptor}%");
                    });
                }

            });
        }

        //Filter by Invoice Type
        if (!empty($filter_invoice_type))
            $q->whereIn('invoice_type', $filter_invoice_type);

        //Filter by Payment Method
        if (!empty($filter_payment_method))
            $q->whereIn('payment_method', $filter_payment_method);

        //Filter by Payment Status(only for PPD)
        if (
            !empty($filter_payment_status) &&
            (empty($filter_payment_method) || in_array('PPD', $filter_payment_method))
        ) {
            $q->where(function ($qq) use (
                $filter_payment_method, $filter_payment_status
            ) {

                // Keep PUE invoices if they are selected
                if (
                    empty($filter_payment_method) || in_array('PUE', $filter_payment_method)
                )
                    $qq->orWhere('payment_method', 'PUE');

                // Apply filter only on PPD invoices
                $qq->orWhere(function ($q2) use ($filter_payment_status) {
                    $q2->where('payment_method', 'PPD');

                    // Paid invoices
                    if (in_array('paid', $filter_payment_status)) {
                        $q2->whereHas('latestTaxPaymentDoc', function ($q3) {
                            $q3->where('remaining_balance', '<=', 0);
                        });
                    }

                    // Pending invoice payments
                    if (
                        in_array('pending', $filter_payment_status)
                        && !in_array('paid', $filter_payment_status)
                    ) {
                        $q2->where(function ($q3) {
                            $q3->doesntHave('latestTaxPaymentDoc')
                                ->orWhereHas('latestTaxPaymentDoc', function ($q4) {
                                    $q4->where('remaining_balance', '>', 0);
                                });
                        });
                    }
                });
            });
        }

        //Filter by Status
        if (!empty($filter_invoice_status)) {
            $statuses      = (array) $filter_invoice_status;
            $non_canceled   = array_diff($statuses, [InvoiceStatus::Canceled->value]);
            $include_canceled   = in_array(InvoiceStatus::Canceled->value, $statuses);

            $q->where(function ($qq) use (
                $non_canceled,
                $include_canceled,
                $filter_cancellation_status
            ) {
                if (!empty($non_canceled))
                    $qq->whereIn('status', $non_canceled);

                if ($include_canceled) {
                    $qq->orWhere(function ($q2) use ($filter_cancellation_status) {
                        $q2->where('status', InvoiceStatus::Canceled->value);

                        if (!empty($filter_cancellation_status))
                            $q2->whereIn('cancellation_status', (array) $filter_cancellation_status);
                    });
                }
            });

        } elseif (!empty($filter_cancellation_status)) {
            $q->where('status', InvoiceStatus::Canceled->value)
                ->whereIn('cancellation_status', (array) $filter_cancellation_status);
        }

        return $q;
    }

    private function buildPaymentQuery(Request $request) {
      //* Build Filters
        // Config Data
        $tz = config('app.timezone');

        // Control Data
        $filter_invoice_type         = (array) $request->input('filter_invoice_type', []);
        $filter_payment_method       = (array) $request->input('filter_payment_method', []);
        $filter_invoice_status       = (array) $request->input('filter_invoice_status', []);
        $filter_cancellation_status  = (array) $request->input('filter_cancellation_status', []);

        $from = $request->filled('from')
            ? Carbon::createFromFormat('Y-m-d', $request->from, $tz)->startOfDay()
            : now($tz)->startOfMonth()->startOfDay();

        $to = $request->filled('to')
            ? Carbon::createFromFormat('Y-m-d', $request->to, $tz)->endOfDay()
            : now($tz)->endOfMonth()->endOfDay();

        // Filter By Dates
        $q = TaxPayment::query()
            ->whereBetween('certification_date', [$from, $to]);

        // Filter by Receptor
        if ($request->filled('filter_receiver')){
            $receptor = $request->filter_receiver;

            $q->where(function ($q) use ($receptor){
                if(is_numeric($receptor)){
                    $q->where('client_id', $receptor);
                } else {
                    $q->whereHas('receptor', function ($qq) use ($receptor) {
                        $qq->where('legal_name', 'like', "%{$receptor}%")
                            ->orWhere('tax_id', 'like', "%{$receptor}%");
                    });
                }
            });
        }

        //Filter by Payment Method
        if ($request->has('filter_invoice_type')) {
            $types = array_filter($filter_invoice_type);

            if (!empty($types) && !in_array('P', $types, true))
                return $q->whereRaw('1 = 0');
        }

        //Filter by Payment Method
        if ($request->has('filter_payment_method')) {
            $methods = array_filter($filter_payment_method);

            if (!empty($methods) && !in_array('PUE', $methods, true))
                return $q->whereRaw('1 = 0');
        }

        //Filter by Status
        if (!empty($filter_invoice_status)) {
            $statuses          = $filter_invoice_status;
            $non_canceled      = array_diff($statuses, [InvoiceStatus::Canceled->value]);
            $include_canceled  = in_array(InvoiceStatus::Canceled->value, $statuses);

            $q->where(function ($qq) use ($non_canceled, $include_canceled, $filter_cancellation_status) {
                if (!empty($non_canceled))
                    $qq->whereIn('status', $non_canceled);

                if ($include_canceled) {
                    $qq->orWhere(function ($q2) use ($filter_cancellation_status) {
                        $q2->where('status', InvoiceStatus::Canceled->value);

                        if (!empty($filter_cancellation_status))
                            $q2->whereIn('cancellation_status', $filter_cancellation_status);
                    });
                }
            });

        } elseif (!empty($filter_cancellation_status)) {
            $q->where('status', InvoiceStatus::Canceled->value)
            ->whereIn('cancellation_status', $filter_cancellation_status);
        }

        return $q;
    }

    private function renderInvoiceActions(TaxInvoice $invoice) {
      // Support Data
        $routes = (object) [
            "edit"          => route('invoices.drafts.edit', ['id' => $invoice->id]),
            "preview"       => route('invoices.drafts.preview', ['id' => $invoice->id]),
            "show"          => route('invoices.show', ['id' => $invoice->id]),
            "download_pdf"  => route('invoices.show', ['id' => $invoice->id, 'download' => true]),
            "download_xml"  => route('invoices.download.xml', ['id' => $invoice->id]),
        ];
        $titles = (object) [
            "edit"                  => __('entity_forms')['tax_invoice']['action_titles']['edit'],
            "delete"                => __('entity_forms')['tax_invoice']['action_titles']['delete'],
            "preview"               => __('entity_forms')['tax_invoice']['action_titles']['preview'],
            "show"                  => __('entity_forms')['tax_invoice']['action_titles']['show'],
            "download_pdf"          => __('entity_forms')['tax_invoice']['action_titles']['download_pdf'],
            "download_xml"          => __('entity_forms')['tax_invoice']['action_titles']['download_xml'],
            "duplicate"             => __('entity_forms')['tax_invoice']['action_titles']['duplicate'],
            "send_cfdi"             => __('entity_forms')['tax_invoice']['action_titles']['send_cfdi'],
            "cancel_cfdi"           => __('entity_forms')['tax_invoice']['action_titles']['cancel_cfdi'],
            "manage_payments"  => __('entity_forms')['tax_invoice']['action_titles']['manage_payments'],
        ];

      // Display Controls
        // Global Controls
        $display_button_conditional = SystemModulesHelper::isEstablishmentTaxDataComplete();
        $role_button_conditional = in_array(Auth::user()->role, ['administrador', 'gerente']);

        // Specific Controls
          // Tax Environment Controls
            $is_deferred_payment = in_array($invoice->payment_method, ['PPD']);
            $has_tax_payments = $invoice->taxPaymentDocs->count() > 0;

          // Cancellation Workflow Controls
            $is_cancellation_pending = $invoice->cancellation_status === InvoiceCancellationStatus::Pending;
            $is_cancellation_rejected = $invoice->cancellation_status === InvoiceCancellationStatus::Rejected;
            $is_finally_canceled = $invoice->status === InvoiceStatus::Canceled;

            $could_be_cancelled = !$is_cancellation_pending && !$is_finally_canceled
                && $invoice->date->isSameYear(now()) &&(!$is_deferred_payment || ($is_deferred_payment && !$has_tax_payments));

          // Stamp Limit Status
            $stamping_limit_status = SystemLimitsHelper::getStampingLimitStatus();
            $stamp_limit_reached = !$stamping_limit_status['reached'];

      // Buttons Control
        // Structure
        $button_options = (object) [
            "edit" => [
                "condition" => $display_button_conditional,
                "structure" => "
                    <a
                        href='$routes->edit'
                        class='mx-0_5 btn btn-info fixed-width'
                        title='$titles->edit'
                    >
                        <i class='fas fa-pen-fancy'></i>
                    </a>
                "
            ],

            "delete" => [
                "condition" => $display_button_conditional && $role_button_conditional,
                "structure" => "
                    <button
                        type='button' onclick='deleteRecord($invoice->id)'
                        class='mx-0_5 btn btn-danger fixed-width'
                        title='$titles->delete'
                    >
                        <i class='fa fa-trash'></i>
                    </button>
                ",
                "dropdown_structure" => "
                    <button type='button' onclick='deleteRecord($invoice->id)'
                        class='dropdown-item text-danger' title='$titles->delete'
                    >
                        <i class='mr-2 fa fa-trash'></i> {$titles->delete}
                    </button>
                "
            ],

            "preview" => [
                "condition" => true,
                "structure" => "
                    <a
                        href='$routes->preview' target='_blank'
                        class='mx-0_5 btn fixed-width btn-harsi'
                        title='$titles->preview'
                    >
                        <i class='fas fa-eye'></i>
                    </a>
                "
            ],

            "show" => [
                "condition" => true,
                "structure" => "
                    <a
                        href='$routes->show' target='_blank'
                        class='mx-0_5 btn fixed-width btn-primary'
                        title='$titles->show'
                    >
                        <i class='fas fa-file-pdf'></i>
                    </a>
                ",
                "dropdown_structure" => "
                    <a href='$routes->show' target='_blank'
                        class='dropdown-item text-primary' title='$titles->show'
                    >
                        <i class='mr-2 fas fa-file-pdf'></i> {$titles->show}
                    </a>
                "
            ],

            "download_pdf" => [
                "condition" => true,
                "structure" => "
                    <a
                        href='$routes->download_pdf'
                        class='mx-0_5 btn fixed-width btn-harsi'
                        title='$titles->download_pdf'
                    >
                        <i class='fas fa-file-download'></i>
                    </a>
                ",
                "dropdown_structure" => "
                    <a href='$routes->download_pdf'
                        class='dropdown-item harsi-color' title='$titles->download_pdf'
                    >
                        <i class='mr-2 fas fa-file-download'></i> {$titles->download_pdf}
                    </a>
                "
            ],

            "download_xml" => [
                "condition" => true,
                "structure" => "
                    <a
                        href='$routes->download_xml'
                        class='mx-0_5 btn fixed-width btn-success'
                        title='$titles->download_xml'
                    >
                        <i class='fas fa-file-code'></i>
                    </a>
                "
            ],

            "duplicate" => [
                "condition" => $display_button_conditional,
                "structure" => "
                    <button
                        type='button' onclick='duplicate($invoice->id)'
                        class='mx-0_5 btn btn-secondary fixed-width'
                        title='$titles->duplicate'
                    >
                        <i class='fas fa-copy'></i>
                    </button>
                ",
                "dropdown_structure" => "
                    <button type='button' onclick='duplicate($invoice->id)'
                        class='dropdown-item text-secondary'
                    >
                        <i class='mr-2 fas fa-copy'></i> {$titles->duplicate}
                    </button>
                "
            ],

            "send_cfdi" => [
                "condition" => true,
                "structure" => "
                    <button
                        type='button' onclick='sendByEmail($invoice->id, `{$invoice->receptor->email_billing}`)'
                        class='p-0 mx-0_5 btn btn-warning fixed-width'
                        title='$titles->send_cfdi'
                    >
                        <i class='fas fa-paper-plane'></i>
                    </button>
                ",
                "dropdown_structure" => "
                    <button
                        type='button' onclick='sendByEmail($invoice->id, `{$invoice->receptor->email_billing}`)'
                        class='dropdown-item text-warning'
                    >
                        <i class='mr-2 fas fa-paper-plane'></i> {$titles->send_cfdi}
                    </button>
                "
            ],

            "cancel_cfdi" => [
                "condition" => $display_button_conditional && $could_be_cancelled && $stamp_limit_reached,
                "structure" => "
                    <button
                        type='button' onclick='cancelInvoice($invoice->id)'
                        class='p-0 mx-0_5 btn btn-danger fixed-width pale-item'
                        title='$titles->cancel_cfdi'
                    >
                        <i class='fas fa-ban'></i>
                    </button>
                ",
                "dropdown_structure" => "
                    <button
                        type='button' onclick='cancelInvoice($invoice->id)'
                        class='dropdown-item text-danger'
                    >
                        <i class='mr-2 fas fa-ban'></i> {$titles->cancel_cfdi}
                    </button>
                "
            ],

            "manage_payments" => [
                "condition" => $display_button_conditional && $is_deferred_payment,
                "structure" => "
                    <button
                        type='button' onclick='managePayments($invoice->id)'
                        class='p-0 mx-0_5 btn btn-harsi fixed-width pale-item'
                        title='$titles->manage_payments'
                    >
                        <i class='fas fa-list-alt'></i>
                    </button>
                ",
                "dropdown_structure" => "
                    <button
                        type='button' onclick='managePayments($invoice->id)'
                        class='dropdown-item harsi-color pale-item'
                    >
                        <i class='mr-2 fas fa-list-alt'></i> {$titles->manage_payments}
                    </button>
                "
            ],
        ];

        // Assignation per Status
          // Main Actions
            $buttons = match (true) {
                ($invoice->status === InvoiceStatus::Draft) => [$button_options->edit, $button_options->preview],
                ($invoice->status === InvoiceStatus::Valid) => [$button_options->download_xml, $button_options->download_pdf],
                ($is_cancellation_pending || $is_cancellation_rejected)  => [$button_options->download_xml, $button_options->download_pdf],
                ($is_finally_canceled)      => [$button_options->download_xml, $button_options->download_pdf],
            };

            $buttons = array_filter($buttons, fn ($btn) => $btn['condition'] === true);
            $render_primary = array_map(fn ($btn) => $btn['structure'], $buttons);

          // Secondary
            $dropdown_items = match (true) {
                ($invoice->status === InvoiceStatus::Draft) => [$button_options->duplicate, $button_options->delete],
                ($invoice->status === InvoiceStatus::Valid) => [$button_options->send_cfdi, $button_options->show, $button_options->duplicate, $button_options->manage_payments, $button_options->cancel_cfdi],
                ($is_cancellation_pending || $is_cancellation_rejected)  => [$button_options->send_cfdi, $button_options->show, $button_options->duplicate, $button_options->manage_payments],
                ($is_finally_canceled)      => [$button_options->show, $button_options->duplicate, $button_options->delete],
            };
            $dropdown_items = array_filter($dropdown_items, fn ($item) => $item['condition'] === true);
            $render_secondary = array_map(fn ($item) => $item['dropdown_structure'], $dropdown_items);

            $dropdown = '';
            if (count($render_secondary) > 0) {
                $dropdown = "
                    <div class='btn-group'>
                        <button type='button'
                            class='mx-0_5 btn btn-outline-secondary dropdown-toggle fixed-width no-caret'
                            data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'
                        >
                            <i class='fas fa-ellipsis-v'></i>
                        </button>
                        <div class='dropdown-menu dropdown-menu-right'>
                            ".implode('', $render_secondary)."
                        </div>
                    </div>
                ";
            }

      // Returns
        return '<div class="d-flex justify-content-center">'
            .implode('', $render_primary).$dropdown.'</div>';
    }

    private function renderPaymentActions(TaxPayment $payment) {
      // Support Data
        $routes = (object) [
            "show"          => route('tax-payments.show', ['id' => $payment->id]),
            "download_pdf"  => route('tax-payments.show', ['id' => $payment->id, 'download' => true]),
            "download_xml"  => route('tax-payments.download.xml', ['id' => $payment->id]),
        ];
        $titles = (object) [
            "show"                  => __('entity_forms')['tax_invoice']['action_titles']['show'],
            "download_pdf"          => __('entity_forms')['tax_invoice']['action_titles']['download_pdf'],
            "download_xml"          => __('entity_forms')['tax_invoice']['action_titles']['download_xml'],
            "send_cfdi"             => __('entity_forms')['tax_invoice']['action_titles']['send_cfdi'],
            "cancel_cfdi"           => __('entity_forms')['tax_invoice']['action_titles']['cancel_cfdi'],
        ];

      // Display Controls
        // Global Controls
        $display_button_conditional = SystemModulesHelper::isEstablishmentTaxDataComplete();

        // Specific Controls
          // Tax Environment Controls
            $is_last_payment = $payment->taxPaymentDoc->taxInvoice->latestTaxPaymentDoc?->tax_payment_id === $payment->id;

          // Cancellation Workflow Controls
            $is_cancellation_pending = $payment->cancellation_status === InvoiceCancellationStatus::Pending;
            $is_cancellation_rejected = $payment->cancellation_status === InvoiceCancellationStatus::Rejected;
            $is_finally_canceled = $payment->status === InvoiceStatus::Canceled;

            $could_be_cancelled = !$is_cancellation_pending && !$is_finally_canceled
                && $payment->payment_date->isSameYear(now()) && $is_last_payment;

          // Stamp Limit Status
            $stamping_limit_status = SystemLimitsHelper::getStampingLimitStatus();
            $stamp_limit_reached = !$stamping_limit_status['reached'];

      // Buttons Control
        // Structure
        $button_options = (object) [
            "show" => [
                "condition" => true,
                "structure" => "
                    <a
                        href='$routes->show' target='_blank'
                        class='mx-0_5 btn fixed-width btn-primary'
                        title='$titles->show'
                    >
                        <i class='fas fa-file-pdf'></i>
                    </a>
                ",
                "dropdown_structure" => "
                    <a href='$routes->show' target='_blank'
                        class='dropdown-item text-primary' title='$titles->show'
                    >
                        <i class='mr-2 fas fa-file-pdf'></i> {$titles->show}
                    </a>
                "
            ],

            "download_pdf" => [
                "condition" => true,
                "structure" => "
                    <a
                        href='$routes->download_pdf'
                        class='mx-0_5 btn fixed-width btn-harsi'
                        title='$titles->download_pdf'
                    >
                        <i class='fas fa-file-download'></i>
                    </a>
                ",
            ],

            "download_xml" => [
                "condition" => true,
                "structure" => "
                    <a
                        href='$routes->download_xml'
                        class='mx-0_5 btn fixed-width btn-success'
                        title='$titles->download_xml'
                    >
                        <i class='fas fa-file-code'></i>
                    </a>
                "
            ],

            "send_cfdi" => [
                "condition" => true,
                "structure" => "
                    <button
                        type='button' onclick='sendPaymentByEmail($payment->id, `{$payment->receptor->email_billing}`)'
                        class='p-0 mx-0_5 btn btn-warning fixed-width'
                        title='$titles->send_cfdi'
                    >
                        <i class='fas fa-paper-plane'></i>
                    </button>
                ",
                "dropdown_structure" => "
                    <button
                        type='button' onclick='sendPaymentByEmail($payment->id, `{$payment->receptor->email_billing}`)'
                        class='dropdown-item text-warning'
                    >
                        <i class='mr-2 fas fa-paper-plane'></i> {$titles->send_cfdi}
                    </button>
                "
            ],

            "cancel_cfdi" => [
                "condition" => $display_button_conditional && $could_be_cancelled && $stamp_limit_reached,
                "structure" => "
                    <button
                        type='button' onclick='cancelPaymentComplement($payment->id)'
                        class='p-0 mx-0_5 btn btn-danger fixed-width pale-item'
                        title='$titles->cancel_cfdi'
                    >
                        <i class='fas fa-ban'></i>
                    </button>
                ",
                "dropdown_structure" => "
                    <button
                        type='button' onclick='cancelPaymentComplement($payment->id)'
                        class='dropdown-item text-danger'
                    >
                        <i class='mr-2 fas fa-ban'></i> {$titles->cancel_cfdi}
                    </button>
                "
            ],
        ];

        // Assignation per Status
          // Main Actions
            $buttons = match ($payment->status) {
                InvoiceStatus::Valid => [$button_options->download_xml, $button_options->download_pdf],
                InvoiceStatus::Canceled => [$button_options->download_xml, $button_options->download_pdf, $button_options->show],
            };

            $buttons = match (true) {
                ($is_cancellation_pending || $is_cancellation_rejected)  => [$button_options->download_xml, $button_options->download_pdf],
                ($is_finally_canceled)      => [$button_options->download_xml, $button_options->download_pdf, $button_options->show],
                ($payment->status === InvoiceStatus::Valid) => [$button_options->download_xml, $button_options->download_pdf],
            };

            $buttons = array_filter($buttons, fn ($btn) => $btn['condition'] === true);
            $render_primary = array_map(fn ($btn) => $btn['structure'], $buttons);

          // Secondary
            $dropdown_items = match (true) {
                ($is_cancellation_pending || $is_cancellation_rejected)  => [$button_options->send_cfdi, $button_options->show],
                ($is_finally_canceled)      => [],
                ($payment->status === InvoiceStatus::Valid) => array_filter([
                    $button_options->send_cfdi, $button_options->show,
                    $could_be_cancelled ? $button_options->cancel_cfdi : null,
                ]),
            };
            $dropdown_items = array_filter($dropdown_items, fn ($item) => $item['condition'] === true);
            $render_secondary = array_map(fn ($item) => $item['dropdown_structure'], $dropdown_items);

            $dropdown = '';
            if (count($render_secondary) > 0) {
                $dropdown = "
                    <div class='btn-group'>
                        <button type='button'
                            class='mx-0_5 btn btn-outline-secondary dropdown-toggle fixed-width no-caret'
                            data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'
                        >
                            <i class='fas fa-ellipsis-v'></i>
                        </button>
                        <div class='dropdown-menu dropdown-menu-right'>
                            ".implode('', $render_secondary)."
                        </div>
                    </div>
                ";
            }

      // Returns
        return '<div class="d-flex justify-content-center">'
            .implode('', $render_primary).$dropdown.'</div>';
    }

    private function ensureCancelInvoice($tax_document): bool {
        return $tax_document->status === InvoiceStatus::Canceled
            && in_array(
                $tax_document->cancellation_status,
                [
                    InvoiceCancellationStatus::None, InvoiceCancellationStatus::Pending,
                    InvoiceCancellationStatus::Accepted, InvoiceCancellationStatus::Expired,
                ],
                true
            );
    }

    private function ensureDraftInvoice($tax_document): bool {
        return $tax_document->status === InvoiceStatus::Draft;
    }

    private function computeGeneralTotals(Request $request) {
      //* Control Data
        $subtotal = 0.0; // Total without Taxes
        $taxes    = 0.0; // Amount per Taxes
        $total    = 0.0; // Total with Taxes
        $paid     = 0.0; // Amount Paid

      //* Tax Payment
        // NOTE: Tax Payments don't be displayed as due charges

      //* Invoices
        // Queries
        $invoiceQuery = $this->buildInvoiceQuery($request)
            ->whereNotIn('status', [InvoiceStatus::Canceled, InvoiceStatus::Draft])
            ->whereNotIn('cancellation_status', [InvoiceCancellationStatus::Accepted, InvoiceCancellationStatus::Pending])
            ->with('latestTaxPaymentDoc');

        // Compute Invoice Totals
        $invoiceQuery->chunk(200, function ($invoices) use (&$subtotal, &$taxes, &$total, &$paid) {  // Work in groups of 200 invoices
            foreach ($invoices as $invoice) {
                $rate = (float) ($invoice->exchange_rate ?: 1);

                $subtotal += (float) ($invoice->subtotal ?? 0) * $rate;
                $taxes    += (float) (($invoice->taxes_total + $invoice->local_taxes_total) ?? 0) * $rate;

                $invoice_total = (float) ($invoice->total ?? 0) * $rate;
                $total += $invoice_total;

                if (($invoice->payment_method ?? null) === 'PUE') {
                    $paid += $invoice_total; // Pago en una sola exhibición
                } else {
                    $remaining = (float) ($invoice?->latestTaxPaymentDoc?->remaining_balance ?? $invoice_total);
                    $paid += ($invoice_total - $remaining);
                }
            }
        });

        return [
            'subtotal' => $subtotal,
            'taxes'    => $taxes,
            'total'    => $total,
            'paid'     => $paid,
        ];
    }

    // Form
    private function parseInvoiceData(InvoiceDraftRequest|InvoiceIssueRequest $request, ?object $complementary_data = null) {
      // Support Data
        $today = Carbon::now();
        $default_due_date = Carbon::now()->addMonths(1);

      // Control Data
        $is_drafting = $request instanceof InvoiceDraftRequest;
        $invoice_data = (object) $request;

            // Relations
            $system_service = PolymorphicResolveHelper::billableService($request->input('system_service', []));
            $tax_format = $invoice_data->input('tax_format_id', null);
            $tax_branch = $invoice_data->input('tax_branch_id', 1);

            // Encabezado de CFDI
            $invoice_type = $invoice_data->input('invoice_type', null);

            $raw_date = $invoice_data->input('date');
            try {
                $normalized_date = $raw_date
                    ? Carbon::parse($raw_date, config('app.timezone'))->format('Y-m-d\TH:i:s')
                    : $today->format('Y-m-d\TH:i:s');
            } catch (\Throwable $e) {
                $normalized_date = $today->format('Y-m-d\TH:i:s');
            }

            // Receiver
                // Control Data
                $tax_country = $receiver_address->country ?? "MEX";
                $requested_tax_id = data_get($request, 'receiver.tax_identifier');

                // Conditionals
                $is_generic_customer = $requested_tax_id === 'XAXX010101000';
                $is_mex_customer = $tax_country === 'MEX';

                /**
                 *  Forced Rules:
                 * 1) Generic Mex RFC => MEX + 616 + zip negocio + S01 + tax_id XAXX...
                 * 2) Foreign (country != MEX) => XEXX... + 616 + 00000 + S01
                 * 3) Common Mex RFC => Requested Data
                 */
                if ($is_generic_customer || !$is_mex_customer) {
                    $cfdi_use = 'S01';
                } else {
                    $cfdi_use = $invoice_data->input('cfdi_use');
                }

            // Payment section
            $exchange_rate = $invoice_data->input('currency', "MXN") === "MXN" ? 1.00 : $request->float('exchange_rate', 1.00);
            $is_deferred_invoice = $invoice_data->input('payment_method', "PUE") === "PPD";
            $payment_form = $is_deferred_invoice ? "99" : $invoice_data->input('payment_form', "01");

            // $credit_days = $is_deferred_invoice ? (int) $invoice_data->input('credit_days', $today->diffInDays($default_due_date)) : null;
            // $due_date = $is_deferred_invoice ? $invoice_data->input('due_date', $default_due_date) : null;
            $credit_days = $invoice_data->input('credit_days', null);
            $due_date = $invoice_data->input('due_date', null);

            // Related Documents
            $related_documents = array_map(fn($doc_data) => (object) $doc_data, $request->input('additional_docs', []));

            // Global Invoice Data
            $global_data = $request->input('global');
                if (!is_array($global_data)) $global_data = null;

            // Taxes and Prices
            $concepts  = $request->input('items_summary', []);
            $tax_summary = (object) SegmentInvoiceTaxesHelper::aggregateFederalTaxes($concepts);
            $local_tax_summary = (object) SegmentInvoiceTaxesHelper::aggregateLocalTaxes($concepts);

            $subtotal = $request->float('subtotal', 0.00);
            $total = $request->float('total', 0.00);

      // Parse Data
        return [
          // Identidad y Relaciones
            "tax_format_id" => $tax_format,
            "tax_branch_id" => $tax_branch,
            "client_id" => $invoice_data->input('receiver.id'),

            "system_service_type" => data_get($system_service, 'type'),
            "system_service_id" => data_get($system_service, 'id'),

          // Encabezado de CFDI
            "cfdi_use" => $cfdi_use,
            "invoice_type" => $invoice_type,
            "payment_form" => $payment_form,
            "payment_method" => $invoice_data->input('payment_method', "PUE"),
            "currency" => $invoice_data->input('currency', "MXN"),
            "export" => $invoice_data->input('export', null),
            "date" => $normalized_date,
            "certification_date" => data_get($complementary_data, 'certification_date'),

          // Identificadores de Timbrado
            "serial_consecutive" => $is_drafting ? null : SequenceControlHelper::nextInvoiceSerialConsecutive($invoice_type, $tax_branch, $tax_format),
            "facturapi_invoice_id" => data_get($complementary_data, 'facturapi_invoice_id'),
            "sat_uuid" => data_get($complementary_data, 'sat_uuid'),

            "cfdi_seal" => data_get($complementary_data, 'cfdi_seal'),
            "sat_seal" => data_get($complementary_data, 'sat_seal'),
            "sat_cert_number" => data_get($complementary_data, 'sat_cert_number'),
            "tfd_original_string" => data_get($complementary_data, 'tfd_original_string'),

          // Condiciones de Pago y Crédito
            "credit_days" => $credit_days,
            "due_date" => $due_date,
            "notes" => $invoice_data->input('notes') ?? __('neutral_words')['none'],
            "global" => $global_data,
            "related_documents" => $related_documents,
            "verification_url" => data_get($complementary_data, 'verification_url'),

          // Importes, Impuestos y Totales
            "local_taxes" => $local_tax_summary->local_taxes,
            "subtotal" => $subtotal,
            "taxes_total" => $tax_summary->taxes_total,
            "local_taxes_total" => $local_tax_summary->local_taxes_total,
            "total" => $total,
            "exchange_rate" => $exchange_rate,

          // Estatus y Ciclo de Vida (Locales)
            "status" => data_get($complementary_data, 'is_draft', true) ? InvoiceStatus::Draft->value : InvoiceStatus::Valid->value,
            "cancellation_status" => InvoiceCancellationStatus::None->value,

          // Recursos (Descargar y Rutas)
            "xml_path" => data_get($complementary_data, 'xml_path'),

          // Control de Respuestas
            "request_payload" => data_get($complementary_data, 'request_payload'),
            "response_payload" => data_get($complementary_data, 'response_payload'),
            "last_error_returned" => data_get($complementary_data, 'last_error_returned'),
        ];
    }

    private function mergeFacturapiData(TaxInvoice $invoice, array $facturapi_response) {
      // Parse Response
        $response = (object) $facturapi_response;

      // Save XML
        // Build Name
        $plane_date = Carbon::parse($invoice->date)->format('YmdHis');
        $invoice_format_serie = ($invoice->taxFormat?->serie ?? $invoice->invoice_type)."-{$invoice->serial_consecutive}";

        $filename = trim(
            implode('-', array_filter([$invoice->receptor->tax_id, $invoice_format_serie, $plane_date])),
            '-'
        ) . '.xml';

        $file_path = 'invoices/xml/' . $filename;

        // Save file
        $xml = $response->xml;
        $xml = trim($xml, "\"");
        $xml = stripcslashes($xml);
        $xml = html_entity_decode($xml);

        Storage::disk('public')->put($file_path, $xml);

      // Update local payload
        $invoice->facturapi_invoice_id   = $response->invoice['id'] ?? null;
        $invoice->certification_date     = $response->invoice['stamp']['date'] ?? null;
        $invoice->sat_uuid               = $response->invoice['uuid'] ?? null;
        $invoice->cfdi_seal              = $response->invoice['stamp']['signature'] ?? null;
        $invoice->sat_seal               = $response->invoice['stamp']['sat_signature'] ?? null;
        $invoice->sat_cert_number        = $response->invoice['stamp']['sat_cert_number'] ?? null;
        $invoice->tfd_original_string    = $response->invoice['stamp']['complement_string'] ?? null;
        $invoice->verification_url       = $response->invoice['verification_url'] ?? null;
        $invoice->xml_path               = $file_path;
        $invoice->request_payload        = $response->payload ?? null;
        $invoice->response_payload       = $response->invoice ?? null;

        $invoice->save();

        return;
    }

    private function syncInvoiceItems(array $concepts, int $invoice_id) {
        // Get removed concepts
        $existing_items = TaxInvoiceItem::where('invoice_id', $invoice_id)->pluck('line_number')->toArray();
        $used_lines = collect($concepts)->pluck('line_number')->toArray();
        $lines_to_delete = array_diff($existing_items, $used_lines);

        if (!empty($lines_to_delete)) { // Delete removed concepts from Draft
            TaxInvoiceItem::where('invoice_id', $invoice_id)
                ->whereIn('line_number', $lines_to_delete)->delete();
        }

        return;
    }

    private function upsertInvoiceItem(array $concept_data, int $invoice_id, int $line, ?bool $only_parse = false) {
      // Parsed Data
        $item_data = (object) $concept_data;
        $third_party_account = (object) $item_data->third_account_data;
        $federal_tax_summary = (object) SegmentInvoiceTaxesHelper::aggregateFederalTaxes([$concept_data]);
        $local_tax_summary = (object) SegmentInvoiceTaxesHelper::aggregateLocalTaxes([$concept_data]);

      // Control Data
        $should_have_taxes = in_array($item_data->object_tax, ["02", "04"]);

      // Structures
        $tax_charges_summary = (object) [
            "taxes" => [
                "iva" => $federal_tax_summary->iva_transferred ?? 0.00,
                "ieps" => $federal_tax_summary->ieps_transferred ?? 0.00,
            ],
            "withholding_taxes" => [
                "isr" => $federal_tax_summary->isr_withheld ?? 0.00,
                "iva" => $federal_tax_summary->iva_withheld ?? 0.00,
            ]
        ];
        $tax_profile = (object) [
            "taxes" => [ "iva" => null, "ieps" => null ],
            "withholding_taxes" => [ "isr" => null, "iva" => null ]
        ];

        if ($should_have_taxes && !empty($item_data->taxes)) {
            foreach ($item_data->taxes as $tax) {
                switch ( $tax['tax'] ) {
                    case '001': // ISR
                        $tax_profile->withholding_taxes['isr'] = [ "rate" => (float) $tax['rate_quote'] ];
                        break;

                    case '003': // IEPS
                        $tax_profile->taxes['ieps'] = [
                            "type" => "cuota",
                            "rate" => (float) $tax['rate_quote']
                        ];
                        break;

                    case '002': // IVA
                    default:
                        $is_transfer = $tax['tax_application'] === "tr";
                        $exempt_iva = $item_data->object_tax === "04";

                        if($is_transfer) {
                            $tax_profile->taxes['iva'] = [ "rate" => (float) ( $exempt_iva ? 0.00 : $tax['rate_quote']), "exempt" => $exempt_iva ];
                        } else {
                            $tax_profile->withholding_taxes['iva'] = [ "rate" => (float) $tax['rate_quote'] ];
                        }
                        break;
                }
            }
        }

      // Parse Data
        $data = [
            'invoice_id'               => $invoice_id,
            'prod_serv_id'             => $item_data->prod_serv_id ?? null,
            'line_number'              => $line,

            'name'                     => $item_data->name ?? null,
            'description'              => $item_data->description,
            'prod_serv_key'            => $item_data->prod_serv_key,
            'sat_description'          => $item_data->sat_description,
            'unit_key'                 => $item_data->unit_key,
            'unit_price'               => (float) $item_data->unit_price,
            'quantity'                 => (float) $item_data->quantity,
            'object_tax'               => $item_data->object_tax,
            'unit_price_includes_tax'  => $item_data->object_tax === "03",
            'discount_type'            => $item_data->discount_type,
            'discount_value'           => (float) $item_data->discount_value,
            'discount_total'           => (float) $item_data->discount_total,

            'tax_profile'              => $tax_profile,
            'local_tax_profile'        => $local_tax_summary->local_taxes,
            'subtotal'                 => (float) $item_data->subtotal,
            'tax_charges_summary'      => $tax_charges_summary,
            'total'                    => (float) $item_data->total,
            'third_party_account'      => $third_party_account,
            'property_tax_account'      => $item_data->property_tax_account,
        ];

        if ($only_parse) return $data;

      // Create/Update Item
        $query = [ 'invoice_id' => $invoice_id, 'line_number'=> $line ];
            if ( property_exists($item_data, 'id') ) $query['id'] = $item_data->id;

        TaxInvoiceItem::updateOrCreate($query, $data);
        return;
    }

    private function updateSelectedReceiver(array $receiver_data) {
      //* Ensure receiver
        if (empty($receiver_data) || empty($receiver_data['id'])) {
            throw ValidationException::withMessages([
                'on_save.receiver.missing' => ['Debes seleccionar un receptor válido.']
            ]);
        }

      //* Craft Format Data
        // Simplify Accesses
        $receiver = (object) $receiver_data;
        $receiver_address = isset($receiver->address) ? (object) $receiver->address : (object)[];

        // Requested Data
        $tax_country = $receiver_address->country ?? "MEX";
        $requested_tax_id = data_get($receiver, 'tax_identifier');

        // Final Data
        $tax_id = $requested_tax_id;
        $tax_system = data_get($receiver, 'tax_system');
        $zip = data_get($receiver, 'zip');
        $cfdi_use = data_get($receiver, 'cfdi_use');

        // Si cfdi viene vacio se le asigna el que ya tenia
        if(empty($cfdi_use) && !empty($receiver->id)){
            $cfdi_use = DB::table('clients')
                ->where('id', $receiver->id)->value('cfdi_use');
        }

        // Conditionals
        $is_generic_customer = $requested_tax_id === 'XAXX010101000';
        $is_mex_customer = $tax_country === 'MEX';

        // Default Data
        $generic_mex_zip = optional(Business::first())->zip ?? "00000";

        /**
         *  Forced Rules:
         * 1) Generic Mex RFC => MEX + 616 + zip negocio + S01 + tax_id XAXX...
         * 2) Foreign (country != MEX) => XEXX... + 616 + 00000 + S01
         * 3) Common Mex RFC => Requested Data
         */
        if ($is_generic_customer) {
            $tax_country = 'MEX';
            $tax_id = 'XAXX010101000';
            $tax_system = '616';
            $zip = $generic_mex_zip;
            $cfdi_use = 'S01';
        } elseif (!$is_mex_customer) {
            $tax_id = 'XEXX010101000';
            $tax_system = '616';
            $zip = '00000';
            $cfdi_use = 'S01';
        }

        $is_mex_customer = $tax_country === 'MEX'; // Revalidate assigned country

      //* Parse Data
        $payload  = [
          // Support
            "hidden_id" => $receiver->id,

          // Datos Fiscales
            "legal_name" => $receiver->legal_name,
            "is_moral_person" => filter_var($receiver->is_moral_person ?? false, FILTER_VALIDATE_BOOL),
            "tax_system" => $tax_system,
            "tax_identifier" => $tax_id,
            "cfdi_use" => $cfdi_use,
            "email_billing" => $receiver->email,
            "phone_billing" => $receiver->phone,

            // Dirección Fiscal
            "individual_zip" => $zip,
            "individual_street" => $receiver_address->street ?? null,
            "individual_ext_number" => $receiver_address->ext_number ?? null,
            "individual_int_number" => $receiver_address->int_number ?? null,
            "individual_neighborhood" => $receiver_address->neighborhood ?? null,
            "individual_city" => $receiver_address->city ?? null,
            "individual_municipality" => $receiver_address->municipality ?? null,
            "individual_country" => $tax_country,
        ];
            if ($is_mex_customer) $payload['individual_state'] = $receiver_address->state ?? null;

      //* Create Internal Request
        $base = Request::create('/', 'POST', $payload);
        $formRequest = UpdateClientFiscalDataRequest::createFromBase($base);
        $formRequest->setContainer(app())->setRedirector(app('redirect'));

        try {
            $formRequest->validateResolved();
        } catch (ValidationException $e) {
            throw $e;
        }

        return app(TaxClientController::class)->update($formRequest);
    }

    //Shared
    protected function loadInvoiceFormData(): void {
        if (!empty($this->invoice_form_requirements)) return; // Avoid reload data

        // Local Data
        $this->invoice_form_requirements['country_sets']  = config('constants.country_sets');
        $this->invoice_form_requirements['establishment'] = Business::first();
        $this->invoice_form_requirements['tax_formats']   = TaxFormat::all();

        // Catálogos del SAT
        $this->invoice_form_requirements['moral_tax_systems'] = Cache::remember('sat_moral_tax_systems', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_RegimenFiscal')
                ->whereIn('item_key', ['601', '603', '616', '620', '623', '624', '626'])->get();
        });

        $this->invoice_form_requirements['physical_tax_systems'] = Cache::remember('sat_physical_tax_systems', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_RegimenFiscal')
                ->whereNotIn('item_key', ['601', '603', '620', '623', '624'])->get();
        });

        $this->invoice_form_requirements['moral_cfdi_uses'] = Cache::remember('sat_moral_cfdi_uses', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_UsoCFDI')
                ->whereNotIn('item_key', ['D01','D02','D03','D04','D05','D06','D07','D08','D09','D10'])->get();
        });

        $this->invoice_form_requirements['physical_cfdi_uses'] = Cache::remember('sat_physical_cfdi_uses', now()->addHours(24), function () {
            // En práctica, ningún UsoCFDI es exclusivo de moral
            return CatalogItem::where('catalog_code', 'c_UsoCFDI')->get();
        });

        // SAT normales
        $this->invoice_form_requirements['tax_systems'] = Cache::remember('sat_tax_systems', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_RegimenFiscal')->get();
        });

        $this->invoice_form_requirements['exports'] = Cache::remember('sat_exports', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_Exportacion')->get();
        });

        $this->invoice_form_requirements['invoice_types'] = Cache::remember('sat_invoice_types', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_TipoDeComprobante')
                ->whereIn('item_key', ['I', 'E'])->get();
        });

        $this->invoice_form_requirements['filter_invoice_types'] = Cache::remember('sat_invoice_types_filter', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_TipoDeComprobante')
                ->whereIn('item_key', ['I', 'E', 'P'])->get();
        });

        $this->invoice_form_requirements['tax_currencies'] = Cache::remember('sat_currencies', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_Moneda')->get();
        });

        $this->invoice_form_requirements['tax_payment_forms'] = Cache::remember('sat_payment_forms', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_FormaPago')->get();
        });

        $this->invoice_form_requirements['tax_payment_methods'] = Cache::remember('sat_payment_methods', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_MetodoPago')->get();
        });

        $this->invoice_form_requirements['document_relations'] = Cache::remember('sat_document_relations', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_TipoDeRelaciones')->get();
        });

        $this->invoice_form_requirements['tax_types_catalog'] = Cache::remember('sat_tax_types', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_Impuesto')->get();
        });

        $this->invoice_form_requirements['tax_objects_catalog'] = Cache::remember('sat_tax_objects', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_ObjetoImp')
                ->whereIn('item_key', ['01','02'])->get();
        });

        $this->invoice_form_requirements['tasa_o_cuotas_catalog'] = Cache::remember('sat_tasa_o_cuota', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_TasaOCuota')
                ->orderBy('item_key', 'ASC')
                ->whereNotIn('item_key', ["1.600000","69.525500"])
                ->get();
        });

        $this->invoice_form_requirements['periodicities'] = Cache::remember('sat_tax_periodicity', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_Periodicidad')->get();
        });

        $this->invoice_form_requirements['fiscal_months'] = Cache::remember('sat_tax_fiscal_month', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_Meses')->get();
        });

        $this->invoice_form_requirements['cancellation_reasons'] = Cache::remember('sat_tax_cancellation_reason', now()->addHours(24), function () {
            return CatalogItem::where('catalog_code', 'c_MotivoCancelacion')->get();
        });
    }
}
