<?php

require_once __DIR__ . '/SimplePdfWriter.php';

/**
 * Plantilla PDF común para cotizaciones y órdenes de compra ICYLAB.
 *
 * Replica el lenguaje visual de la cotización HTML: hoja carta, cabecera con
 * logo, recuadro verde de identificación, ficha de contraparte, tabla clara y
 * resumen financiero. No representa un DTE ni reemplaza el PDF de FACTO.
 */
class CommercialDocumentPdfRenderer
{
    private $pdf;
    private $document = array();
    private $company = array();
    private $y = 0.0;
    private $page = 0;
    private $margin = 36.0;
    private $itemsTableTop = null;
    private $green = array(0, 210, 45);
    private $dark = array(30, 41, 59);
    private $muted = array(71, 85, 105);
    private $border = array(226, 232, 240);
    private $surface = array(248, 250, 252);

    public function render(array $document): string
    {
        $this->document = $document;
        $this->company = isset($document['company']) && is_array($document['company']) ? $document['company'] : array();
        $this->pdf = new SimplePdfWriter('LETTER');
        $this->newPage(true);
        $this->renderItems();
        $this->renderSummary();
        $this->renderFooter();
        return $this->pdf->output();
    }

    public static function formatTaxIdentifier($value, $country = ''): string
    {
        $original = trim((string)$value);
        if ($original === '') return '—';
        $compact = strtoupper(preg_replace('/[^0-9K]/i', '', $original));
        if (!self::isChileanIdentifier($original, $country) || strlen($compact) < 2) return $original;

        $body = substr($compact, 0, -1);
        $checkDigit = substr($compact, -1);
        $groups = array();
        while ($body !== '') {
            $groups[] = substr($body, -3);
            $body = substr($body, 0, max(0, strlen($body) - 3));
        }
        return implode('.', array_reverse($groups)) . '-' . $checkDigit;
    }

    public static function taxIdentifierLabel($value, $country = ''): string
    {
        return self::isChileanIdentifier((string)$value, $country) ? 'R.U.T.' : 'ID Tributario';
    }

    private static function isChileanIdentifier(string $value, $country): bool
    {
        $normalizedCountry = strtoupper(trim((string)$country));
        $normalizedCountry = str_replace(array('.', '_'), array('', ' '), $normalizedCountry);
        if (in_array($normalizedCountry, array('CHILE', 'CL', 'CHL'), true)) return true;
        if ($normalizedCountry !== '') return false;
        $compact = strtoupper(preg_replace('/[^0-9K]/i', '', $value));
        return self::isValidChileanRut($compact);
    }

    private static function isValidChileanRut(string $compact): bool
    {
        if (!preg_match('/^[0-9]{7,8}[0-9K]$/', $compact)) return false;
        $body = substr($compact, 0, -1);
        $expected = substr($compact, -1);
        $sum = 0;
        $multiplier = 2;
        for ($index = strlen($body) - 1; $index >= 0; $index--) {
            $sum += ((int)$body[$index]) * $multiplier;
            $multiplier = $multiplier === 7 ? 2 : $multiplier + 1;
        }
        $result = 11 - ($sum % 11);
        $calculated = $result === 11 ? '0' : ($result === 10 ? 'K' : (string)$result);
        return $calculated === $expected;
    }

    private function newPage(bool $withInformation = false, bool $withItemsHeader = true): void
    {
        if ($this->page > 0) $this->renderFooter();
        $this->page = $this->pdf->addPage();
        $this->renderHeader();
        if ($withInformation) {
            $this->renderInformation();
        } else {
            $this->pdf->text($this->margin, 655, 'Continuación de productos y servicios', 8.5, true, 'left', $this->muted);
            $this->y = 637;
        }
        if ($withItemsHeader) $this->renderItemsHeader();
    }

    private function renderHeader(): void
    {
        $top = 756.0;
        $logo = $this->resolveLogoPath();
        $drawn = $logo ? $this->pdf->image($logo, $this->margin, $top - 35, 166, 36) : false;
        if (!$drawn) {
            $this->pdf->text($this->margin, $top - 18, (string)($this->company['company_name'] ?? 'ICYLAB SPA'), 18, true, 'left', $this->dark);
        }

        $giro = trim((string)($this->company['company_giro'] ?? 'PRODUCCIÓN, I+D Y VENTA DE ADITIVOS E INSUMOS PARA BEBIDAS Y ALIMENTOS'));
        $giroLines = $this->wrap($giro, 58);
        $metaY = 708.0;
        foreach (array_slice($giroLines, 0, 2) as $line) {
            $this->pdf->text($this->margin, $metaY, strtoupper($line), 7.4, true, 'left', $this->dark);
            $metaY -= 9;
        }
        $address = $this->join(array(
            $this->company['company_address'] ?? '',
            $this->company['company_comuna'] ?? '',
            $this->company['company_ciudad'] ?? '',
        ));
        $this->pdf->text($this->margin, $metaY - 2, 'Casa Matriz: ' . ($address ?: 'No informada'), 7.2, false, 'left', $this->muted);
        $contact = trim((string)($this->company['company_phone'] ?? ''));
        $email = trim((string)($this->company['company_email'] ?? ''));
        $this->pdf->text($this->margin, $metaY - 12, 'Fono: ' . ($contact ?: '—') . '  ·  Email: ' . ($email ?: '—'), 7.2, false, 'left', $this->muted);

        $boxX = 385.0;
        $boxY = 684.0;
        $boxW = 191.0;
        $boxH = 72.0;
        $this->pdf->roundedRect($boxX, $boxY, $boxW, $boxH, 5.5, $this->green, 1.4, $this->surface);
        $this->pdf->text($boxX + $boxW / 2, $boxY + 53, 'R.U.T.: ' . self::formatTaxIdentifier($this->company['company_tax_id'] ?? '77.884.104-5', 'Chile'), 10.0, true, 'center', $this->dark);
        $this->pdf->text($boxX + $boxW / 2, $boxY + 32, strtoupper((string)($this->document['document_type'] ?? 'DOCUMENTO')), 12.0, true, 'center', $this->green);
        $number = (string)($this->document['document_number'] ?? 'S/N');
        $this->pdf->text($boxX + $boxW / 2, $boxY + 13, 'Nº ' . $number, 8.8, true, 'center', $this->muted);
    }

    private function renderInformation(): void
    {
        $x = $this->margin;
        $top = 664.0;
        $width = 540.0;
        $labelWidth = 82.0;
        $valueWidth = 188.0;
        $rows = isset($this->document['information_rows']) && is_array($this->document['information_rows'])
            ? array_slice($this->document['information_rows'], 0, 4)
            : array();
        while (count($rows) < 4) $rows[] = array('', '', '', '');

        $preparedRows = array();
        $totalHeight = 0.0;
        foreach ($rows as $row) {
            $cells = array(
                $this->informationCell((string)($row[0] ?? ''), 15, 7.4, true, $labelWidth - 14, $this->muted),
                $this->informationCell((string)($row[1] ?? ''), 34, 7.7, !empty($row[4]), $valueWidth - 14, $this->dark),
                $this->informationCell((string)($row[2] ?? ''), 15, 7.4, true, $labelWidth - 14, $this->muted),
                $this->informationCell((string)($row[3] ?? ''), 34, 7.7, false, $valueWidth - 14, $this->dark),
            );
            $lineCount = max(array_map(function ($cell) { return count($cell['lines']); }, $cells));
            $rowHeight = max(22.0, 10.0 + ($lineCount * 9.5));
            $preparedRows[] = array('cells' => $cells, 'height' => $rowHeight);
            $totalHeight += $rowHeight;
        }

        $bottom = $top - $totalHeight;
        $this->pdf->beginRoundedClip($x, $bottom, $width, $totalHeight, 4.5);
        $rowTop = $top;
        foreach ($preparedRows as $index => $prepared) {
            $rowHeight = $prepared['height'];
            $rowBottom = $rowTop - $rowHeight;
            if ($index > 0) $this->pdf->line($x, $rowTop, $x + $width, $rowTop, 0.55, $this->border);
            $this->pdf->fillRect($x, $rowBottom, $labelWidth, $rowHeight, $this->surface);
            $this->pdf->fillRect($x + $labelWidth + $valueWidth, $rowBottom, $labelWidth, $rowHeight, $this->surface);
            $this->pdf->line($x + $labelWidth, $rowBottom, $x + $labelWidth, $rowTop, 0.45, $this->border);
            $this->pdf->line($x + $labelWidth + $valueWidth, $rowBottom, $x + $labelWidth + $valueWidth, $rowTop, 0.45, $this->border);
            $this->pdf->line($x + ($labelWidth * 2) + $valueWidth, $rowBottom, $x + ($labelWidth * 2) + $valueWidth, $rowTop, 0.45, $this->border);
            $this->drawInformationCell($x + 7, $rowTop, $rowHeight, $prepared['cells'][0]);
            $this->drawInformationCell($x + $labelWidth + 7, $rowTop, $rowHeight, $prepared['cells'][1]);
            $this->drawInformationCell($x + $labelWidth + $valueWidth + 7, $rowTop, $rowHeight, $prepared['cells'][2]);
            $this->drawInformationCell($x + ($labelWidth * 2) + $valueWidth + 7, $rowTop, $rowHeight, $prepared['cells'][3]);
            $rowTop = $rowBottom;
        }
        $this->pdf->endClip();
        $this->pdf->roundedRect($x, $bottom, $width, $totalHeight, 4.5, $this->border, 1.0);
        $this->y = $bottom - 18;
    }

    private function informationCell(string $text, int $wrapAt, float $size, bool $bold, float $maxWidth, array $color): array
    {
        $lines = $this->wrap($text, $wrapAt);
        if (!$lines) $lines = array('—');
        if (count($lines) > 2) $lines = array($lines[0], implode(' ', array_slice($lines, 1)));
        return array('lines' => $lines, 'size' => $size, 'bold' => $bold, 'max_width' => $maxWidth, 'color' => $color);
    }

    private function drawInformationCell(float $x, float $rowTop, float $rowHeight, array $cell): void
    {
        $lines = $cell['lines'];
        $lineHeight = 9.5;
        $firstY = $rowTop - ($rowHeight / 2) + (((count($lines) - 1) * $lineHeight) / 2) - 2.5;
        foreach ($lines as $index => $line) {
            $size = (float)$cell['size'];
            $textWidth = $this->pdf->textWidth($line, $size, (bool)$cell['bold']) * 1.08;
            if ($textWidth > (float)$cell['max_width'] && $textWidth > 0) {
                $size = max(5.6, $size * ((float)$cell['max_width'] / $textWidth));
            }
            $this->pdf->text($x, $firstY - ($index * $lineHeight), $line, $size, (bool)$cell['bold'], 'left', $cell['color']);
        }
    }

    private function renderItemsHeader(): void
    {
        $x = $this->margin;
        $height = 22.0;
        $this->itemsTableTop = $this->y;
        $this->pdf->fillRoundedRect($x, $this->y - $height, 540, $height, 4.5, $this->surface);
        $this->pdf->fillRect($x, $this->y - $height, 540, $height - 4.5, $this->surface);
        $this->pdf->line($x, $this->y - $height, $x + 540, $this->y - $height, 0.7, $this->border);
        $columns = $this->columns();
        foreach ($columns as $column) {
            $align = $column['header_align'] ?? $column['align'];
            $textX = $align === 'right' ? $column['x'] + $column['width'] - 5 : ($align === 'center' ? $column['x'] + $column['width'] / 2 : $column['x'] + 5);
            $this->pdf->text($textX, $this->y - 14.2, $column['label'], 7.0, true, $align, $this->muted);
        }
        $this->y -= $height;
    }

    private function renderItems(): void
    {
        $items = isset($this->document['items']) && is_array($this->document['items']) ? $this->document['items'] : array();
        foreach ($items as $item) {
            $name = trim((string)($item['name'] ?? $item['description'] ?? 'Producto'));
            $description = trim((string)($item['description'] ?? ''));
            $nameLines = $this->wrap($name, 39);
            $descriptionLines = $description !== '' && $description !== $name ? $this->wrap($description, 48) : array();
            $rowHeight = max(26.0, 10 + (count($nameLines) * 9.2) + (min(2, count($descriptionLines)) * 8.2));
            if ($this->y - $rowHeight < 150) {
                $this->finalizeItemsTable($this->y);
                $this->newPage(false);
            }

            $x = $this->margin;
            $bottom = $this->y - $rowHeight;
            $this->pdf->line($x, $bottom, $x + 540, $bottom, 0.45, $this->border);
            $columns = $this->columns();
            foreach ($columns as $index => $column) {
                if ($index > 0) $this->pdf->line($column['x'], $bottom, $column['x'], $this->y, 0.35, $this->border);
            }
            $baseY = $this->y - 15;
            $this->pdf->text($columns[0]['x'] + 5, $baseY, $this->truncate((string)($item['sku'] ?? '—'), 12), 7.0, false, 'left', $this->muted);
            foreach ($nameLines as $lineIndex => $line) {
                $this->pdf->text($columns[1]['x'] + 5, $baseY - ($lineIndex * 9.2), $line, 7.5, true, 'left', $this->dark);
            }
            $descriptionY = $baseY - (count($nameLines) * 9.2);
            foreach (array_slice($descriptionLines, 0, 2) as $lineIndex => $line) {
                $this->pdf->text($columns[1]['x'] + 5, $descriptionY - ($lineIndex * 8.2), $line, 6.8, false, 'left', $this->muted);
            }
            $this->cellText($columns[2], $baseY, $this->quantity($item['quantity'] ?? 0));
            $this->cellText($columns[3], $baseY, (string)($item['unit'] ?? 'unidad'));
            $this->cellText($columns[4], $baseY, $this->money($item['unit_price'] ?? 0));
            $this->cellText($columns[5], $baseY, !empty($item['taxable']) ? 'SI' : 'NO', !empty($item['taxable']) ? $this->green : $this->muted, true);
            $this->cellText($columns[6], $baseY, $this->money($item['amount'] ?? 0), $this->dark, true);
            $this->y = $bottom;
        }

        $minimumRows = max(0, 5 - count($items));
        for ($i = 0; $i < $minimumRows && $this->y - 24 >= 150; $i++) {
            $this->pdf->line($this->margin, $this->y - 24, $this->margin + 540, $this->y - 24, 0.45, $this->border);
            $this->y -= 24;
        }
        $this->finalizeItemsTable($this->y);
        $this->y -= 16;
    }

    private function finalizeItemsTable(float $bottom): void
    {
        if ($this->itemsTableTop === null || $this->itemsTableTop <= $bottom) return;
        $this->pdf->roundedRect($this->margin, $bottom, 540, $this->itemsTableTop - $bottom, 4.5, $this->border, 1.0);
        $this->itemsTableTop = null;
    }

    private function renderSummary(): void
    {
        $notes = isset($this->document['left_sections']) && is_array($this->document['left_sections']) ? $this->document['left_sections'] : array();
        $totals = isset($this->document['totals']) && is_array($this->document['totals']) ? $this->document['totals'] : array();
        $totals = array_values(array_filter($totals, function ($row) {
            return empty($row['hide_when_zero']) || abs((float)($row['value'] ?? 0)) > 0.00001;
        }));
        $estimated = 32 + (count($totals) * 17) + (count($notes) * 34);
        if ($this->y - $estimated < 58) {
            $this->newPage(false, false);
            $this->y -= 12;
        }

        $leftX = $this->margin;
        $leftWidth = 300.0;
        $rightX = 354.0;
        $rightWidth = 222.0;
        $top = $this->y;
        foreach ($notes as $section) {
            $title = strtoupper(trim((string)($section['title'] ?? '')));
            if ($title === '') continue;
            $this->pdf->text($leftX, $this->y, $title, 7.2, true, 'left', $this->muted);
            $this->pdf->line($leftX, $this->y - 5, $leftX + $leftWidth, $this->y - 5, 0.5, $this->border);
            $this->y -= 18;
            $lines = isset($section['lines']) && is_array($section['lines']) ? $section['lines'] : $this->wrap((string)($section['text'] ?? ''), 70);
            $boxLines = array();
            foreach ($lines as $line) {
                foreach ($this->wrap((string)$line, 70) as $wrapped) $boxLines[] = $wrapped;
            }
            $boxHeight = max(38, 15 + count($boxLines) * 9);
            $this->pdf->roundedRect($leftX, $this->y - $boxHeight + 7, $leftWidth, $boxHeight, 4.5, $this->border, 0.55, $this->surface);
            foreach ($boxLines as $index => $line) {
                $this->pdf->text($leftX + 9, $this->y - 7 - ($index * 9), $line, 7.0, $index === 0, 'left', $this->muted);
            }
            $this->y -= $boxHeight + 8;
        }

        $totalY = $top;
        foreach ($totals as $index => $row) {
            $isGrand = !empty($row['grand']);
            $isDiscount = !empty($row['negative']);
            $isBold = $isGrand || $isDiscount;
            $height = $isGrand ? 24.0 : 17.0;
            if ($isGrand) $this->pdf->fillRect($rightX, $totalY - $height + 4, $rightWidth, $height, $this->surface);
            $this->pdf->text($rightX + 8, $totalY - 8, (string)($row['label'] ?? ''), $isGrand ? 8.5 : 7.6, $isBold, 'left', $this->dark);
            $value = ($isDiscount ? '- ' : '') . $this->money($row['value'] ?? 0);
            $this->pdf->text($rightX + $rightWidth - 8, $totalY - 8, $value, $isGrand ? 8.8 : 7.6, $isBold, 'right', $this->dark);
            $this->pdf->line($rightX, $totalY - $height + 4, $rightX + $rightWidth, $totalY - $height + 4, $isGrand ? 1.4 : 0.45, $isGrand ? $this->green : $this->border);
            $totalY -= $height;
        }
        $this->y = min($this->y, $totalY) - 6;
    }

    private function renderFooter(): void
    {
        $lines = $this->footerLines();
        if (!$lines) return;
        $y = 16 + ((count($lines) - 1) * 9);
        foreach ($lines as $line) {
            $this->pdf->text($this->margin, $y, $line, 6.6, false, 'left', $this->muted);
            $y -= 9;
        }
    }

    private function columns(): array
    {
        $x = $this->margin;
        $spec = array(
            array('label' => 'SKU', 'width' => 58, 'align' => 'left', 'header_align' => 'left'),
            array('label' => 'GLOSA / DESCRIPCIÓN', 'width' => 197, 'align' => 'left', 'header_align' => 'left'),
            array('label' => 'CANT.', 'width' => 45, 'align' => 'center', 'header_align' => 'center'),
            array('label' => 'UNIDAD', 'width' => 47, 'align' => 'center', 'header_align' => 'center'),
            array('label' => 'PRC. UNIT.', 'width' => 68, 'align' => 'right', 'header_align' => 'center'),
            array('label' => 'AFECTO', 'width' => 48, 'align' => 'center', 'header_align' => 'center'),
            array('label' => 'MONTO', 'width' => 77, 'align' => 'right', 'header_align' => 'center'),
        );
        foreach ($spec as &$column) {
            $column['x'] = $x;
            $x += $column['width'];
        }
        unset($column);
        return $spec;
    }

    private function cellText(array $column, float $y, string $text, ?array $color = null, bool $bold = false): void
    {
        $align = $column['align'];
        $x = $align === 'right' ? $column['x'] + $column['width'] - 5 : ($align === 'center' ? $column['x'] + $column['width'] / 2 : $column['x'] + 5);
        $this->pdf->text($x, $y, $this->truncate($text, 14), 7.2, $bold, $align, $color ?: $this->dark);
    }

    private function money($value): string
    {
        $currency = strtoupper((string)($this->document['currency'] ?? 'CLP'));
        $decimals = $currency === 'CLP' ? 0 : 2;
        $prefix = $currency === 'CLP' ? '$' : $currency . ' ';
        return $prefix . number_format((float)$value, $decimals, ',', '.');
    }

    private function quantity($value): string
    {
        return rtrim(rtrim(number_format((float)$value, 3, ',', '.'), '0'), ',');
    }

    private function resolveLogoPath(): ?string
    {
        $appRoot = realpath(dirname(__DIR__, 2));
        if (!$appRoot) return null;
        $configured = trim((string)($this->company['company_logo'] ?? ''));
        if ($configured !== '') {
            if (strpos($configured, $appRoot) === 0 && is_file($configured)) return $configured;
            $candidate = $appRoot . '/' . ltrim(parse_url($configured, PHP_URL_PATH) ?: $configured, '/');
            if (is_file($candidate)) return $candidate;
        }
        $fallback = $appRoot . '/assets/img/icylab_document_logo.jpg';
        return is_file($fallback) ? $fallback : null;
    }

    private function join(array $values): string
    {
        return trim(implode(', ', array_filter(array_map(function ($value) { return trim((string)$value); }, $values), function ($value) { return $value !== ''; })));
    }

    private function footerLines(): array
    {
        $footer = str_replace(array("\r\n", "\r"), "\n", (string)($this->company['commercial_document_footer'] ?? ''));
        if (trim($footer) === '') return array();
        $lines = array();
        foreach (explode("\n", $footer) as $paragraph) {
            if (trim($paragraph) === '') {
                if ($lines && end($lines) !== '') $lines[] = '';
                continue;
            }
            foreach ($this->wrap($paragraph, 120) as $line) $lines[] = $line;
        }
        return array_slice($lines, 0, 8);
    }

    private function wrap(string $text, int $maxChars): array
    {
        $text = trim(preg_replace('/\s+/u', ' ', $text));
        if ($text === '') return array();
        $words = preg_split('/\s+/u', $text);
        $lines = array();
        $line = '';
        foreach ($words as $word) {
            $candidate = $line === '' ? $word : $line . ' ' . $word;
            if ($this->length($candidate) <= $maxChars) {
                $line = $candidate;
            } else {
                if ($line !== '') $lines[] = $line;
                $line = $word;
            }
        }
        if ($line !== '') $lines[] = $line;
        return $lines;
    }

    private function truncate(string $text, int $max): string
    {
        $text = trim(preg_replace('/\s+/u', ' ', $text));
        if ($this->length($text) <= $max) return $text;
        return rtrim($this->substring($text, 0, max(1, $max - 1))) . '…';
    }

    private function length(string $text): int
    {
        return function_exists('mb_strlen') ? mb_strlen($text, 'UTF-8') : strlen($text);
    }

    private function substring(string $text, int $start, int $length): string
    {
        return function_exists('mb_substr') ? mb_substr($text, $start, $length, 'UTF-8') : substr($text, $start, $length);
    }
}
