<?php

require_once __DIR__ . '/BillingHelpers.php';

class BillingTaxDocumentFactory
{
    const CHILE_COUNTRY_ID = 253;
    const CHILE_CURRENCY_ID = 39;
    const CHILE_IVA_TAX_TYPE_ID = '387';

    public function buildAdjustmentPayload(array $context)
    {
        $operationType = $context['operation_type'];
        if (!in_array($operationType, array('credit_note', 'debit_note'), true)) {
            throw new InvalidArgumentException('Tipo de operación tributaria no válido.');
        }

        $source = $context['source'];
        $remoteHeader = $context['remote_header'];
        $company = $context['company'];
        $receiver = $context['receiver'];
        $amounts = $context['amounts'];
        $reason = trim((string)$context['reason']);
        $referenceCode = (int)$context['reference_code'];
        $issueDate = $this->normalizeDate($context['issue_date']);
        $referenceDate = $this->normalizeDate($source['issue_date'] ?? '');
        $sourceTypeId = (int)($source['document_type_id'] ?? 0);
        $isExport = $sourceTypeId === 46;

        if ($reason === '') {
            throw new InvalidArgumentException('Debes informar el motivo tributario.');
        }
        if (!$issueDate) {
            throw new InvalidArgumentException('La fecha de emisión de la nota no es válida.');
        }
        if (!$referenceDate) {
            throw new RuntimeException('La factura de origen no tiene una fecha válida para la referencia tributaria.');
        }
        if (empty($source['document_id'])) {
            throw new RuntimeException('La factura de origen no tiene document_id FACTO.');
        }

        $issuerTaxId = billing_clean_rut($company['tax_id'] ?? ($remoteHeader['issuer_tax_id_code'] ?? ''));
        if (!$this->isValidChileRut($issuerTaxId)) {
            throw new RuntimeException('El RUT del emisor configurado no es válido.');
        }

        if ($isExport) {
            $receiverTaxId = trim((string)($receiver['tax_id'] ?? ($remoteHeader['receiver_tax_id_code'] ?? '')));
            $receiverTaxIdType = trim((string)($receiver['tax_id_type'] ?? ($remoteHeader['receiver_tax_id_type'] ?? '')));
            $receiverCountryId = (int)($receiver['country_id'] ?? ($remoteHeader['receiver_country_id'] ?? 0));
            if ($receiverTaxId === '') throw new RuntimeException('El receptor extranjero no tiene identificador tributario.');
            if ($receiverTaxIdType === '') throw new RuntimeException('El receptor extranjero no tiene tipo de identificador tributario.');
            if ($receiverCountryId <= 0 || $receiverCountryId === self::CHILE_COUNTRY_ID) throw new RuntimeException('La nota de exportación requiere un país receptor extranjero.');
        } else {
            $receiverTaxId = billing_clean_rut($receiver['tax_id'] ?? ($remoteHeader['receiver_tax_id_code'] ?? ''));
            if (!$this->isValidChileRut($receiverTaxId)) {
                throw new RuntimeException('El RUT del receptor no es válido: ' . $receiverTaxId . '.');
            }
            $receiverTaxIdType = 'CL-RUT';
            $receiverCountryId = self::CHILE_COUNTRY_ID;
        }

        $requiredReceiver = array(
            'legal_name' => 'razón social',
            'address' => 'dirección',
            'city' => 'ciudad',
            'activity' => 'actividad económica'
        );
        if (!$isExport) $requiredReceiver['district'] = 'comuna';
        $missing = array();
        foreach ($requiredReceiver as $key => $label) {
            if (trim((string)($receiver[$key] ?? '')) === '') $missing[] = $label;
        }
        if ($missing) throw new RuntimeException('Faltan datos del receptor: ' . implode(', ', $missing) . '.');

        $targetTypeId = $isExport
            ? ($operationType === 'credit_note' ? 48 : 47)
            : ($operationType === 'credit_note' ? 16 : 17);
        $sourceFolio = trim((string)($source['document_number'] ?? ''));
        $label = $operationType === 'credit_note' ? 'Nota de crédito' : 'Nota de débito';
        if ($isExport) $label .= ' de exportación';

        $header = array(
            'account_id' => (int)($context['account_id'] ?? 0),
            'document_type_id' => $targetTypeId,
            'received_issued_flag' => 1,
            'issue_date' => $issueDate,
            'issuer_tax_id_code' => $issuerTaxId,
            'issuer_tax_id_type' => 'CL-RUT',
            'issuer_legal_name' => trim((string)($company['legal_name'] ?? ($remoteHeader['issuer_legal_name'] ?? ''))),
            'issuer_address' => trim((string)($company['address'] ?? ($remoteHeader['issuer_address'] ?? ''))),
            'issuer_district' => trim((string)($company['district'] ?? ($remoteHeader['issuer_district'] ?? ''))),
            'issuer_city' => trim((string)($company['city'] ?? ($remoteHeader['issuer_city'] ?? ''))),
            'issuer_country_id' => self::CHILE_COUNTRY_ID,
            'issuer_phone' => trim((string)($company['phone'] ?? ($remoteHeader['issuer_phone'] ?? ''))),
            'issuer_activity' => trim((string)($company['activity'] ?? ($remoteHeader['issuer_activity'] ?? ''))),
            'receiver_tax_id_code' => $receiverTaxId,
            'receiver_tax_id_type' => $receiverTaxIdType,
            'receiver_legal_name' => trim((string)$receiver['legal_name']),
            'receiver_address' => trim((string)$receiver['address']),
            'receiver_district' => trim((string)($receiver['district'] ?? '')),
            'receiver_city' => trim((string)$receiver['city']),
            'receiver_country_id' => $receiverCountryId,
            'receiver_phone' => trim((string)($receiver['phone'] ?? '')),
            'receiver_activity' => trim((string)$receiver['activity']),
            'payment_conditions' => '0',
            'currency_id' => (int)($remoteHeader['currency_id'] ?? ($source['currency_id'] ?? self::CHILE_CURRENCY_ID)),
            'observations' => 'ICYLAB OS - ' . billing_safe_substr($reason, 220),
            'deployment_observations' => 1
        );
        if (!empty($receiver['email'])) $header['receiver_email'] = trim((string)$receiver['email']);
        if ($isExport) {
            $header['exchange_rate_value'] = (float)($remoteHeader['exchange_rate_value'] ?? ($source['exchange_rate_value'] ?? 1));
            $header['sale_modality'] = (int)($remoteHeader['sale_modality'] ?? ($source['sale_modality'] ?? 1));
            $header['sale_clause'] = (int)($remoteHeader['sale_clause'] ?? ($source['sale_clause'] ?? 6));
        }

        $detail = array(
            'quantity' => 1,
            'sku' => $operationType === 'credit_note' ? ($isExport ? 'NCEXP-AJUSTE' : 'NC-AJUSTE') : ($isExport ? 'NDEXP-AJUSTE' : 'ND-AJUSTE'),
            'line_description' => billing_safe_substr($label . ' DTE ' . $sourceTypeId . ' folio ' . $sourceFolio, 100),
            'long_description' => billing_safe_substr($reason, 500),
            'unit_measure' => 'UN',
            'unit_price' => $isExport ? round((float)$amounts['net_amount'], 4) : (int)$amounts['net_amount'],
            'modifier_amount' => 0,
            'modifier_percentage' => 0,
            'total_taxes' => $isExport ? 0 : (int)$amounts['tax_amount'],
            'total_amount_line' => $isExport ? round((float)$amounts['total_amount'], 4) : (int)$amounts['total_amount']
        );
        if (!$isExport && (int)$amounts['tax_amount'] > 0) {
            $detail['taxes'] = array(array(
                'tax_type_id' => defined('ICYLAB_FACTO_CHILE_IVA_TAX_TYPE_ID') ? (string)ICYLAB_FACTO_CHILE_IVA_TAX_TYPE_ID : self::CHILE_IVA_TAX_TYPE_ID,
                'tax_percentage' => 19,
                'tax_amount' => (int)$amounts['tax_amount']
            ));
        } else {
            $detail['taxes'] = array();
        }

        return array(
            'options' => array('rounding_type' => 'net'),
            'header' => $header,
            'details' => array($detail),
            'references' => array(array(
                'document_type_id' => $sourceTypeId,
                'reference_document_id' => (int)$source['document_id'],
                'reference_code' => $referenceCode,
                'description' => billing_safe_substr($reason, 255),
                'reference_date' => $referenceDate
            )),
            'totals' => array(
                'net_amount' => $isExport ? round((float)$amounts['net_amount'], 4) : (int)$amounts['net_amount'],
                'taxes_amount' => $isExport ? 0 : (int)$amounts['tax_amount'],
                'total_amount' => $isExport ? round((float)$amounts['total_amount'], 4) : (int)$amounts['total_amount']
            )
        );
    }

    public function splitChileGrossAmount($totalAmount, $taxable)
    {
        $total = (int)round((float)$totalAmount, 0);
        if ($total <= 0) throw new InvalidArgumentException('El monto del ajuste debe ser mayor a cero.');
        if (!$taxable) return array('net_amount' => $total, 'tax_amount' => 0, 'total_amount' => $total);
        $net = (int)round($total / 1.19, 0);
        $tax = $total - $net;
        return array('net_amount' => $net, 'tax_amount' => $tax, 'total_amount' => $total);
    }

    public function splitExportAmount($totalAmount)
    {
        $total = round((float)$totalAmount, 4);
        if ($total <= 0) throw new InvalidArgumentException('El monto del ajuste debe ser mayor a cero.');
        return array('net_amount' => $total, 'tax_amount' => 0.0, 'total_amount' => $total);
    }

    public function isValidChileRut($rut)
    {
        $rut = billing_clean_rut($rut);
        if (!preg_match('/^(\d{7,8})-([0-9K])$/', $rut, $matches)) return false;
        $number = $matches[1];
        $dv = $matches[2];
        $sum = 0;
        $multiplier = 2;
        for ($i = strlen($number) - 1; $i >= 0; $i--) {
            $sum += ((int)$number[$i]) * $multiplier;
            $multiplier = $multiplier === 7 ? 2 : $multiplier + 1;
        }
        $expected = 11 - ($sum % 11);
        if ($expected === 11) $expectedDv = '0';
        elseif ($expected === 10) $expectedDv = 'K';
        else $expectedDv = (string)$expected;
        return $dv === $expectedDv;
    }

    private function normalizeDate($value)
    {
        $value = trim((string)$value);
        if ($value === '') return null;
        $date = DateTime::createFromFormat('Y-m-d', substr($value, 0, 10));
        return $date && $date->format('Y-m-d') === substr($value, 0, 10) ? $date->format('Y-m-d') : null;
    }
}
