<?php

namespace App\Services\Video;

use Illuminate\Support\Facades\Log;

class SlideAnnotationService
{
    private const TARGET_WIDTH = 1920;

    private const TARGET_HEIGHT = 1080;

    private const CAPTION_HEIGHT = 180;

    private const MARKER_RADIUS = 35;

    private string $fontPath;

    public function __construct()
    {
        // Docker에 설치된 나눔고딕 폰트
        $this->fontPath = '/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf';
        if (! file_exists($this->fontPath)) {
            $this->fontPath = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf';
        }
    }

    /**
     * 스크린샷 위에 시각적 어노테이션 추가
     *
     * @param  string  $imagePath  원본 스크린샷 경로
     * @param  array  $uiElements  UI 요소 배열 [{type, label, x, y, description}]
     * @param  int  $stepNumber  현재 스텝 번호
     * @param  string  $caption  하단 캡션 텍스트
     * @param  string  $outputPath  출력 파일 경로
     * @return string|null 성공 시 출력 경로
     */
    public function annotateSlide(
        string $imagePath,
        array $uiElements,
        int $stepNumber,
        string $caption,
        string $outputPath
    ): ?string {
        if (! file_exists($imagePath)) {
            Log::error("SlideAnnotation: 원본 이미지 없음 - {$imagePath}");

            return null;
        }

        try {
            $dir = dirname($outputPath);
            if (! is_dir($dir)) {
                mkdir($dir, 0755, true);
            }

            // 원본 이미지 로드
            $source = $this->loadImage($imagePath);
            if (! $source) {
                return null;
            }

            $srcW = imagesx($source);
            $srcH = imagesy($source);

            // 16:9 캔버스 생성
            $canvas = imagecreatetruecolor(self::TARGET_WIDTH, self::TARGET_HEIGHT);

            // 배경 검정
            $black = imagecolorallocate($canvas, 0, 0, 0);
            imagefill($canvas, 0, 0, $black);

            // 캡션 영역 제외한 영역에 이미지 리사이즈+센터링
            $availH = self::TARGET_HEIGHT - self::CAPTION_HEIGHT;
            $scale = min(self::TARGET_WIDTH / $srcW, $availH / $srcH);
            $newW = (int) ($srcW * $scale);
            $newH = (int) ($srcH * $scale);
            $offsetX = (int) ((self::TARGET_WIDTH - $newW) / 2);
            $offsetY = (int) (($availH - $newH) / 2);

            imagecopyresampled($canvas, $source, $offsetX, $offsetY, 0, 0, $newW, $newH, $srcW, $srcH);
            imagedestroy($source);

            // 반투명 오버레이 (약간 dim)
            $overlay = imagecolorallocatealpha($canvas, 0, 0, 0, 100); // 약간 어둡게
            imagefilledrectangle($canvas, $offsetX, $offsetY, $offsetX + $newW, $offsetY + $newH, $overlay);

            // UI 요소 마커 그리기
            $this->drawMarkers($canvas, $uiElements, $offsetX, $offsetY, $newW, $newH);

            // 하단 캡션 바
            $this->drawCaptionBar($canvas, $caption, $stepNumber);

            // 상단 스텝 인디케이터
            $this->drawStepBadge($canvas, $stepNumber);

            // PNG로 저장
            imagepng($canvas, $outputPath, 6);
            imagedestroy($canvas);

            Log::info('SlideAnnotation: 슬라이드 생성 완료', ['output' => $outputPath]);

            return $outputPath;
        } catch (\Exception $e) {
            Log::error('SlideAnnotation: 예외 발생', ['error' => $e->getMessage()]);

            return null;
        }
    }

    /**
     * UI 요소 위치에 빨간 번호 마커 그리기
     */
    private function drawMarkers(\GdImage $canvas, array $uiElements, int $offsetX, int $offsetY, int $imgW, int $imgH): void
    {
        $red = imagecolorallocate($canvas, 239, 68, 68);
        $white = imagecolorallocate($canvas, 255, 255, 255);
        $highlightBg = imagecolorallocatealpha($canvas, 239, 68, 68, 100);

        foreach ($uiElements as $i => $element) {
            $x = $element['x'] ?? 0.5;
            $y = $element['y'] ?? 0.5;

            // 비율 좌표 → 실제 픽셀 좌표
            $px = $offsetX + (int) ($x * $imgW);
            $py = $offsetY + (int) ($y * $imgH);

            // 하이라이트 영역 (요소 주변 밝게)
            $hlSize = 60;
            imagefilledrectangle(
                $canvas,
                max(0, $px - $hlSize),
                max(0, $py - $hlSize),
                min(self::TARGET_WIDTH, $px + $hlSize),
                min(self::TARGET_HEIGHT - self::CAPTION_HEIGHT, $py + $hlSize),
                $highlightBg
            );

            // 빨간 원형 배지
            $r = self::MARKER_RADIUS;
            imagefilledellipse($canvas, $px, $py, $r * 2, $r * 2, $red);

            // 흰색 테두리
            imageellipse($canvas, $px, $py, $r * 2, $r * 2, $white);

            // 번호 텍스트
            $num = (string) ($i + 1);
            $fontSize = 14;

            if (file_exists($this->fontPath)) {
                $bbox = imagettfbbox($fontSize, 0, $this->fontPath, $num);
                $tw = $bbox[2] - $bbox[0];
                $th = $bbox[1] - $bbox[7];
                imagettftext($canvas, $fontSize, 0, $px - (int) ($tw / 2), $py + (int) ($th / 2), $white, $this->fontPath, $num);
            } else {
                imagestring($canvas, 5, $px - 4, $py - 7, $num, $white);
            }
        }
    }

    /**
     * 하단 캡션 바 그리기
     */
    private function drawCaptionBar(\GdImage $canvas, string $caption, int $stepNumber): void
    {
        $barY = self::TARGET_HEIGHT - self::CAPTION_HEIGHT;

        // 반투명 검정 배경
        $barBg = imagecolorallocatealpha($canvas, 0, 0, 0, 40);
        imagefilledrectangle($canvas, 0, $barY, self::TARGET_WIDTH, self::TARGET_HEIGHT, $barBg);

        // 상단 구분선 (인디고)
        $accent = imagecolorallocate($canvas, 79, 70, 229);
        imagefilledrectangle($canvas, 0, $barY, self::TARGET_WIDTH, $barY + 4, $accent);

        // 캡션 텍스트
        $white = imagecolorallocate($canvas, 255, 255, 255);
        $fontSize = 38;

        if (file_exists($this->fontPath)) {
            $wrappedText = $this->wrapText($caption, 40);
            $lines = explode("\n", $wrappedText);

            $lineHeight = 52;
            $startY = $barY + 30 + $lineHeight;

            if (count($lines) > 1) {
                $fontSize = 32;
                $lineHeight = 46;
                $startY = $barY + 22 + $lineHeight;
            }

            foreach ($lines as $li => $line) {
                $bbox = imagettfbbox($fontSize, 0, $this->fontPath, $line);
                $tw = $bbox[2] - $bbox[0];
                $tx = (int) ((self::TARGET_WIDTH - $tw) / 2);
                imagettftext($canvas, $fontSize, 0, $tx, $startY + ($li * $lineHeight), $white, $this->fontPath, $line);
            }
        } else {
            imagestring($canvas, 5, 40, $barY + 80, $caption, $white);
        }
    }

    /**
     * 좌상단 스텝 배지 그리기
     */
    private function drawStepBadge(\GdImage $canvas, int $stepNumber): void
    {
        $badgeBg = imagecolorallocate($canvas, 79, 70, 229);
        $white = imagecolorallocate($canvas, 255, 255, 255);

        // 둥근 사각형 배지 (좌상단)
        $bx = 30;
        $by = 20;
        $bw = 140;
        $bh = 44;

        imagefilledrectangle($canvas, $bx, $by, $bx + $bw, $by + $bh, $badgeBg);

        $text = "STEP {$stepNumber}";

        if (file_exists($this->fontPath)) {
            $fontSize = 18;
            $bbox = imagettfbbox($fontSize, 0, $this->fontPath, $text);
            $tw = $bbox[2] - $bbox[0];
            $tx = $bx + (int) (($bw - $tw) / 2);
            imagettftext($canvas, $fontSize, 0, $tx, $by + 32, $white, $this->fontPath, $text);
        } else {
            imagestring($canvas, 5, $bx + 20, $by + 12, $text, $white);
        }
    }

    /**
     * 스포트라이트 효과가 적용된 슬라이드 생성
     *
     * 원본 이미지를 dim 처리한 후, focused_element 영역만 밝게 복원
     */
    public function annotateSlideWithSpotlight(
        string $imagePath,
        ?array $focusedElement,
        int $stepNumber,
        int $totalSteps,
        string $caption,
        string $outputPath
    ): ?string {
        if (! file_exists($imagePath)) {
            Log::error("SlideAnnotation: 원본 이미지 없음 - {$imagePath}");

            return null;
        }

        try {
            $dir = dirname($outputPath);
            if (! is_dir($dir)) {
                mkdir($dir, 0755, true);
            }

            $source = $this->loadImage($imagePath);
            if (! $source) {
                return null;
            }

            $srcW = imagesx($source);
            $srcH = imagesy($source);

            // 16:9 캔버스 생성
            $canvas = imagecreatetruecolor(self::TARGET_WIDTH, self::TARGET_HEIGHT);
            $black = imagecolorallocate($canvas, 0, 0, 0);
            imagefill($canvas, 0, 0, $black);

            // 캡션 영역 제외한 영역에 이미지 배치
            $availH = self::TARGET_HEIGHT - self::CAPTION_HEIGHT;
            $scale = min(self::TARGET_WIDTH / $srcW, $availH / $srcH);
            $newW = (int) ($srcW * $scale);
            $newH = (int) ($srcH * $scale);
            $offsetX = (int) ((self::TARGET_WIDTH - $newW) / 2);
            $offsetY = (int) (($availH - $newH) / 2);

            // 원본 이미지 배치
            imagecopyresampled($canvas, $source, $offsetX, $offsetY, 0, 0, $newW, $newH, $srcW, $srcH);

            // 강한 dim 오버레이 (alpha 60 → 상당히 어둡게)
            $dimOverlay = imagecolorallocatealpha($canvas, 0, 0, 0, 60);
            imagefilledrectangle($canvas, $offsetX, $offsetY, $offsetX + $newW, $offsetY + $newH, $dimOverlay);

            // 스포트라이트: focused_element 영역만 원본 밝기로 복원
            if ($focusedElement) {
                $fx = $focusedElement['x'] ?? 0.5;
                $fy = $focusedElement['y'] ?? 0.5;
                $fw = $focusedElement['w'] ?? 0.2;
                $fh = $focusedElement['h'] ?? 0.15;

                // 비율 → 원본 이미지 좌표
                $srcFx = (int) ($fx * $srcW);
                $srcFy = (int) ($fy * $srcH);
                $srcFw = (int) ($fw * $srcW);
                $srcFh = (int) ($fh * $srcH);

                // 비율 → 캔버스 좌표
                $canvasFx = $offsetX + (int) ($fx * $newW);
                $canvasFy = $offsetY + (int) ($fy * $newH);
                $canvasFw = (int) ($fw * $newW);
                $canvasFh = (int) ($fh * $newH);

                // 경계 보정
                $canvasFw = min($canvasFw, self::TARGET_WIDTH - $canvasFx);
                $canvasFh = min($canvasFh, $availH - $canvasFy + $offsetY);

                // 원본 이미지에서 해당 영역을 다시 복사 (dim 위에 덮어씌움)
                if ($canvasFw > 0 && $canvasFh > 0) {
                    imagecopyresampled(
                        $canvas, $source,
                        $canvasFx, $canvasFy,
                        $srcFx, $srcFy,
                        $canvasFw, $canvasFh,
                        $srcFw, $srcFh
                    );

                    // 이중 테두리 (안쪽 흰색 + 바깥 빨간색)
                    $red = imagecolorallocate($canvas, 239, 68, 68);
                    $borderWhite = imagecolorallocate($canvas, 255, 255, 255);
                    $outerW = 4;
                    $innerW = 2;

                    // 바깥 빨간 테두리
                    for ($b = 0; $b < $outerW; $b++) {
                        imagerectangle(
                            $canvas,
                            $canvasFx - $innerW - $b, $canvasFy - $innerW - $b,
                            $canvasFx + $canvasFw + $innerW + $b, $canvasFy + $canvasFh + $innerW + $b,
                            $red
                        );
                    }
                    // 안쪽 흰색 테두리
                    for ($b = 0; $b < $innerW; $b++) {
                        imagerectangle(
                            $canvas,
                            $canvasFx - $b, $canvasFy - $b,
                            $canvasFx + $canvasFw + $b, $canvasFy + $canvasFh + $b,
                            $borderWhite
                        );
                    }

                    // 번호 마커 (테두리 바깥 좌상단)
                    $markerOffset = self::MARKER_RADIUS + $outerW + 4;
                    $this->drawNumberMarker($canvas, $stepNumber, $canvasFx - $markerOffset, $canvasFy - $markerOffset);
                }
            }

            imagedestroy($source);

            // 하단 캡션 바
            $this->drawCaptionBar($canvas, $caption, $stepNumber);

            // 상단 STEP 배지 (진행 표시)
            $this->drawStepProgressBadge($canvas, $stepNumber, $totalSteps);

            // PNG로 저장
            imagepng($canvas, $outputPath, 6);
            imagedestroy($canvas);

            Log::info('SlideAnnotation: 스포트라이트 슬라이드 생성 완료', ['output' => $outputPath]);

            return $outputPath;
        } catch (\Exception $e) {
            Log::error('SlideAnnotation: 스포트라이트 예외 발생', ['error' => $e->getMessage()]);

            return null;
        }
    }

    /**
     * 인트로 슬라이드 생성
     */
    public function createIntroSlide(string $title, string $outputPath): ?string
    {
        try {
            $dir = dirname($outputPath);
            if (! is_dir($dir)) {
                mkdir($dir, 0755, true);
            }

            $canvas = imagecreatetruecolor(self::TARGET_WIDTH, self::TARGET_HEIGHT);

            // 인디고 그라데이션 배경
            $topColor = [49, 46, 129]; // indigo-900
            $bottomColor = [79, 70, 229]; // indigo-600
            for ($y = 0; $y < self::TARGET_HEIGHT; $y++) {
                $ratio = $y / self::TARGET_HEIGHT;
                $r = (int) ($topColor[0] + ($bottomColor[0] - $topColor[0]) * $ratio);
                $g = (int) ($topColor[1] + ($bottomColor[1] - $topColor[1]) * $ratio);
                $b = (int) ($topColor[2] + ($bottomColor[2] - $topColor[2]) * $ratio);
                $color = imagecolorallocate($canvas, $r, $g, $b);
                imageline($canvas, 0, $y, self::TARGET_WIDTH, $y, $color);
            }

            $white = imagecolorallocate($canvas, 255, 255, 255);
            $lightWhite = imagecolorallocate($canvas, 200, 200, 220);

            if (file_exists($this->fontPath)) {
                // 타이틀
                $titleSize = 72;
                $bbox = imagettfbbox($titleSize, 0, $this->fontPath, $title);
                $tw = $bbox[2] - $bbox[0];
                $tx = (int) ((self::TARGET_WIDTH - $tw) / 2);
                imagettftext($canvas, $titleSize, 0, $tx, 440, $white, $this->fontPath, $title);

                // 서브타이틀
                $sub = 'SAM 사용법을 안내합니다.';
                $subSize = 40;
                $bbox2 = imagettfbbox($subSize, 0, $this->fontPath, $sub);
                $tw2 = $bbox2[2] - $bbox2[0];
                $tx2 = (int) ((self::TARGET_WIDTH - $tw2) / 2);
                imagettftext($canvas, $subSize, 0, $tx2, 550, $lightWhite, $this->fontPath, $sub);

                // 구분선
                $lineColor = imagecolorallocatealpha($canvas, 255, 255, 255, 80);
                imagefilledrectangle($canvas, (self::TARGET_WIDTH / 2) - 80, 590, (self::TARGET_WIDTH / 2) + 80, 594, $lineColor);
            } else {
                imagestring($canvas, 5, self::TARGET_WIDTH / 2 - 100, 450, $title, $white);
            }

            imagepng($canvas, $outputPath, 6);
            imagedestroy($canvas);

            return $outputPath;
        } catch (\Exception $e) {
            Log::error('SlideAnnotation: 인트로 슬라이드 생성 실패', ['error' => $e->getMessage()]);

            return null;
        }
    }

    /**
     * 아웃트로 슬라이드 생성
     */
    public function createOutroSlide(string $title, string $outputPath): ?string
    {
        try {
            $dir = dirname($outputPath);
            if (! is_dir($dir)) {
                mkdir($dir, 0755, true);
            }

            $canvas = imagecreatetruecolor(self::TARGET_WIDTH, self::TARGET_HEIGHT);

            // 진한 그라데이션 배경
            $topColor = [30, 27, 75]; // indigo-950
            $bottomColor = [49, 46, 129]; // indigo-900
            for ($y = 0; $y < self::TARGET_HEIGHT; $y++) {
                $ratio = $y / self::TARGET_HEIGHT;
                $r = (int) ($topColor[0] + ($bottomColor[0] - $topColor[0]) * $ratio);
                $g = (int) ($topColor[1] + ($bottomColor[1] - $topColor[1]) * $ratio);
                $b = (int) ($topColor[2] + ($bottomColor[2] - $topColor[2]) * $ratio);
                $color = imagecolorallocate($canvas, $r, $g, $b);
                imageline($canvas, 0, $y, self::TARGET_WIDTH, $y, $color);
            }

            $white = imagecolorallocate($canvas, 255, 255, 255);
            $lightWhite = imagecolorallocate($canvas, 180, 180, 200);

            if (file_exists($this->fontPath)) {
                $mainText = '이상으로 안내를 마칩니다.';
                $mainSize = 72;
                $bbox = imagettfbbox($mainSize, 0, $this->fontPath, $mainText);
                $tw = $bbox[2] - $bbox[0];
                $tx = (int) ((self::TARGET_WIDTH - $tw) / 2);
                imagettftext($canvas, $mainSize, 0, $tx, 440, $white, $this->fontPath, $mainText);

                $subText = '감사합니다.';
                $subSize = 48;
                $bbox2 = imagettfbbox($subSize, 0, $this->fontPath, $subText);
                $tw2 = $bbox2[2] - $bbox2[0];
                $tx2 = (int) ((self::TARGET_WIDTH - $tw2) / 2);
                imagettftext($canvas, $subSize, 0, $tx2, 560, $lightWhite, $this->fontPath, $subText);
            } else {
                imagestring($canvas, 5, self::TARGET_WIDTH / 2 - 100, 470, 'Thank you', $white);
            }

            imagepng($canvas, $outputPath, 6);
            imagedestroy($canvas);

            return $outputPath;
        } catch (\Exception $e) {
            Log::error('SlideAnnotation: 아웃트로 슬라이드 생성 실패', ['error' => $e->getMessage()]);

            return null;
        }
    }

    /**
     * 번호 마커 그리기 (스포트라이트용)
     */
    private function drawNumberMarker(\GdImage $canvas, int $number, int $cx, int $cy): void
    {
        $red = imagecolorallocate($canvas, 239, 68, 68);
        $white = imagecolorallocate($canvas, 255, 255, 255);
        $r = self::MARKER_RADIUS;

        imagefilledellipse($canvas, $cx, $cy, $r * 2, $r * 2, $red);
        imageellipse($canvas, $cx, $cy, $r * 2, $r * 2, $white);

        $num = (string) $number;
        $fontSize = 26;

        if (file_exists($this->fontPath)) {
            $bbox = imagettfbbox($fontSize, 0, $this->fontPath, $num);
            $tw = $bbox[2] - $bbox[0];
            $th = $bbox[1] - $bbox[7];
            imagettftext($canvas, $fontSize, 0, $cx - (int) ($tw / 2), $cy + (int) ($th / 2), $white, $this->fontPath, $num);
        } else {
            imagestring($canvas, 5, $cx - 4, $cy - 7, $num, $white);
        }
    }

    /**
     * 상단 STEP 진행 배지 (STEP 1/5 형식)
     */
    private function drawStepProgressBadge(\GdImage $canvas, int $stepNumber, int $totalSteps): void
    {
        $badgeBg = imagecolorallocate($canvas, 79, 70, 229);
        $white = imagecolorallocate($canvas, 255, 255, 255);

        $bx = 20;
        $by = 14;
        $bw = 150;
        $bh = 38;

        imagefilledrectangle($canvas, $bx, $by, $bx + $bw, $by + $bh, $badgeBg);

        $text = "STEP {$stepNumber}/{$totalSteps}";

        if (file_exists($this->fontPath)) {
            $fontSize = 17;
            $bbox = imagettfbbox($fontSize, 0, $this->fontPath, $text);
            $tw = $bbox[2] - $bbox[0];
            $tx = $bx + (int) (($bw - $tw) / 2);
            imagettftext($canvas, $fontSize, 0, $tx, $by + 27, $white, $this->fontPath, $text);
        } else {
            imagestring($canvas, 5, $bx + 20, $by + 20, $text, $white);
        }
    }

    /**
     * 이미지 로드 (형식 자동 감지)
     */
    private function loadImage(string $path): ?\GdImage
    {
        $info = getimagesize($path);
        if (! $info) {
            Log::error("SlideAnnotation: 이미지 정보를 읽을 수 없음 - {$path}");

            return null;
        }

        return match ($info[2]) {
            IMAGETYPE_PNG => imagecreatefrompng($path),
            IMAGETYPE_JPEG => imagecreatefromjpeg($path),
            IMAGETYPE_GIF => imagecreatefromgif($path),
            IMAGETYPE_WEBP => imagecreatefromwebp($path),
            default => null,
        };
    }

    /**
     * 텍스트 줄바꿈
     */
    private function wrapText(string $text, int $maxChars): string
    {
        if (mb_strlen($text) <= $maxChars) {
            return $text;
        }

        $words = preg_split('/(?<=\s)|(?<=\.)|(?<=,)/u', $text);
        $lines = [];
        $currentLine = '';

        foreach ($words as $word) {
            if (mb_strlen($currentLine.$word) > $maxChars && $currentLine !== '') {
                $lines[] = trim($currentLine);
                $currentLine = $word;
            } else {
                $currentLine .= $word;
            }
        }

        if ($currentLine !== '') {
            $lines[] = trim($currentLine);
        }

        return implode("\n", array_slice($lines, 0, 3));
    }
}
