import { useState, useRef, useEffect, useCallback } from 'react';
import * as exifr from 'exifr';
import { stitchImages } from '../services/aerialApi';
import { analyzeImage } from '@tabs/prediction/services/predictionApi';
import { setPendingImageAnalysis } from '@common/utils/imageAnalysisSignal';
import { navigateToTab } from '@common/hooks/useSubMenu';
import { decimalToDMS } from '@common/utils/coordinates';

const MAX_IMAGES = 6;

interface ImageExif {
  lat: number | null;
  lon: number | null;
  altitude: number | null;
  make: string | null;
  model: string | null;
  dateTime: Date | string | null;
  exposureTime: number | null;
  fNumber: number | null;
  iso: number | null;
  focalLength: number | null;
  imageWidth: number | null;
  imageHeight: number | null;
}

function formatFileSize(bytes?: number): string | null {
  if (bytes == null) return null;
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

function formatDateTime(dt: Date | string | null): string | null {
  if (!dt) return null;
  if (dt instanceof Date) {
    return dt.toLocaleString('ko-KR', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
    });
  }
  return String(dt);
}

interface MetaRowProps {
  label: string;
  value: string | null | undefined;
}

function MetaRow({ label, value }: MetaRowProps) {
  if (value == null) return null;
  return (
    <div className="flex justify-between gap-2 py-0.5 border-b border-stroke/40 last:border-0 font-korean">
      <span className="text-fg-disabled shrink-0">{label}</span>
      <span className="text-fg text-right break-all">{value}</span>
    </div>
  );
}

export function OilAreaAnalysis() {
  const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
  const [previewUrls, setPreviewUrls] = useState<string[]>([]);
  const [imageExifs, setImageExifs] = useState<(ImageExif | undefined)[]>([]);
  const [selectedImageIndex, setSelectedImageIndex] = useState<number | null>(null);
  const [stitchedBlob, setStitchedBlob] = useState<Blob | null>(null);
  const [stitchedPreviewUrl, setStitchedPreviewUrl] = useState<string | null>(null);
  const [isStitching, setIsStitching] = useState(false);
  const [isAnalyzing, setIsAnalyzing] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const processedFilesRef = useRef<Set<File>>(new Set());

  // Object URL 메모리 누수 방지 — 언마운트 시 전체 revoke
  useEffect(() => {
    return () => {
      previewUrls.forEach((url) => URL.revokeObjectURL(url));
      if (stitchedPreviewUrl) URL.revokeObjectURL(stitchedPreviewUrl);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // 선택된 파일이 바뀔 때마다 새 파일의 EXIF 전체 추출
  useEffect(() => {
    selectedFiles.forEach((file, i) => {
      if (processedFilesRef.current.has(file)) return;
      processedFilesRef.current.add(file);

      exifr
        .parse(file, { gps: true, exif: true, ifd0: true, translateValues: false })
        .then((exif) => {
          const info: ImageExif = {
            lat: exif?.latitude ?? null,
            lon: exif?.longitude ?? null,
            altitude: exif?.GPSAltitude ?? null,
            make: exif?.Make ?? null,
            model: exif?.Model ?? null,
            dateTime: exif?.DateTimeOriginal ?? null,
            exposureTime: exif?.ExposureTime ?? null,
            fNumber: exif?.FNumber ?? null,
            iso: exif?.ISO ?? null,
            focalLength: exif?.FocalLength ?? null,
            imageWidth: exif?.ImageWidth ?? exif?.ExifImageWidth ?? null,
            imageHeight: exif?.ImageHeight ?? exif?.ExifImageHeight ?? null,
          };
          setImageExifs((prev) => {
            const updated = [...prev];
            while (updated.length <= i) updated.push(undefined);
            updated[i] = info;
            return updated;
          });
        })
        .catch(() => {
          setImageExifs((prev) => {
            const updated = [...prev];
            while (updated.length <= i) updated.push(undefined);
            updated[i] = {
              lat: null,
              lon: null,
              altitude: null,
              make: null,
              model: null,
              dateTime: null,
              exposureTime: null,
              fNumber: null,
              iso: null,
              focalLength: null,
              imageWidth: null,
              imageHeight: null,
            };
            return updated;
          });
        });
    });
  }, [selectedFiles]);

  const handleFileSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    setError(null);
    const incoming = Array.from(e.target.files ?? []);
    if (incoming.length === 0) return;

    setSelectedFiles((prev) => {
      const merged = [...prev, ...incoming].slice(0, MAX_IMAGES);
      if (prev.length + incoming.length > MAX_IMAGES) {
        setError(`최대 ${MAX_IMAGES}장까지 선택할 수 있습니다.`);
      }
      return merged;
    });

    // setSelectedFiles updater 밖에서 독립 호출 — updater 내부 side effect는
    // React Strict Mode의 이중 호출로 인해 URL이 중복 생성되는 버그를 유발함
    setPreviewUrls((prev) => {
      const available = MAX_IMAGES - prev.length;
      const toAdd = incoming.slice(0, available);
      return [...prev, ...toAdd.map((f) => URL.createObjectURL(f))];
    });

    // input 초기화 (동일 파일 재선택 허용)
    e.target.value = '';
  }, []);

  const handleRemoveFile = useCallback(
    (idx: number) => {
      setSelectedFiles((prev) => prev.filter((_, i) => i !== idx));
      setPreviewUrls((prev) => {
        URL.revokeObjectURL(prev[idx]);
        return prev.filter((_, i) => i !== idx);
      });
      setImageExifs((prev) => prev.filter((_, i) => i !== idx));
      setSelectedImageIndex((prev) => {
        if (prev === null) return null;
        if (prev === idx) return null;
        if (prev > idx) return prev - 1;
        return prev;
      });
      // 합성 결과 초기화 (선택 파일이 바뀌었으므로)
      setStitchedBlob(null);
      if (stitchedPreviewUrl) {
        URL.revokeObjectURL(stitchedPreviewUrl);
        setStitchedPreviewUrl(null);
      }
      setError(null);
    },
    [stitchedPreviewUrl],
  );

  const handleStitch = async () => {
    if (selectedFiles.length < 2) {
      setError('이미지를 2장 이상 선택해주세요.');
      return;
    }
    setError(null);
    setIsStitching(true);
    try {
      const blob = await stitchImages(selectedFiles);
      if (stitchedPreviewUrl) URL.revokeObjectURL(stitchedPreviewUrl);
      setStitchedBlob(blob);
      setStitchedPreviewUrl(URL.createObjectURL(blob));
    } catch (err) {
      const msg =
        err instanceof Error
          ? err.message
          : ((err as { message?: string }).message ?? '이미지 합성에 실패했습니다.');
      const status = err instanceof Error ? 0 : ((err as { status?: number }).status ?? 0);
      setError(status === 504 ? '이미지 합성 서버 응답 시간이 초과되었습니다.' : msg);
    } finally {
      setIsStitching(false);
    }
  };

  const handleAnalyze = async () => {
    if (!stitchedBlob) return;
    setError(null);
    setIsAnalyzing(true);
    try {
      const stitchedFile = new File([stitchedBlob], `stitch_${Date.now()}.jpg`, {
        type: 'image/jpeg',
      });
      const result = await analyzeImage(stitchedFile);
      setPendingImageAnalysis({ ...result, autoRun: true });
      navigateToTab('prediction', 'analysis');
    } catch (err) {
      const msg = err instanceof Error ? err.message : '분석에 실패했습니다.';
      setError(
        msg.includes('GPS')
          ? '이미지에 GPS 정보가 없습니다. GPS 정보가 포함된 이미지를 사용해주세요.'
          : msg,
      );
      setIsAnalyzing(false);
    }
  };

  const canStitch = selectedFiles.length >= 2 && !isStitching && !isAnalyzing;
  const canAnalyze = stitchedBlob !== null && !isStitching && !isAnalyzing;

  return (
    <div className="flex gap-5 h-full overflow-hidden">
      {/* ── Left Panel ── */}
      <div className="w-[280px] min-w-[280px] flex flex-col overflow-y-auto scrollbar-thin">
        <div className="text-body-2 font-bold mb-1 font-korean">🧩 영상사진합성</div>
        <div className="text-label-2 text-fg-disabled mb-4 font-korean">
          드론 사진을 합성하여 유출유 확산 면적과 기름 양을 산정합니다.
        </div>

        {/* 이미지 선택 버튼 */}
        <input
          ref={fileInputRef}
          type="file"
          accept="image/*"
          multiple
          className="hidden"
          onChange={handleFileSelect}
        />
        <button
          onClick={() => fileInputRef.current?.click()}
          disabled={selectedFiles.length >= MAX_IMAGES || isStitching || isAnalyzing}
          className="w-full py-2 mb-3 border border-dashed border-stroke rounded-sm text-caption font-korean text-fg-sub
                     hover:border-color-accent hover:text-color-accent transition-colors cursor-pointer
                     disabled:opacity-40 disabled:cursor-not-allowed"
        >
          + 이미지 선택 ({selectedFiles.length}/{MAX_IMAGES})
        </button>

        {/* 선택된 이미지 목록 */}
        {selectedFiles.length > 0 && (
          <>
            <div className="text-label-2 font-bold mb-1.5 font-korean">선택된 이미지</div>
            <div className="flex flex-col gap-1 mb-3">
              {selectedFiles.map((file, i) => (
                <div key={`${file.name}-${i}`}>
                  <div
                    className={`flex items-center gap-2 px-2 py-1.5 bg-bg-card border rounded-sm text-label-2 font-korean cursor-pointer transition-colors
                      ${selectedImageIndex === i ? 'border-color-accent' : 'border-stroke'}`}
                    onClick={() => setSelectedImageIndex(i)}
                  >
                    <span className="text-color-accent">📷</span>
                    <span className="flex-1 truncate text-fg">{file.name}</span>
                    <button
                      onClick={(e) => {
                        e.stopPropagation();
                        handleRemoveFile(i);
                      }}
                      disabled={isStitching || isAnalyzing}
                      className="text-fg-disabled hover:text-color-danger transition-colors cursor-pointer
                                 disabled:opacity-40 disabled:cursor-not-allowed ml-1 shrink-0"
                      title="제거"
                    >
                      ✕
                    </button>
                  </div>
                  {selectedImageIndex === i && imageExifs[i] !== undefined && (
                    <div className="mt-1 mb-1 px-2 py-1.5 bg-bg-base border border-stroke/60 rounded-sm text-label-2 font-korean">
                      <MetaRow label="파일 크기" value={formatFileSize(file.size)} />
                      <MetaRow
                        label="해상도"
                        value={
                          imageExifs[i]!.imageWidth && imageExifs[i]!.imageHeight
                            ? `${imageExifs[i]!.imageWidth} × ${imageExifs[i]!.imageHeight}`
                            : null
                        }
                      />
                      <MetaRow label="촬영일시" value={formatDateTime(imageExifs[i]!.dateTime)} />
                      <MetaRow
                        label="장비"
                        value={
                          imageExifs[i]!.make || imageExifs[i]!.model
                            ? [imageExifs[i]!.make, imageExifs[i]!.model].filter(Boolean).join(' ')
                            : null
                        }
                      />
                      <MetaRow
                        label="위도"
                        value={
                          imageExifs[i]!.lat !== null
                            ? decimalToDMS(imageExifs[i]!.lat!, true)
                            : null
                        }
                      />
                      <MetaRow
                        label="경도"
                        value={
                          imageExifs[i]!.lon !== null
                            ? decimalToDMS(imageExifs[i]!.lon!, false)
                            : null
                        }
                      />
                      <MetaRow
                        label="고도"
                        value={
                          imageExifs[i]!.altitude !== null
                            ? `${imageExifs[i]!.altitude!.toFixed(1)} m`
                            : null
                        }
                      />
                      <MetaRow
                        label="셔터속도"
                        value={
                          imageExifs[i]!.exposureTime
                            ? imageExifs[i]!.exposureTime! < 1
                              ? `1/${Math.round(1 / imageExifs[i]!.exposureTime!)}s`
                              : `${imageExifs[i]!.exposureTime}s`
                            : null
                        }
                      />
                      <MetaRow
                        label="조리개"
                        value={imageExifs[i]!.fNumber ? `f/${imageExifs[i]!.fNumber}` : null}
                      />
                      <MetaRow
                        label="ISO"
                        value={imageExifs[i]!.iso ? String(imageExifs[i]!.iso) : null}
                      />
                      <MetaRow
                        label="초점거리"
                        value={
                          imageExifs[i]!.focalLength ? `${imageExifs[i]!.focalLength} mm` : null
                        }
                      />
                    </div>
                  )}
                </div>
              ))}
            </div>
          </>
        )}

        {/* 에러 메시지 */}
        {error && (
          <div className="mb-3 px-2.5 py-2 bg-[rgba(239,68,68,0.08)] border border-[rgba(239,68,68,0.3)] rounded-sm text-label-2 text-color-danger font-korean">
            {error}
          </div>
        )}

        {/* 이미지 합성 버튼 */}
        <button
          onClick={handleStitch}
          disabled={!canStitch}
          className="w-full py-2.5 mb-2 rounded-sm text-label-1 font-bold font-korean cursor-pointer transition-colors
                     border border-color-accent text-color-accent bg-[rgba(6,182,212,0.06)]
                     hover:bg-[rgba(6,182,212,0.15)] disabled:opacity-40 disabled:cursor-not-allowed"
        >
          {isStitching ? '⏳ 합성 중...' : stitchedBlob ? '✅ 다시 합성' : '🔗 이미지 합성'}
        </button>

        {/* 분석 시작 버튼 */}
        <button
          onClick={handleAnalyze}
          disabled={!canAnalyze}
          className="w-full py-3 rounded-sm text-title-4 font-bold font-korean cursor-pointer border-none transition-colors
                     disabled:opacity-40 disabled:cursor-not-allowed text-white"
          style={
            canAnalyze
              ? { background: 'linear-gradient(135deg, var(--color-accent), var(--color-info))' }
              : { background: 'var(--bg-3)' }
          }
        >
          {isAnalyzing ? '⏳ 분석 중...' : '🧩 분석 시작'}
        </button>
      </div>

      {/* ── Right Panel ── */}
      <div className="flex-1 flex flex-col overflow-hidden">
        {/* 3×2 이미지 그리드 */}
        <div className="text-label-2 font-bold mb-2 font-korean">선택된 이미지 미리보기</div>
        <div className="grid grid-cols-3 gap-1.5 mb-3">
          {Array.from({ length: MAX_IMAGES }).map((_, i) => (
            <div
              key={i}
              className={`bg-bg-card border rounded-sm overflow-hidden flex flex-col transition-colors
                ${previewUrls[i] ? 'cursor-pointer' : ''}
                ${selectedImageIndex === i ? 'border-color-accent' : 'border-stroke'}`}
              style={{ height: '300px' }}
              onClick={() => {
                if (previewUrls[i]) setSelectedImageIndex(i);
              }}
            >
              {previewUrls[i] ? (
                <>
                  <div className="flex-1 min-h-0 overflow-hidden">
                    <img
                      src={previewUrls[i]}
                      alt={selectedFiles[i]?.name ?? ''}
                      className="w-full h-full object-cover"
                    />
                  </div>
                  <div className="px-2 py-1 bg-bg-base border-t border-stroke shrink-0 flex items-start justify-between gap-1">
                    <div className="text-caption text-fg-sub truncate font-korean flex-1 min-w-0">
                      {selectedFiles[i]?.name}
                    </div>
                    {imageExifs[i] === undefined ? (
                      <div className="text-caption text-fg-disabled font-korean shrink-0">
                        GPS 읽는 중...
                      </div>
                    ) : imageExifs[i]?.lat !== null ? (
                      <div className="text-caption text-color-accent font-mono leading-tight text-right shrink-0">
                        {decimalToDMS(imageExifs[i]!.lat!, true)}
                        <br />
                        {decimalToDMS(imageExifs[i]!.lon!, false)}
                      </div>
                    ) : (
                      <div className="text-caption text-fg-disabled font-korean shrink-0">
                        GPS 정보 없음
                      </div>
                    )}
                  </div>
                </>
              ) : (
                <div className="flex items-center justify-center h-full text-fg-disabled text-lg font-mono opacity-20">
                  {i + 1}
                </div>
              )}
            </div>
          ))}
        </div>

        {/* 합성 결과 */}
        <div className="text-label-2 font-bold mb-2 font-korean">합성 결과</div>
        <div
          className="relative bg-bg-base border border-stroke rounded-sm overflow-hidden flex items-center justify-center"
          style={{ minHeight: '160px', flex: '1 1 0' }}
        >
          {stitchedPreviewUrl ? (
            <img
              src={stitchedPreviewUrl}
              alt="합성 결과"
              className="max-w-full max-h-full object-contain"
            />
          ) : (
            <div className="text-label-1 text-fg-disabled font-korean text-center px-4">
              {isStitching
                ? '⏳ 이미지를 합성하고 있습니다...'
                : '이미지를 선택하고 합성 버튼을 클릭하면\n합성 결과가 여기에 표시됩니다.'}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}
