<?php

namespace App\Services\Facturapi;

use Facturapi\Exceptions\Facturapi_Exception;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Log;

use App\Enums\Invoicing\InvoiceStatus;

use App\Models\Hotel\Client;
use App\Models\Invoicing\TaxInvoice;
use App\Models\Invoicing\TaxPayment;
use App\Models\Invoicing\TaxFormat;
use App\Models\Invoicing\TaxBranch;

class FacturapiPaymentService extends FacturapiResourceService
{
  //* CRUD Functions

    /**
     * Crear complemento de pago (factura tipo P).
     */
    public function create(TaxPayment $payment_data, iterable $docs, bool $return_payload = false): array
    {
        try {
            $payload = $this->parsePayload(['payment_data' => $payment_data, 'documents' => $docs]);

            $result = $this->client->Invoices->create($payload);
            $parsed_result = $this->toArray($result);

            if ($return_payload)
                return [ 'response' => $parsed_result, 'payload'  => $payload ];

            return $parsed_result;
        } catch (Facturapi_Exception $e) {
            $this->handleFacturapiException($e);
        }
    }


    /**
     * Cancelar factura
     */
    public function cancel(string $facturapiId, array $options = []): array
    {
        try {
            $result = $this->client->Invoices->cancel($facturapiId, $options);

            return $this->toArray($result);
        } catch (Facturapi_Exception $e) {
            $this->handleFacturapiException($e);
        }
    }

    /**
     * Descargar XML del comprobante de pago.
     */
    public function download_xml(string $facturapiId, array $options = [])
    {
        try {
            $result = $this->client->Invoices->download_xml($facturapiId, $options);

            return $result;
        } catch (Facturapi_Exception $e) {
            $this->handleFacturapiException($e);
        }
    }

    /**
     * Timbrar complemento de pago y descargar XML en un solo flujo.
     *
     * Retorna algo así como:
     *
     * [
     *   'invoice' => [ ...datos de la factura timbrada... ],
     *   'xml'     => [ ...respuesta de download_xml... ],
     * ]
     */
    public function createWithXml(TaxPayment $payment_data, iterable $docs, array $downloadOptions = []): array
    {
      // Stamp Invoice
        $payment_complement = $this->create($payment_data, $docs, true);

        if (empty($payment_complement['response']['id'])) {
            Log::error('La respuesta de Facturapi al crear la factura no incluye ID.', ['invoice_response' => $payment_complement['response']]);

            throw ValidationException::withMessages([
                'facturapi' => [ "La factura se timbró pero la respuesta de Facturapi no incluye el ID. Notifica este error."],
            ]);
        }

      // Download XML
        $xml = $this->download_xml($payment_complement['response']['id'], $downloadOptions);

        return [
            'payload' => $payment_complement['payload'],
            'invoice' => $payment_complement['response'],
            'xml'     => $xml,
        ];
    }

  //* Inherited Functions
    protected function parsePayload(array $form_data): array
    {
      // Segment Data
        $payment_complement = (object) $form_data['payment_data'];
        $payment_docs = (object) $form_data['documents'];

      // Get Complementary data
        $customer = Client::findOrFail($payment_complement->client_id);
        $branch = TaxBranch::findOrFail($payment_complement->tax_branch_id);
        $format = TaxFormat::find($payment_complement->tax_format_id);

        // Series
        $invoice_serie = $format ? $format->serie : "P";

      // Build Related Documents
        $related_documents = [];
        foreach ($payment_docs as $txp_doc) {
            $invoice = $txp_doc->taxInvoice ?? TaxInvoice::with('taxItems')->findOrFail($txp_doc->invoice_id);

            if (empty($invoice->sat_uuid)) {
                throw ValidationException::withMessages([
                    'invoice' => ['La factura relacionada no tiene UUID SAT.'],
                ]);
            }

            $iva_rate = 0.0;
            $first_item = $invoice->taxItems->first();
            $taxes = [];

            $amount_paid = (float) $txp_doc->amount_paid;
            $previous_balance = (float) $txp_doc->previous_balance;

            if ($first_item && is_array($first_item->tax_profile)) {
              // IVA Traslado
                $iva_rate = (float) data_get($first_item->tax_profile, 'taxes.iva.rate', 0.0);
                $base = round( $amount_paid / (1 + $iva_rate), 2 );

                if($iva_rate > 0){
                    $taxes[] = [
                        'base' => $base,
                        'type'  => 'IVA',
                        'rate' => $iva_rate,
                        'factor' => 'Tasa',
                    ];
                }

              //IVA Retenido
                $retained_iva_rate = (float) data_get($first_item->tax_profile, 'withholding_taxes.iva.rate', 0.0);

                if ($retained_iva_rate > 0) {
                    $taxes[] = [
                        'base' => $base,
                        'type'  => 'IVA',
                        'rate' => $retained_iva_rate,
                        'factor' => 'Tasa',
                        'withholding' => true,
                    ];
                }
            }

            $related_documents[] = array_filter([
                'uuid'               => $invoice->sat_uuid,
                'installment'        => (int) $txp_doc->installment_number,
                'last_balance'       => $previous_balance,
                'amount'             => $amount_paid,
                'currency'           => $txp_doc->currency ?? $payment_complement->currency ?? 'MXN',
                'exchange'           => $txp_doc->exchange_rate ?? 1.00,
                'taxes'              => $taxes,
            ], fn ($v) => $v !== null);
        }


        $paymentData = array_filter([
            'payment_form'      => $payment_complement->payment_form,
            'date'              => $payment_complement->payment_date->format('Y-m-d\TH:i:s'),
            'currency'          => $payment_complement->currency ?? 'MXN',
            'exchange'          => $payment_complement->exchange_rate ?? 1.00,
            'related_documents' => $related_documents,
        ], fn ($v) => $v !== null);

        // Third Account
        $third_party = null;

        if (is_array($payment_complement->third_party_account)) {
            $third_party = array_filter([
                'legal_name' => $payment_complement->third_party_account['legal_name'] ?? null,
                'tax_id'     => $payment_complement->third_party_account['tax_id'] ?? null,
                'tax_system' => $payment_complement->third_party_account['tax_system'] ?? null,
                'zip'        => $payment_complement->third_party_account['zip'] ?? null,
            ], fn ($v) => $v !== null);

            if (empty($third_party)) {
                $third_party = null;
            }
        }

        return array_filter([
            'type' => 'P',

            'customer' => [
                'legal_name'          => $customer->legal_name,
                'tax_id'              => $customer->tax_id,
                'tax_system'          => $customer->tax_system,
                'address'             => array_filter([
                    'zip' => $customer->zip,
                    ...($customer->tax_address ?? []),
                ], fn ($value) => $value !== null),
                'email'               => $customer->email_billing,
                'phone'               => $customer->client_phone,
                'default_invoice_use' => $payment_complement->cfdi_use ?? $customer->cfdi_use,
            ],

            'address' => array_filter([
                'zip'          => $branch->zip,
                'street'       => $branch->tax_address['street'],
                'exterior'     => $branch->tax_address['exterior'],
                'interior'     => $branch->tax_address['interior'],
                'neighborhood' => $branch->tax_address['neighborhood'],
                'city'         => $branch->tax_address['city'],
                'municipality' => $branch->tax_address['municipality'],
                'state'        => $branch->tax_address['state'],
            ], fn ($value) => $value !== null), // Retrieves only non-null fields

            'complements' => [
                [
                    'type' => 'pago',
                    'data' => $paymentData,
                ],
            ],

            'third_party' => $third_party,
            "use" => $payment_complement->cfdi_use ?? $customer->cfdi_use,

            'status' => InvoiceStatus::Pending,
            'series' => $invoice_serie,
            "folio_number" => $payment_complement->serial_consecutive,
        ], fn ($value) => $value !== null);
    }

}
