<?php

namespace App\Services\ESign;

use App\Models\ESign\EsignContract;
use App\Models\ESign\EsignSignField;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use setasign\Fpdi\Tcpdf\Fpdi;

class PdfSignatureService
{
    private ?string $koreanFontName = null;

    /**
     * Pretendard 한글 폰트를 등록하고 폰트 이름을 반환한다.
     * PDF 문서와 동일한 폰트로 필드 텍스트를 렌더링하여 일관성을 유지한다.
     */
    private function getKoreanFont(): string
    {
        if ($this->koreanFontName) {
            return $this->koreanFontName;
        }

        // 1순위: 프로젝트 내장 Pretendard (로컬/서버 공통)
        $fontPath = storage_path('fonts/Pretendard-Regular.ttf');

        if (file_exists($fontPath)) {
            $this->koreanFontName = \TCPDF_FONTS::addTTFfont($fontPath, 'TrueTypeUnicode', '', 96);
        }

        return $this->koreanFontName ?: 'helvetica';
    }

    /**
     * 모든 서명 필드를 원본 PDF 위에 합성하여 서명 완료 PDF를 생성한다.
     */
    public function mergeSignatures(EsignContract $contract): string
    {
        $disk = Storage::disk('local');
        $originalPath = $disk->path($contract->original_file_path);

        if (! file_exists($originalPath)) {
            throw new \RuntimeException("원본 PDF 파일이 존재하지 않습니다: {$contract->original_file_path}");
        }

        // FPDI(TCPDF 확장)로 원본 PDF 임포트
        $pdf = new Fpdi;
        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(false);

        $pageCount = $pdf->setSourceFile($originalPath);

        // 서명 필드를 페이지별로 그룹핑
        $signFields = EsignSignField::withoutGlobalScopes()
            ->where('contract_id', $contract->id)
            ->with('signer')
            ->orderBy('page_number')
            ->orderBy('sort_order')
            ->get()
            ->groupBy('page_number');

        // 각 페이지를 임포트하고 서명 필드 오버레이
        for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
            $templateId = $pdf->importPage($pageNo);
            $size = $pdf->getTemplateSize($templateId);

            $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
            $pdf->useTemplate($templateId, 0, 0, $size['width'], $size['height']);

            // 이 페이지에 배치된 서명 필드 오버레이
            if ($signFields->has($pageNo)) {
                foreach ($signFields[$pageNo] as $field) {
                    $this->overlayField($pdf, $field, $size['width'], $size['height']);
                }
            }
        }

        // 결과 PDF 저장
        $signedDir = "esign/{$contract->tenant_id}/signed";
        $signedRelPath = "{$signedDir}/{$contract->id}_signed.pdf";
        $signedAbsPath = $disk->path($signedRelPath);

        // 디렉토리 생성
        if (! is_dir(dirname($signedAbsPath))) {
            mkdir(dirname($signedAbsPath), 0755, true);
        }

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

        // DB 업데이트
        $contract->update([
            'signed_file_path' => $signedRelPath,
            'signed_file_hash' => hash_file('sha256', $signedAbsPath),
        ]);

        Log::info('PDF 서명 합성 완료', [
            'contract_id' => $contract->id,
            'signed_file_path' => $signedRelPath,
        ]);

        return $signedRelPath;
    }

    /**
     * 서명/도장을 제외한 필드(텍스트, 날짜, 체크박스)만 합성한 미리보기 PDF를 생성한다.
     * 서명 전 문서 확인 시 사용된다.
     */
    public function generatePreview(EsignContract $contract): string
    {
        $disk = Storage::disk('local');
        $originalPath = $disk->path($contract->original_file_path);

        if (! file_exists($originalPath)) {
            throw new \RuntimeException("원본 PDF 파일이 존재하지 않습니다: {$contract->original_file_path}");
        }

        $pdf = new Fpdi;
        $pdf->setPrintHeader(false);
        $pdf->setPrintFooter(false);

        $pageCount = $pdf->setSourceFile($originalPath);

        // 서명/도장을 제외한 필드만 조회
        $signFields = EsignSignField::withoutGlobalScopes()
            ->where('contract_id', $contract->id)
            ->whereNotIn('field_type', ['signature', 'stamp'])
            ->with('signer')
            ->orderBy('page_number')
            ->orderBy('sort_order')
            ->get()
            ->groupBy('page_number');

        for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
            $templateId = $pdf->importPage($pageNo);
            $size = $pdf->getTemplateSize($templateId);

            $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
            $pdf->useTemplate($templateId, 0, 0, $size['width'], $size['height']);

            if ($signFields->has($pageNo)) {
                foreach ($signFields[$pageNo] as $field) {
                    $this->overlayField($pdf, $field, $size['width'], $size['height']);
                }
            }
        }

        // 미리보기 PDF 저장
        $previewDir = "esign/{$contract->tenant_id}/preview";
        $previewRelPath = "{$previewDir}/{$contract->id}_preview.pdf";
        $previewAbsPath = $disk->path($previewRelPath);

        if (! is_dir(dirname($previewAbsPath))) {
            mkdir(dirname($previewAbsPath), 0755, true);
        }

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

        return $previewRelPath;
    }

    /**
     * 개별 필드를 PDF 위에 오버레이한다.
     */
    private function overlayField(Fpdi $pdf, EsignSignField $field, float $pageWidth, float $pageHeight): void
    {
        // % 좌표 → PDF pt 좌표 변환
        $x = ($field->position_x / 100) * $pageWidth;
        $y = ($field->position_y / 100) * $pageHeight;
        $w = ($field->width / 100) * $pageWidth;
        $h = ($field->height / 100) * $pageHeight;

        switch ($field->field_type) {
            case 'signature':
            case 'stamp':
                $this->overlayImage($pdf, $field, $x, $y, $w, $h);
                break;

            case 'date':
                $this->overlayDate($pdf, $field, $x, $y, $w, $h);
                break;

            case 'text':
                $this->overlayText($pdf, $field, $x, $y, $w, $h);
                break;

            case 'checkbox':
                $this->overlayCheckbox($pdf, $field, $x, $y, $w, $h);
                break;
        }
    }

    /**
     * 서명/도장 이미지를 PDF에 배치한다.
     */
    private function overlayImage(Fpdi $pdf, EsignSignField $field, float $x, float $y, float $w, float $h): void
    {
        $signer = $field->signer;
        if (! $signer || ! $signer->signature_image_path) {
            return;
        }

        $imagePath = Storage::disk('local')->path($signer->signature_image_path);
        if (! file_exists($imagePath)) {
            Log::warning('서명 이미지 파일 없음', [
                'signer_id' => $signer->id,
                'path' => $signer->signature_image_path,
            ]);

            return;
        }

        $pdf->Image($imagePath, $x, $y, $w, $h, 'PNG');
    }

    /**
     * 날짜 텍스트를 PDF에 렌더링한다.
     */
    private function overlayDate(Fpdi $pdf, EsignSignField $field, float $x, float $y, float $w, float $h): void
    {
        $signer = $field->signer;
        $dateText = $field->field_value;

        if (! $dateText && $signer && $signer->signed_at) {
            $dateText = $signer->signed_at->format('Y년 n월 j일');
        }

        if (! $dateText) {
            $dateText = now()->format('Y년 n월 j일');
        }

        // 날짜 필드는 폰트 크기를 일관되게 유지 (미지정 시 12pt 고정)
        $fontSize = $field->font_size ?: 12;
        $this->renderText($pdf, $dateText, $x, $y, $w, $h, $fontSize, $field->text_align ?? 'L');
    }

    /**
     * 텍스트 필드를 PDF에 렌더링한다.
     */
    private function overlayText(Fpdi $pdf, EsignSignField $field, float $x, float $y, float $w, float $h): void
    {
        $text = $field->field_value ?? '';
        if ($text === '') {
            return;
        }

        $this->renderText($pdf, $text, $x, $y, $w, $h, $field->font_size, $field->text_align ?? 'L');
    }

    /**
     * 체크박스를 PDF에 렌더링한다.
     */
    private function overlayCheckbox(Fpdi $pdf, EsignSignField $field, float $x, float $y, float $w, float $h): void
    {
        if (! $field->field_value) {
            return;
        }

        $this->renderText($pdf, "\xe2\x9c\x93", $x, $y, $w, $h, $field->font_size, 'C'); // ✓ (UTF-8)
    }

    /**
     * 텍스트를 지정 영역에 렌더링하는 공통 메서드.
     */
    private function renderText(Fpdi $pdf, string $text, float $x, float $y, float $w, float $h, ?int $fieldFontSize = null, string $textAlign = 'L'): void
    {
        // 필드에 지정된 폰트 크기 우선, 없으면 영역 높이 기반 자동 산출 (2배 확대)
        if ($fieldFontSize && $fieldFontSize >= 4) {
            $fontSize = $fieldFontSize;
        } else {
            $fontSize = min($h * 1.4, 24);
            if ($fontSize < 6) {
                $fontSize = 6;
            }
        }

        $pdf->SetFont($this->getKoreanFont(), '', $fontSize);
        $pdf->SetTextColor(0, 0, 0);

        // 텍스트를 지정된 정렬 방식으로 배치 (L=왼쪽, C=가운데, R=오른쪽)
        $align = in_array($textAlign, ['L', 'C', 'R']) ? $textAlign : 'L';
        $pdf->SetXY($x, $y);
        $pdf->Cell($w, $h, $text, 0, 0, $align, false, '', 0, false, 'T', 'M');
    }
}
