<?php

namespace App\Services;

use App\Models\Boards\File;
use App\Models\Equipment\Equipment;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class EquipmentPhotoService
{
    private const MAX_PHOTOS = 10;

    private const MAX_FILE_SIZE = 1048576; // 1MB

    private const MAX_DIMENSION = 2048;

    private const INITIAL_QUALITY = 85;

    private const MIN_QUALITY = 40;

    private const QUALITY_STEP = 5;

    public function __construct(
        private readonly GoogleCloudService $googleCloudService,
        private readonly GoogleCloudStorageService $googleCloudStorageService,
    ) {}

    /**
     * 설비 사진 업로드 (멀티)
     *
     * @param  array<UploadedFile>  $files
     * @return array ['uploaded' => int, 'errors' => array]
     */
    public function uploadPhotos(Equipment $equipment, array $files): array
    {
        $tenantId = session('selected_tenant_id');
        $currentCount = $equipment->photos()->count();
        $uploaded = 0;
        $errors = [];

        foreach ($files as $index => $file) {
            if ($currentCount + $uploaded >= self::MAX_PHOTOS) {
                $errors[] = '최대 '.self::MAX_PHOTOS.'장까지 업로드 가능합니다.';
                break;
            }

            $extension = strtolower($file->getClientOriginalExtension());
            $compressedPath = null;

            try {
                [$compressedPath, $finalExtension] = $this->compressImage($file->getRealPath(), $extension);

                $timestamp = now()->format('Ymd_His');
                $objectName = "equipment-photos/{$tenantId}/{$equipment->id}/{$timestamp}_{$index}.{$finalExtension}";

                $result = $this->googleCloudService->uploadToStorage($compressedPath, $objectName);

                if (! $result) {
                    $errors[] = "파일 '{$file->getClientOriginalName()}' 업로드 실패";
                    Log::error('EquipmentPhoto: GCS 업로드 실패', [
                        'equipment_id' => $equipment->id,
                        'file' => $file->getClientOriginalName(),
                    ]);

                    continue;
                }

                $mimeType = $finalExtension === 'png' ? 'image/png' : 'image/jpeg';

                $storedName = basename($objectName);

                File::create([
                    'tenant_id' => $tenantId,
                    'document_id' => $equipment->id,
                    'document_type' => 'equipment',
                    'file_path' => $objectName,
                    'stored_name' => $storedName,
                    'original_name' => $file->getClientOriginalName(),
                    'display_name' => $file->getClientOriginalName(),
                    'file_size' => $result['size'],
                    'mime_type' => $mimeType,
                    'file_type' => 'image',
                    'gcs_object_name' => $objectName,
                    'gcs_uri' => $result['uri'],
                    'uploaded_by' => Auth::id(),
                    'is_temp' => false,
                ]);

                $uploaded++;
            } catch (\Throwable $e) {
                $errors[] = "파일 '{$file->getClientOriginalName()}' 압축 실패: {$e->getMessage()}";
                Log::error('EquipmentPhoto: 이미지 압축 실패', [
                    'equipment_id' => $equipment->id,
                    'file' => $file->getClientOriginalName(),
                    'error' => $e->getMessage(),
                ]);
            } finally {
                if ($compressedPath && file_exists($compressedPath) && $compressedPath !== $file->getRealPath()) {
                    @unlink($compressedPath);
                }
            }
        }

        return ['uploaded' => $uploaded, 'errors' => $errors];
    }

    /**
     * 로컬 파일 경로에서 설비 사진 업로드 (엑셀 Import용)
     */
    public function uploadPhotoFromPath(
        Equipment $equipment,
        string $localPath,
        string $originalName,
        string $extension
    ): bool {
        $tenantId = session('selected_tenant_id');
        $currentCount = $equipment->photos()->count();

        if ($currentCount >= self::MAX_PHOTOS) {
            return false;
        }

        $compressedPath = null;

        try {
            [$compressedPath, $finalExtension] = $this->compressImage($localPath, $extension);

            $timestamp = now()->format('Ymd_His');
            $uniqueId = substr(md5(uniqid()), 0, 8);
            $objectName = "equipment-photos/{$tenantId}/{$equipment->id}/{$timestamp}_{$uniqueId}.{$finalExtension}";

            $result = $this->googleCloudService->uploadToStorage($compressedPath, $objectName);

            if (! $result) {
                Log::error('EquipmentPhoto: GCS 업로드 실패 (Import)', [
                    'equipment_id' => $equipment->id,
                    'file' => $originalName,
                ]);

                return false;
            }

            $mimeType = $finalExtension === 'png' ? 'image/png' : 'image/jpeg';

            $storedName = basename($objectName);

            File::create([
                'tenant_id' => $tenantId,
                'document_id' => $equipment->id,
                'document_type' => 'equipment',
                'file_path' => $objectName,
                'stored_name' => $storedName,
                'original_name' => $originalName,
                'display_name' => $originalName,
                'file_size' => $result['size'],
                'mime_type' => $mimeType,
                'file_type' => 'image',
                'gcs_object_name' => $objectName,
                'gcs_uri' => $result['uri'],
                'uploaded_by' => Auth::id(),
                'is_temp' => false,
            ]);

            return true;
        } catch (\Throwable $e) {
            Log::error('EquipmentPhoto: Import 사진 처리 실패', [
                'equipment_id' => $equipment->id,
                'file' => $originalName,
                'error' => $e->getMessage(),
            ]);

            return false;
        } finally {
            if ($compressedPath && file_exists($compressedPath) && $compressedPath !== $localPath) {
                @unlink($compressedPath);
            }
            if (file_exists($localPath)) {
                @unlink($localPath);
            }
        }
    }

    /**
     * 설비 사진 삭제
     */
    public function deletePhoto(Equipment $equipment, int $fileId): bool
    {
        $file = $equipment->photos()->where('id', $fileId)->first();

        if (! $file) {
            return false;
        }

        if ($file->gcs_object_name) {
            $this->googleCloudService->deleteFromStorage($file->gcs_object_name);
        }

        $file->forceDelete();

        return true;
    }

    /**
     * 설비 사진 목록 (Signed URL 포함)
     */
    public function getPhotoUrls(Equipment $equipment): array
    {
        $photos = $equipment->photos()->get();

        return $photos->map(function ($photo) {
            $url = null;
            if ($photo->gcs_object_name) {
                $url = $this->googleCloudStorageService->getSignedUrl($photo->gcs_object_name, 120);
            }

            return [
                'id' => $photo->id,
                'original_name' => $photo->original_name,
                'file_size' => $photo->file_size,
                'mime_type' => $photo->mime_type,
                'url' => $url,
                'created_at' => $photo->created_at?->format('Y-m-d H:i'),
            ];
        })->toArray();
    }

    /**
     * 설비의 모든 사진 삭제 (설비 삭제 시)
     */
    public function deleteAllPhotos(Equipment $equipment): void
    {
        foreach ($equipment->photos as $photo) {
            if ($photo->gcs_object_name) {
                $this->googleCloudService->deleteFromStorage($photo->gcs_object_name);
            }
            $photo->forceDelete();
        }
    }

    /**
     * 이미지 압축 (1MB 이하로)
     *
     * 1단계: 장축 2048px 초과 시 리사이즈
     * 2단계: JPEG 품질 85→40 점진적 감소
     *
     * @return array{0: string, 1: string} [압축 파일 경로, 최종 확장자]
     */
    public function compressImage(string $sourcePath, string $extension): array
    {
        $image = $this->createImageFromFile($sourcePath, $extension);
        if (! $image) {
            return [$sourcePath, $extension];
        }

        // 1단계: 리사이즈 (장축 2048px 초과 시)
        $width = imagesx($image);
        $height = imagesy($image);

        if ($width > self::MAX_DIMENSION || $height > self::MAX_DIMENSION) {
            $ratio = min(self::MAX_DIMENSION / $width, self::MAX_DIMENSION / $height);
            $newWidth = (int) round($width * $ratio);
            $newHeight = (int) round($height * $ratio);

            $resized = imagecreatetruecolor($newWidth, $newHeight);
            imagecopyresampled($resized, $image, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
            imagedestroy($image);
            $image = $resized;
        }

        // PNG 투명도 확인 → 투명 있으면 PNG 유지, 없으면 JPEG 변환
        $outputAsPng = ($extension === 'png') && $this->hasTransparency($image);
        $finalExtension = $outputAsPng ? 'png' : 'jpg';

        $tempPath = tempnam(sys_get_temp_dir(), 'eq_compress_');

        if ($outputAsPng) {
            // PNG: 최대 압축 레벨(9)로 저장
            imagepng($image, $tempPath, 9);
            imagedestroy($image);

            return [$tempPath, $finalExtension];
        }

        // 2단계: JPEG 품질 점진적 감소
        $quality = self::INITIAL_QUALITY;
        while ($quality >= self::MIN_QUALITY) {
            imagejpeg($image, $tempPath, $quality);

            if (filesize($tempPath) <= self::MAX_FILE_SIZE) {
                break;
            }

            $quality -= self::QUALITY_STEP;
        }

        imagedestroy($image);

        return [$tempPath, $finalExtension];
    }

    /**
     * 확장자에 따라 GD 이미지 리소스 생성
     */
    public function createImageFromFile(string $path, string $extension): ?\GdImage
    {
        return match ($extension) {
            'jpg', 'jpeg' => @imagecreatefromjpeg($path),
            'png' => @imagecreatefrompng($path),
            'gif' => @imagecreatefromgif($path),
            'bmp' => @imagecreatefrombmp($path),
            default => null,
        };
    }

    /**
     * 이미지에 투명 픽셀이 있는지 확인
     */
    public function hasTransparency(\GdImage $image): bool
    {
        $width = imagesx($image);
        $height = imagesy($image);

        // 전체 픽셀 검사는 비효율적이므로 샘플링 (최대 1000개)
        $step = max(1, (int) sqrt($width * $height / 1000));

        for ($x = 0; $x < $width; $x += $step) {
            for ($y = 0; $y < $height; $y += $step) {
                $rgba = imagecolorat($image, $x, $y);
                $alpha = ($rgba >> 24) & 0x7F;
                if ($alpha > 0) {
                    return true;
                }
            }
        }

        return false;
    }
}
