<?php

namespace App\Services;

use App\Models\ContractTemplate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\ValidationException;
use setasign\Fpdi\Fpdi;

class PdfContractTemplateService
{
    public const ALIGNS = [
        'L' => 'Left',
        'C' => 'Center',
        'R' => 'Right',
    ];

    /** @return array{extracted_text:string,blocks:array<int,array<string,mixed>>,mappings:array<int,array<string,mixed>>} */
    public function inspectPath(string $absolutePath): array
    {
        $this->assertLibrariesAvailable();
        abort_unless(is_file($absolutePath), 404, 'The uploaded PDF template file is missing.');

        try {
            $pdf = new Fpdi('P', 'mm');
            $pageCount = $pdf->setSourceFile($absolutePath);
            $pages = [];
            for ($page = 1; $page <= $pageCount; $page++) {
                $templateId = $pdf->importPage($page);
                $size = $pdf->getTemplateSize($templateId);
                $pages[] = [
                    'id' => 'pdf:'.$page,
                    'page' => $page,
                    'width' => round((float) $size['width'], 2),
                    'height' => round((float) $size['height'], 2),
                ];
            }
        } catch (\Throwable $e) {
            throw ValidationException::withMessages([
                'template_file' => 'The PDF could not be read as a contract template. Password-protected or damaged PDFs are not supported. '.$e->getMessage(),
            ]);
        }

        return [
            'extracted_text' => 'PDF visual template · '.$pageCount.' page'.($pageCount === 1 ? '' : 's').'. Add system fields manually and position them using page coordinates.',
            'blocks' => $pages,
            'mappings' => [],
        ];
    }

    public function generate(ContractTemplate $template, array $values, array $overrides = [], array $formatting = []): string
    {
        $this->assertLibrariesAvailable();
        $source = Storage::disk('local')->path($template->file_path);
        abort_unless(is_file($source), 404, 'The source PDF contract template file is missing.');

        try {
            $pdf = new Fpdi('P', 'mm');
            $pdf->SetAutoPageBreak(false);
            $pdf->SetMargins(0, 0, 0);
            $pdf->SetCompression(false);
            $pageCount = $pdf->setSourceFile($source);
            $mappings = array_values($template->mappings ?? []);

            for ($page = 1; $page <= $pageCount; $page++) {
                $templateId = $pdf->importPage($page);
                $size = $pdf->getTemplateSize($templateId);
                $width = (float) $size['width'];
                $height = (float) $size['height'];
                $orientation = $width > $height ? 'L' : 'P';
                $pdf->AddPage($orientation, [$width, $height]);
                $pdf->useTemplate($templateId, 0, 0, $width, $height, true);

                foreach ($mappings as $index => $mapping) {
                    if ((int) ($mapping['page'] ?? 1) !== $page) continue;
                    $field = (string) ($mapping['field'] ?? '');
                    if ($field === '' || ! array_key_exists($field, $values)) continue;
                    $value = array_key_exists($index, $overrides) ? (string) $overrides[$index] : (string) ($values[$field] ?? '');
                    $value = trim($value);
                    if ($value === '') continue;

                    $format = $this->formatFor($mapping, $formatting[$index] ?? [], $width, $height);
                    $pdf->SetFont('Helvetica', '', $format['font_size']);
                    $pdf->SetTextColor(0, 0, 0);
                    $pdf->SetXY($format['x'], $format['y']);
                    $pdf->MultiCell($format['width'], $format['line_height'], $this->pdfText($value), 0, $format['align'], false);
                }
            }

            return $pdf->Output('S');
        } catch (ValidationException $e) {
            throw $e;
        } catch (\Throwable $e) {
            throw ValidationException::withMessages([
                'contract' => 'The final PDF contract could not be generated from this template. '.$e->getMessage(),
            ]);
        }
    }

    /** @return array{page:int,x:float,y:float,width:float,font_size:float,line_height:float,align:string} */
    public function formatFor(array $mapping, array $override = [], float $pageWidth = 210, float $pageHeight = 297): array
    {
        $page = max(1, (int) ($override['page'] ?? $mapping['page'] ?? 1));
        $x = (float) ($override['x'] ?? $mapping['x'] ?? 20);
        $y = (float) ($override['y'] ?? $mapping['y'] ?? 20);
        $width = (float) ($override['width'] ?? $mapping['width'] ?? 80);
        $fontSize = (float) ($override['font_size'] ?? $mapping['font_size'] ?? 10);
        $lineHeight = (float) ($override['line_height'] ?? $mapping['line_height'] ?? 5);
        $align = strtoupper((string) ($override['align'] ?? $mapping['align'] ?? 'L'));
        if (! array_key_exists($align, self::ALIGNS)) $align = 'L';

        $x = max(0, min(max(0, $pageWidth - 1), $x));
        $y = max(0, min(max(0, $pageHeight - 1), $y));
        $width = max(5, min(max(5, $pageWidth - $x), $width));
        $fontSize = max(6, min(30, $fontSize));
        $lineHeight = max(3, min(20, $lineHeight));

        return [
            'page' => $page,
            'x' => $x,
            'y' => $y,
            'width' => $width,
            'font_size' => $fontSize,
            'line_height' => $lineHeight,
            'align' => $align,
        ];
    }

    private function pdfText(string $text): string
    {
        $converted = iconv('UTF-8', 'Windows-1252//TRANSLIT//IGNORE', $text);
        return is_string($converted) && $converted !== '' ? $converted : $text;
    }

    private function assertLibrariesAvailable(): void
    {
        if (! class_exists(Fpdi::class) || ! class_exists('FPDF')) {
            throw ValidationException::withMessages([
                'template_file' => 'PDF contract templates require setasign/fpdi and setasign/fpdf to be installed.',
            ]);
        }
    }
}
