<?php

declare(strict_types=1);

use setasign\Fpdi\Fpdi;

require_once __DIR__ . '/vendor_loader.php';
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/pdf_helpers.php';
require_once __DIR__ . '/contract_form.php';
require_once __DIR__ . '/contract_template.php';

function pdf_bootstrap_vendor(): void
{
    karvi_load_vendor(true);
}

pdf_bootstrap_vendor();

class ContractPDF extends FPDF
{
    public const EDGE_PAD = 8;
    public const HEADER_BAR_HEIGHT = 16;
    public const FOOTER_HEIGHT = 34;

    private const SECTION_GRAY = 240;
    private const HEADER_GRAY = 240;
    private const FOOTER_GRAY = 240;

    private string $reference = '';
    private array $karvi = [];
    private string $logoPath = '';

    public function setReference(string $reference): void
    {
        $this->reference = $reference;
    }

    public function setKarvi(array $karvi): void
    {
        $this->karvi = $karvi;
    }

    public function setLogoPath(string $logoPath): void
    {
        $this->logoPath = $logoPath;
    }

    public function Header(): void
    {
        $edgePad = self::EDGE_PAD;
        $barHeight = self::HEADER_BAR_HEIGHT;
        $totalHeight = $edgePad + $barHeight;

        $this->SetFillColor(self::HEADER_GRAY, self::HEADER_GRAY, self::HEADER_GRAY);
        $this->Rect(0, 0, 210, $totalHeight, 'F');

        if ($this->logoPath !== '' && file_exists($this->logoPath)) {
            $this->Image($this->logoPath, 12, $edgePad + 3, 41, 0);
        } else {
            $this->SetTextColor(40, 40, 40);
            $this->SetFont('Arial', '', 9);
            $this->SetXY(12, $edgePad + 5);
            $this->Cell(90, 6, pdf_text($this->karvi['name'] ?? 'Karvi GmbH'), 0, 0, 'L');
        }

        $this->SetTextColor(0, 0, 0);
        $this->SetY($totalHeight + 4);
    }

    public function Footer(): void
    {
        $karvi = $this->karvi;
        $footerHeight = self::FOOTER_HEIGHT;
        $edgePad = self::EDGE_PAD;

        $this->SetFillColor(self::FOOTER_GRAY, self::FOOTER_GRAY, self::FOOTER_GRAY);
        $this->Rect(0, $this->GetPageHeight() - $footerHeight, 210, $footerHeight, 'F');

        $this->SetY(-($footerHeight - $edgePad));

        $addressLine = sprintf(
            '%s, %s, %s, %s',
            $karvi['name'] ?? 'Karvi GmbH',
            $karvi['street'] ?? '',
            $karvi['city'] ?? '',
            $karvi['country'] ?? 'Deutschland'
        );

        $this->SetFont('Arial', '', 8);
        $this->SetTextColor(30, 30, 30);
        $this->Cell(0, 5, pdf_text($addressLine), 0, 1, 'L');

        $this->SetDrawColor(210, 210, 210);
        $this->SetLineWidth(0.2);
        $this->Line(12, $this->GetY() + 1, 198, $this->GetY() + 1);
        $this->Ln(3);

        $columnTop = $this->GetY();
        $this->SetFont('Arial', '', 7.5);
        $this->SetTextColor(50, 50, 50);

        $this->SetXY(12, $columnTop);
        $this->Cell(93, 4, pdf_text('Tel.: ' . ($karvi['phone'] ?? '')), 0, 2, 'L');
        $this->SetX(12);
        $this->Cell(93, 4, pdf_text('E-Mail: ' . ($karvi['email'] ?? '')), 0, 2, 'L');
        $this->SetX(12);
        $this->Cell(93, 4, pdf_text('Website: ' . ($karvi['web'] ?? '')), 0, 0, 'L');

        $this->SetXY(110, $columnTop);
        $this->Cell(88, 4, pdf_text('Frankfurt am Main'), 0, 2, 'R');
        $this->SetX(110);
        $this->Cell(88, 4, pdf_text($karvi['register'] ?? ''), 0, 2, 'R');
        $this->SetX(110);
        $this->Cell(88, 4, pdf_text('Inhaber/-in: ' . ($karvi['owner'] ?? '')), 0, 0, 'R');
    }
}

function pdf_euro(): string
{
    return chr(128);
}

function pdf_text(string $text): string
{
    if ($text === '') {
        return '';
    }

    $euroPlaceholder = '__PDF_EURO__';
    $text = str_replace('€', $euroPlaceholder, $text);

    if (function_exists('iconv')) {
        $converted = @iconv('UTF-8', 'Windows-1252//TRANSLIT', $text);
        if ($converted === false) {
            $converted = @iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $text);
        }
        if ($converted !== false) {
            return str_replace([$euroPlaceholder, ' EUR'], [pdf_euro(), pdf_euro()], $converted);
        }
    }

    if (function_exists('mb_convert_encoding')) {
        $converted = @mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
        if ($converted !== false) {
            return str_replace([$euroPlaceholder, ' EUR'], [pdf_euro(), pdf_euro()], $converted);
        }
    }

    return str_replace([$euroPlaceholder, ' EUR'], [pdf_euro(), pdf_euro()], preg_replace('/[^\x09\x0A\x0D\x20-\x7E]/', '?', $text));
}

function pdf_contract_date(?string $date): string
{
    if (!$date) {
        return '___ / ___ / ______';
    }

    $timestamp = strtotime($date);
    if ($timestamp === false) {
        return $date;
    }

    return date('d', $timestamp) . ' / ' . date('m', $timestamp) . ' / ' . date('Y', $timestamp);
}

function pdf_contract_date_dash(?string $date): string
{
    if (!$date) {
        return '___-__-____';
    }

    $timestamp = strtotime($date);
    if ($timestamp === false) {
        return $date;
    }

    return date('d-m-Y', $timestamp);
}

function pdf_check_space(FPDF $pdf, float $height = 20): void
{
    $pageHeight = $pdf->GetPageHeight();
    $limit = $pageHeight - ContractPDF::FOOTER_HEIGHT - 6.0;

    if ($pdf->PageNo() >= 4) {
        $limit = pdf_signature_zone_top($pageHeight);
        if ($pdf->GetY() + $height > $limit) {
            return;
        }
    }

    if ($pdf->GetY() + $height > $limit) {
        $pdf->AddPage();
    }
}

function pdf_draw_checkbox(FPDF $pdf, float $x, float $y, float $size, bool $checked): void
{
    $pdf->SetDrawColor(60, 60, 60);
    $pdf->SetLineWidth(0.3);
    $pdf->Rect($x, $y, $size, $size);

    if (!$checked) {
        return;
    }

    $pdf->SetDrawColor(20, 20, 20);
    $pdf->SetLineWidth(0.45);
    $pdf->Line($x + ($size * 0.2), $y + ($size * 0.52), $x + ($size * 0.42), $y + ($size * 0.74));
    $pdf->Line($x + ($size * 0.42), $y + ($size * 0.74), $x + ($size * 0.8), $y + ($size * 0.24));
}

function pdf_page_break(FPDF $pdf): void
{
    $pdf->AddPage();
}

function pdf_section_heading(FPDF $pdf, string $number, string $title): void
{
    pdf_check_space($pdf, 16);

    $pdf->SetFillColor(240, 240, 240);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->SetFont('Arial', '', 12);
    $pdf->Cell(0, 9, pdf_text(trim($number . '  ' . $title)), 0, 1, 'L', true);
    $pdf->Ln(3);
}

function pdf_subheading(FPDF $pdf, string $title): void
{
    pdf_check_space($pdf, 12);
    $pdf->SetFillColor(240, 240, 240);
    $pdf->SetFont('Arial', '', 11);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->Cell(0, 8, pdf_text($title), 0, 1, 'L', true);
    $pdf->Ln(2);
}

function pdf_paragraph(FPDF $pdf, string $text, int $fontSize = 9): void
{
    pdf_check_space($pdf, 14);
    $pdf->SetFont('Arial', '', $fontSize);
    $pdf->SetTextColor(40, 40, 40);
    $pdf->MultiCell(0, 4.8, pdf_text($text), 0, 'J');
    $pdf->Ln(2);
}

function pdf_clause(FPDF $pdf, string $number, string $text): void
{
    pdf_check_space($pdf, 16);
    $pdf->SetFont('Arial', '', 11);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->Cell(14, 5, pdf_text($number), 0, 0, 'L');
    $pdf->SetFont('Arial', '', 9);
    $pdf->SetTextColor(40, 40, 40);
    $pdf->MultiCell(0, 4.8, pdf_text($text), 0, 'J');
    $pdf->Ln(1);
}

function pdf_clause_number_value(string $number): float
{
    return (float)str_replace(',', '.', trim($number));
}

function pdf_clause_compact(FPDF $pdf, string $number, string $text): void
{
    pdf_check_space($pdf, 10);
    $pdf->SetFont('Arial', '', 10);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->Cell(12, 4.2, pdf_text($number), 0, 0, 'L');
    $pdf->SetFont('Arial', '', 8.3);
    $pdf->SetTextColor(40, 40, 40);
    $pdf->MultiCell(0, 3.8, pdf_text($text), 0, 'J');
    $pdf->Ln(0.2);
}

function pdf_signature_reserved_height(): float
{
    return ContractPDF::FOOTER_HEIGHT + 34.0 + 48.0 + 8.0;
}

function pdf_signature_zone_top(float $pageHeight): float
{
    $layout = pdf_partner_signature_box($pageHeight);

    return $layout['y_start'] - 8.0;
}

function pdf_clause_41(FPDF $pdf, array $contract): void
{
    pdf_check_space($pdf, 24);

    $selected = contract_selected_brands($contract);
    $brands = [
        ['key' => 'Gulab', 'label' => 'Gulab'],
        ['key' => 'Flamers', 'label' => 'Flamers Pizza'],
    ];

    $pdf->SetFont('Arial', '', 11);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->Cell(14, 5, pdf_text('4.1'), 0, 0, 'L');
    $pdf->SetFont('Arial', '', 9);
    $pdf->SetTextColor(40, 40, 40);
    $pdf->MultiCell(0, 4.8, pdf_text(contract_template_clause_41_intro()), 0, 'J');

    $pdf->Ln(2);
    $pdf->SetFont('Arial', '', 9);
    $pdf->SetTextColor(40, 40, 40);

    $checkboxSize = 3.5;
    $startX = 26.0;
    $lineH = 5.0;

    foreach ($brands as $brand) {
        $checked = in_array($brand['key'], $selected, true);
        $y = $pdf->GetY();
        pdf_draw_checkbox($pdf, $startX, $y + 0.6, $checkboxSize, $checked);
        $pdf->SetXY($startX + $checkboxSize + 2.5, $y);
        $pdf->Cell(0, $lineH, pdf_text($brand['label']), 0, 1, 'L');
    }

    $pdf->Ln(1);
}

function pdf_party_block(FPDF $pdf, array $karvi, bool $allowBreak = true): void
{
    pdf_section_heading($pdf, '1.', 'Franchisegeber / Dienstleister');

    $lines = [
        $karvi['name'],
        $karvi['street'],
        $karvi['city'],
        $karvi['country'],
        '(nachfolgend „Karvi GmbH" genannt)',
    ];

    $pdf->SetFont('Arial', '', 9);
    $pdf->SetTextColor(40, 40, 40);
    foreach ($lines as $line) {
        if ($allowBreak) {
            pdf_check_space($pdf, 6);
        }
        $pdf->Cell(0, 4.2, pdf_text($line), 0, 1, 'L');
    }
    $pdf->Ln(1);
}

function pdf_partner_table(FPDF $pdf, array $contract, bool $allowBreak = true): void
{
    pdf_section_heading($pdf, '2.', 'Franchisepartner / Restaurant');

    $fields = contract_template_partner_fields();
    $labelW = 93.0;
    $valueW = 93.0;
    $padX = 1.2;
    $padY = 1.0;
    $lineH = 5.0;
    $minRowH = 7.0;
    $row = 0;

    foreach ($fields as $key => $label) {
        if ($allowBreak) {
            pdf_check_space($pdf, 14);
        }

        $x = $pdf->GetX();
        $y = $pdf->GetY();
        $fill = $row % 2 === 0;
        $pdf->SetFillColor($fill ? 248 : 255, $fill ? 248 : 255, $fill ? 248 : 255);
        $pdf->SetDrawColor(220, 220, 220);
        $pdf->SetTextColor(40, 40, 40);

        $pdf->SetFont('Arial', '', 9);
        $pdf->SetXY($x + $padX, $y + $padY);
        $pdf->MultiCell($labelW - ($padX * 2), $lineH, pdf_text($label), 0, 'L');
        $rowHeight = max($minRowH, ($pdf->GetY() - $y) + $padY);

        $pdf->Rect($x, $y, $labelW, $rowHeight, 'DF');
        $pdf->Rect($x + $labelW, $y, $valueW, $rowHeight, 'DF');

        $pdf->SetFont('Arial', '', 9);
        $pdf->SetXY($x + $padX, $y + $padY);
        $pdf->MultiCell($labelW - ($padX * 2), $lineH, pdf_text($label), 0, 'L');

        $pdf->SetFont('Arial', '', 9);
        $pdf->SetXY($x + $labelW + $padX, $y + (($rowHeight - $lineH) / 2));
        $pdf->Cell($valueW - ($padX * 2), $lineH, pdf_text(pdf_partner_field_value($contract, $key)), 0, 0, 'L');

        $pdf->SetXY($x, $y + $rowHeight);
        $row++;
    }

    $pdf->SetTextColor(0, 0, 0);
    $pdf->Ln(1);
}

function pdf_dashed_line(FPDF $pdf, float $x1, float $y1, float $x2, float $y2, float $dash = 2, float $gap = 1.5): void
{
    $length = hypot($x2 - $x1, $y2 - $y1);
    if ($length <= 0) {
        return;
    }

    $dx = ($x2 - $x1) / $length;
    $dy = ($y2 - $y1) / $length;
    $pos = 0.0;
    $draw = true;

    while ($pos < $length) {
        $segment = $draw ? $dash : $gap;
        $next = min($pos + $segment, $length);

        if ($draw) {
            $pdf->Line(
                $x1 + ($dx * $pos),
                $y1 + ($dy * $pos),
                $x1 + ($dx * $next),
                $y1 + ($dy * $next)
            );
        }

        $pos = $next;
        $draw = !$draw;
    }
}

function pdf_dashed_rect(FPDF $pdf, float $x, float $y, float $w, float $h): void
{
    $pdf->SetDrawColor(150, 150, 150);
    $pdf->SetLineWidth(0.35);
    pdf_dashed_line($pdf, $x, $y, $x + $w, $y);
    pdf_dashed_line($pdf, $x, $y + $h, $x + $w, $y + $h);
    pdf_dashed_line($pdf, $x, $y, $x, $y + $h);
    pdf_dashed_line($pdf, $x + $w, $y, $x + $w, $y + $h);
}

function pdf_commission_block(FPDF $pdf, string $platform, array $platformMeta, array $contract, bool $allowBreak = true): void
{
    $rows = contract_template_commission_rows();
    $rowCount = count($rows);
    $cardX = 12.0;
    $cardW = 186.0;
    $pad = 2.0;
    $headerH = 9.0;
    $rowH = 8.0;
    $cardH = ($pad * 2) + $headerH + 2.0 + ($rowCount * $rowH);

    if ($allowBreak) {
        pdf_check_space($pdf, $cardH + 4);
    }

    $yStart = $pdf->GetY() + 1;

    $pdf->SetFillColor(252, 252, 252);
    $pdf->Rect($cardX, $yStart, $cardW, $cardH, 'F');
    pdf_dashed_rect($pdf, $cardX, $yStart, $cardW, $cardH);

    $pdf->SetFillColor(245, 245, 245);
    $pdf->Rect($cardX + 2, $yStart + 2, $cardW - 4, $headerH, 'F');
    $pdf->SetFont('Arial', '', 10);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->SetXY($cardX + 5, $yStart + 4.5);
    $pdf->Cell($cardW - 10, 5, pdf_text($platformMeta['heading']), 0, 1, 'L');

    $y = $yStart + 2 + $headerH + 2;
    $labelW = 98.0;
    $valueW = $cardW - 10 - $labelW;
    $rowIndex = 0;

    foreach ($rows as $suffix => $label) {
        if ($suffix === 'platform_delivery_pct') {
            $label = $platformMeta['platform_delivery_label'];
        }

        $field = $platform . '_' . $suffix;
        $value = $contract[$field] ?? '';

        if (str_ends_with($suffix, '_pct')) {
            $display = pdf_format_pct((string)$value);
        } else {
            $display = pdf_format_fee((string)$value);
        }

        $fill = $rowIndex % 2 === 0;
        $pdf->SetDrawColor(210, 210, 210);
        $pdf->SetFillColor($fill ? 255 : 248, $fill ? 255 : 248, $fill ? 255 : 248);
        $pdf->SetXY($cardX + 5, $y);
        $pdf->SetFont('Arial', '', 9.5);
        $pdf->Cell($labelW, $rowH, pdf_text($label), 1, 0, 'L', true);
        $pdf->Cell($valueW, $rowH, pdf_text($display), 1, 1, 'L', true);

        $y += $rowH;
        $rowIndex++;
    }

    $pdf->SetY($yStart + $cardH + 2);
}

function pdf_signature_block(FPDF $pdf, array $contract, array $karvi): float
{
    $pageHeight = $pdf->GetPageHeight();
    $layout = pdf_partner_signature_box($pageHeight);
    $yStart = $layout['y_start'];
    $boxHeight = $layout['box_height'];
    $headingY = $yStart - 7.0;

    $pdf->SetFont('Arial', '', 10);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->SetXY(12.0, $headingY);
    $pdf->Cell(0, 5, pdf_text('UNTERSCHRIFTEN'), 0, 1, 'L');

    $contractDate = pdf_contract_date_dash($contract['contract_date'] ?? null);
    $signatureLineOffset = $boxHeight - 12.0;

    $contentX = 12.0;
    $contentW = 186.0;
    $gap = 4.0;
    $leftW = ($contentW - $gap) / 3;
    $rightW = ($contentW - $gap) * 2 / 3;
    $rightX = $contentX + $leftW + $gap;
    $leftPadX = $contentX + 4;
    $rightPadX = $rightX + 4;
    $leftTextW = $leftW - 8;
    $rightTextW = $rightW - 8;

    $pdf->SetFillColor(250, 250, 250);
    $pdf->Rect($contentX, $yStart, $leftW, $boxHeight, 'F');
    $pdf->Rect($rightX, $yStart, $rightW, $boxHeight, 'F');

    $pdf->SetFont('Arial', '', 9);
    $pdf->SetXY($leftPadX, $yStart + 3);
    $pdf->Cell($leftTextW, 4.5, pdf_text($karvi['name']), 0, 2);
    $pdf->SetFont('Arial', '', 8.5);
    $pdf->Cell($leftTextW, 4.5, pdf_text('Name: ' . $karvi['signatory_name']), 0, 2);
    $pdf->Cell($leftTextW, 4.5, pdf_text('Position: ' . $karvi['signatory_title']), 0, 2);
    $pdf->Cell($leftTextW, 4.5, pdf_text('Unterschrift:'), 0, 2);

    $karviSignatureLineY = $yStart + $signatureLineOffset;
    $pdf->Line($leftPadX, $karviSignatureLineY, $contentX + $leftW - 4, $karviSignatureLineY);
    $pdf->SetXY($leftPadX, $karviSignatureLineY + 1.5);
    $pdf->Cell($leftTextW, 4.5, pdf_text('Datum: ' . $contractDate), 0, 0);

    $pdf->SetFont('Arial', '', 9);
    $pdf->SetXY($rightPadX, $yStart + 3);
    $pdf->Cell($rightTextW, 4.5, pdf_text('Franchisepartner / Restaurant'), 0, 2);
    $pdf->SetFont('Arial', '', 8.5);
    $pdf->Cell($rightTextW, 4.5, pdf_text('Restaurantname: ' . pdf_field_value($contract, 'restaurant_name')), 0, 2);
    $pdf->Cell($rightTextW, 4.5, pdf_text('Name des Inhabers: ' . pdf_partner_field_value($contract, 'owner_name')), 0, 2);
    $pdf->Cell($rightTextW, 4.5, pdf_text('Unterschrift:'), 0, 2);

    $signatureLineY = $yStart + $signatureLineOffset;
    $pdf->Line($rightPadX, $signatureLineY, $rightX + $rightW - 4, $signatureLineY);
    $pdf->SetXY($rightPadX, $signatureLineY + 1.5);
    $pdf->Cell($rightTextW, 4.5, pdf_text('Datum: ' . $contractDate), 0, 0);

    $pdf->SetY($layout['audit_max_y']);

    return $signatureLineY;
}

function generate_contract_pdf(array $contract, ?string $outputPath = null): string
{
    $previousLocale = $GLOBALS['karvi_locale'] ?? null;
    $previousTranslations = $GLOBALS['karvi_translations'] ?? null;
    $GLOBALS['karvi_locale'] = 'de';
    $GLOBALS['karvi_translations'] = require __DIR__ . '/../lang/de.php';

    $config = app_config();
    $filename = $contract['token'] . '.pdf';
    $targetPath = $outputPath ?? ($config['contracts_dir'] . '/' . $filename);

    $karvi = contract_template_karvi();
    $ref = 'KP-' . str_pad((string)($contract['id'] ?? 0), 5, '0', STR_PAD_LEFT);

    $pdf = new ContractPDF();
    $pdf->setReference($ref);
    $pdf->setKarvi($karvi);
    $pdf->setLogoPath($config['logo_path'] ?? '');
    $pdf->AliasNbPages();
    $pdf->SetAutoPageBreak(true, ContractPDF::FOOTER_HEIGHT + 4);
    $pdf->SetMargins(12, ContractPDF::EDGE_PAD + ContractPDF::HEADER_BAR_HEIGHT + 6, 12);
    $pdf->AddPage();

    $pdf->Ln(6);
    pdf_check_space($pdf, 16);
    $pdf->SetFont('Arial', '', 12);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->MultiCell(0, 6, pdf_text(contract_template_title()), 0, 'C');
    $pdf->Ln(2);

    $opening = str_replace(
        '{contract_date}',
        pdf_contract_date($contract['contract_date'] ?? null),
        contract_template_opening()
    );
    pdf_paragraph($pdf, $opening, 10);
    pdf_paragraph($pdf, 'zwischen', 10);

    pdf_party_block($pdf, $karvi, false);
    pdf_paragraph($pdf, 'und', 10);
    pdf_partner_table($pdf, $contract, false);

    pdf_page_break($pdf);

    $pdf->SetAutoPageBreak(false);
    pdf_section_heading($pdf, '3.', 'Kommerzielle Bedingungen');

    foreach (contract_template_commission_platforms() as $platform => $meta) {
        pdf_commission_block($pdf, $platform, $meta, $contract, false);
    }

    $pdf->Ln(6);
    pdf_subheading($pdf, 'Technische Dienstleistungen');
    foreach (contract_template_technical_services() as $service) {
        $pdf->SetFont('Arial', '', 10);
        $pdf->Cell(4, 5, pdf_text('•'), 0, 0);
        $pdf->MultiCell(0, 5.5, pdf_text($service), 0, 'L');
    }
    $pdf->Ln(3);

    pdf_subheading($pdf, 'Zusätzliche Bedingungen');
    $pdf->SetFont('Arial', '', 10);
    $pdf->SetTextColor(40, 40, 40);
    $pdf->MultiCell(0, 5.2, pdf_text(contract_template_additional_conditions()), 0, 'J');
    $pdf->Ln(2);

    pdf_page_break($pdf);
    $pdf->SetAutoPageBreak(true, ContractPDF::FOOTER_HEIGHT + 4);

    pdf_section_heading($pdf, '4.', 'Franchise-Rechte, Verantwortlichkeiten, Haftung, DSGVO, Laufzeit & Schlussbestimmungen');

    pdf_clause_41($pdf, $contract);

    foreach (contract_template_legal_clauses() as $number => $text) {
        $clauseValue = pdf_clause_number_value($number);

        if ($number === '4.20') {
            if ($pdf->PageNo() < 4) {
                pdf_page_break($pdf);
            }
            $pdf->SetFont('Arial', '', 10);
            $pdf->SetTextColor(0, 0, 0);
            $pdf->Cell(0, 6, pdf_text('Bevollmächtigung'), 0, 1, 'L');
            $pdf->Ln(1);
        }

        if ($clauseValue >= 4.2) {
            pdf_clause_compact($pdf, $number, $text);
            continue;
        }

        pdf_clause($pdf, $number, $text);
    }

    if ($pdf->PageNo() > 4) {
        throw new RuntimeException('Contract content exceeds the 4-page layout. Please shorten custom fields.');
    }

    pdf_signature_block($pdf, $contract, $karvi);

    $dir = dirname($targetPath);
    if (!is_dir($dir) && !@mkdir($dir, 0755, true) && !is_dir($dir)) {
        throw new RuntimeException('Cannot create PDF folder. Make uploads/contracts writable on the server.');
    }

    if (!is_writable($dir)) {
        throw new RuntimeException('PDF folder is not writable. Set chmod 755 or 775 on uploads/contracts.');
    }

    $pdf->Output('F', $targetPath);

    if (!is_file($targetPath)) {
        throw new RuntimeException('PDF could not be saved. Check uploads/contracts permissions.');
    }

    if ($previousLocale !== null) {
        $GLOBALS['karvi_locale'] = $previousLocale;
    } else {
        unset($GLOBALS['karvi_locale']);
    }

    if ($previousTranslations !== null) {
        $GLOBALS['karvi_translations'] = $previousTranslations;
    } else {
        unset($GLOBALS['karvi_translations']);
    }

    return basename($targetPath);
}

function generate_template_pdf(bool $force = true): string
{
    $config = app_config();
    $path = $config['template_pdf'];

    if (!$force && is_file($path)) {
        return $path;
    }

    $dir = $config['templates_dir'] ?? dirname($path);
    if (!is_dir($dir)) {
        mkdir($dir, 0755, true);
    }

    generate_contract_pdf(contract_template_sample_data(), $path);

    return $path;
}

function pdf_signature_image_size(string $path, float $maxWidth, float $maxHeight): array
{
    $info = @getimagesize($path);
    if ($info === false || ($info[1] ?? 0) <= 0) {
        return ['width' => $maxWidth, 'height' => $maxHeight];
    }

    $ratio = (float)$info[0] / (float)$info[1];
    $height = $maxHeight;
    $width = $height * $ratio;

    if ($width > $maxWidth) {
        $width = $maxWidth;
        $height = $width / $ratio;
    }

    return ['width' => $width, 'height' => $height];
}

function pdf_partner_signature_box(float $pageHeight): array
{
    $contentX = 12.0;
    $contentW = 186.0;
    $gap = 4.0;
    $leftW = ($contentW - $gap) / 3;
    $rightW = ($contentW - $gap) * 2 / 3;
    $rightX = $contentX + $leftW + $gap;
    $rightPadX = $rightX + 4;
    $rightInnerW = $rightW - 8;
    $auditStripHeight = 34.0;
    $boxHeight = 48.0;
    $yStart = $pageHeight - ContractPDF::FOOTER_HEIGHT - $auditStripHeight - $boxHeight - 2.0;
    $centerY = $yStart + ($boxHeight / 2.0);
    $auditY = $yStart + $boxHeight + 1.5;

    return [
        'content_x' => $contentX,
        'content_w' => $contentW,
        'right_pad_x' => $rightPadX,
        'right_inner_w' => $rightInnerW,
        'center_y' => $centerY,
        'right_x' => $rightX,
        'right_w' => $rightW,
        'y_start' => $yStart,
        'box_height' => $boxHeight,
        'audit_y' => $auditY,
        'audit_strip_height' => $auditStripHeight,
        'audit_max_y' => $auditY + $auditStripHeight - 2.0,
    ];
}

function pdf_audit_datetime(?string $value): string
{
    $value = trim((string)$value);
    if ($value === '') {
        return '—';
    }

    $timestamp = strtotime($value);
    if ($timestamp === false) {
        return $value;
    }

    return date('d.m.Y H:i:s', $timestamp);
}

function pdf_audit_method_label(?string $method): string
{
    return match (strtolower(trim((string)$method))) {
        'upload' => 'Upload',
        'draw' => 'Zeichnung',
        default => '—',
    };
}

function pdf_audit_device_label(?string $userAgent): string
{
    $userAgent = trim((string)$userAgent);
    if ($userAgent === '') {
        return '—';
    }

    if (preg_match('/iPhone/i', $userAgent)) {
        return 'iPhone';
    }
    if (preg_match('/iPad/i', $userAgent)) {
        return 'iPad';
    }
    if (preg_match('/Android/i', $userAgent)) {
        return 'Android';
    }
    if (preg_match('/Mobile|Tablet/i', $userAgent)) {
        return 'Mobiles Gerät';
    }
    if (preg_match('/Windows NT/i', $userAgent)) {
        return 'Windows-Rechner';
    }
    if (preg_match('/Mac OS X|Macintosh/i', $userAgent)) {
        return 'macOS';
    }
    if (preg_match('/Linux/i', $userAgent)) {
        return 'Linux';
    }

    return mb_strlen($userAgent) > 48 ? mb_substr($userAgent, 0, 45) . '...' : $userAgent;
}

function pdf_signature_audit_lines(array $record): array
{
    $lines = [];

    $name = trim((string)($record['signer_name'] ?? ''));
    if ($name !== '') {
        $lines[] = 'Name: ' . $name;
    }

    $date = pdf_audit_datetime($record['signed_at'] ?? null);
    if ($date !== '—') {
        $lines[] = 'Datum: ' . $date;
    }

    $ip = trim((string)($record['ip_address'] ?? ''));
    if ($ip !== '' && $ip !== 'unknown') {
        $lines[] = 'IP-Adresse: ' . $ip;
    }

    $device = pdf_audit_device_label($record['user_agent'] ?? null);
    if ($device !== '' && $device !== '—') {
        $lines[] = 'Gerät: ' . $device;
    }

    $screen = trim((string)($record['screen_resolution'] ?? ''));
    if ($screen !== '') {
        $lines[] = 'Bildschirm: ' . $screen;
    }

    $timezone = trim((string)($record['timezone'] ?? ''));
    if ($timezone !== '') {
        $lines[] = 'Zeitzone: ' . $timezone;
    }

    $method = pdf_audit_method_label($record['signature_method'] ?? null);
    if ($method !== '—') {
        $lines[] = 'Methode: ' . $method;
    }

    return $lines;
}

function pdf_signature_audit_line(array $record): string
{
    $lines = pdf_signature_audit_lines($record);

    if ($lines === []) {
        return '';
    }

    return implode(' | ', $lines);
}

function pdf_page_audit_strip_box(float $pageHeight, string $layoutStyle = 'partner'): array
{
    $contentX = $layoutStyle === 'invoice' ? 14.0 : 12.0;
    $contentW = $layoutStyle === 'invoice' ? 182.0 : 186.0;
    $stripH = $layoutStyle === 'invoice' ? 14.0 : 12.0;
    $footerH = $layoutStyle === 'invoice' ? 45.0 : (float)ContractPDF::FOOTER_HEIGHT;
    $auditY = $pageHeight - $footerH - $stripH - 2.0;

    return [
        'content_x' => $contentX,
        'content_w' => $contentW,
        'audit_y' => $auditY,
        'audit_strip_height' => $stripH,
        'audit_max_y' => $auditY + $stripH - 1.0,
    ];
}

function pdf_signature_audit_box(array $signatureBox, float $pageHeight, string $layoutStyle): array
{
    $contentX = (float)($signatureBox['content_x'] ?? 12.0);
    $contentW = (float)($signatureBox['content_w'] ?? 186.0);
    $stripH = 14.0;
    $footerReserve = $layoutStyle === 'invoice' ? 45.0 : (float)ContractPDF::FOOTER_HEIGHT;

    if ($layoutStyle === 'invoice') {
        $yStart = (float)($signatureBox['y_start'] ?? 44.0);
        $headingH = 7.0;
        $boxHeight = (float)($signatureBox['box_height'] ?? 52.0);
        $auditY = $yStart + $headingH + $boxHeight + 3.0;
    } else {
        $auditY = (float)($signatureBox['audit_y'] ?? ($pageHeight - $footerReserve - $stripH - 2.0));
    }

    return [
        'content_x' => $contentX,
        'content_w' => $contentW,
        'audit_y' => $auditY,
        'audit_strip_height' => $stripH,
        'audit_max_y' => min($auditY + $stripH - 1.0, $pageHeight - $footerReserve - 2.0),
    ];
}

function pdf_draw_signature_audit_form(FPDF $pdf, array $box, array $auditRecords): void
{
    if ($auditRecords === []) {
        return;
    }

    $x = (float)($box['content_x'] ?? 12.0);
    $w = (float)($box['content_w'] ?? 186.0);
    $y = (float)($box['audit_y']);
    $maxY = (float)($box['audit_max_y']);

    $pdf->SetFillColor(240, 240, 240);
    $pdf->SetFont('Arial', 'B', 8);
    $pdf->SetTextColor(70, 70, 70);
    $pdf->SetXY($x, $y);
    $pdf->Cell($w, 4.5, pdf_text('Digitale Signatur-Bestätigung'), 0, 1, 'L', true);
    $lineY = $pdf->GetY() + 0.5;

    foreach ($auditRecords as $record) {
        if ($lineY > ($maxY - 3.5)) {
            break;
        }

        $line = pdf_signature_audit_line($record);
        if ($line === '') {
            continue;
        }

        $pdf->SetFont('Arial', '', 7.5);
        $pdf->SetTextColor(70, 70, 70);
        $pdf->SetXY($x, $lineY);
        $pdf->MultiCell($w, 3.5, pdf_text($line), 0, 'L');
        $lineY = $pdf->GetY() + 0.6;
    }
}

function pdf_signature_layout_box(string $layoutStyle, float $pageHeight): array
{
    if ($layoutStyle === 'invoice') {
        require_once __DIR__ . '/invoice_pdf_generator.php';

        $stored = invoice_pdf_get_signature_layout();
        if ($stored !== null) {
            return $stored;
        }

        return invoice_pdf_signature_layout();
    }

    return pdf_partner_signature_box($pageHeight);
}

function pdf_overlay_signatures_on_page(Fpdi $pdf, array $box, array $validPaths, string $layoutStyle): void
{
    if ($validPaths === []) {
        return;
    }

    if ($layoutStyle === 'invoice') {
        $path = $validPaths[count($validPaths) - 1];
        $padX = (float)$box['right_pad_x'];
        $innerW = (float)$box['right_inner_w'];
        $sigBoxY = (float)$box['sig_box_y'];
        $sigBoxH = (float)$box['sig_box_h'];
        $sigLineY = (float)$box['sig_line_y'];
        $maxWidth = $innerW - 2.0;
        $maxHeight = $sigBoxH - 2.0;
        $dimensions = pdf_signature_image_size($path, $maxWidth, $maxHeight);
        $signatureX = $padX + (($innerW - $dimensions['width']) / 2.0);
        $signatureY = $sigBoxY + (($sigBoxH - $dimensions['height']) / 2.0);
        $pdf->Image($path, $signatureX, $signatureY, $dimensions['width'], $dimensions['height']);

        $pdf->SetFont('Arial', '', 7.5);
        $pdf->SetTextColor(80, 80, 80);
        $pdf->SetXY($padX, $sigLineY - 4.0);
        $pdf->Cell($innerW, 3.5, pdf_text('Digital unterzeichnet'), 0, 0, 'C');

        return;
    }

    $slots = pdf_partner_signature_slots(count($validPaths), $box);
    $maxWidth = count($validPaths) >= 2 ? min(52.0, ((float)$box['right_inner_w'] / count($validPaths)) - 6.0) : min(54.0, (float)$box['right_inner_w']);
    $maxHeight = 14.0;
    $signatureLineY = (float)$box['y_start'] + (float)$box['box_height'] - 12.0;

    foreach ($validPaths as $index => $path) {
        if (!isset($slots[$index])) {
            break;
        }

        $dimensions = pdf_signature_image_size($path, $maxWidth, $maxHeight);
        $slot = $slots[$index];
        $signatureX = $slot['x'] - ($dimensions['width'] / 2.0);
        $signatureY = $signatureLineY - $dimensions['height'] - 0.5;
        $pdf->Image($path, $signatureX, $signatureY, $dimensions['width'], $dimensions['height']);
    }
}

function append_signatures_to_pdf(
    string $contractPdfPath,
    array $signatureImagePaths,
    string $outputPath,
    array $auditRecords = [],
    string $layoutStyle = 'partner'
): void
{
    $pdf = new Fpdi();
    $pdf->SetAutoPageBreak(false);
    $pageCount = $pdf->setSourceFile($contractPdfPath);

    for ($page = 1; $page <= $pageCount; $page++) {
        $tpl = $pdf->importPage($page);
        $size = $pdf->getTemplateSize($tpl);
        $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
        $pdf->useTemplate($tpl);

        if ($page !== $pageCount) {
            continue;
        }

        $pageHeight = (float)$size['height'];
        $validPaths = array_values(array_filter($signatureImagePaths, static fn(string $path): bool => is_file($path)));
        if ($validPaths === []) {
            if ($auditRecords !== []) {
                throw new RuntimeException('Signature images could not be found for the signed PDF.');
            }
            continue;
        }

        $box = pdf_signature_layout_box($layoutStyle, $pageHeight);
        pdf_overlay_signatures_on_page($pdf, $box, $validPaths, $layoutStyle);

        if ($auditRecords !== []) {
            pdf_draw_signature_audit_form(
                $pdf,
                pdf_signature_audit_box($box, $pageHeight, $layoutStyle),
                $auditRecords
            );
        }
    }

    $pdf->Output('F', $outputPath);
}

function pdf_partner_signature_slots(int $count, array $box): array
{
    $slots = [];
    $padX = $box['right_pad_x'];
    $innerW = $box['right_inner_w'];
    $centerY = $box['center_y'];

    if ($count <= 1) {
        $slots[] = ['x' => $padX + ($innerW / 2.0), 'y' => $centerY];
        return $slots;
    }

    if ($count === 2) {
        $slots[] = ['x' => $padX + ($innerW * 0.25), 'y' => $centerY];
        $slots[] = ['x' => $padX + ($innerW * 0.75), 'y' => $centerY];
        return $slots;
    }

    for ($index = 0; $index < $count; $index++) {
        $slots[] = [
            'x' => $padX + ($innerW * ($index + 0.5) / $count),
            'y' => $centerY,
        ];
    }

    return $slots;
}

function append_signature_to_pdf(string $contractPdfPath, string $signatureImagePath, string $outputPath, array $auditRecord = []): void
{
    append_signatures_to_pdf(
        $contractPdfPath,
        [$signatureImagePath],
        $outputPath,
        $auditRecord !== [] ? [$auditRecord] : []
    );
}
