/**
 * PC(Tauri) 앱 — 업데이트 확인·다운로드·설치 진행 모달
 */
import React, { useCallback, useEffect, useRef, useState } from 'react';
import DraggableModalFrame from './DraggableModalFrame';
import {
  getDesktopAppVersion,
  runDesktopUpdate,
  type DesktopUpdatePhase,
  type DesktopUpdateProgress,
} from '../utils/desktopBridge';

/** 다른 오버레이(플로팅 위젯 13000 등)보다 위에 표시 */
const DESKTOP_UPDATE_MODAL_Z = 15000;
import { isMacDesktop, isWindowsDesktop } from '../utils/platform';
import { PlatformBrandIcon, type PlatformBrandIconId } from './icons/PlatformBrandIcons';

interface Props {
  open: boolean;
  onClose: () => void;
  /** 열릴 때 자동으로 업데이트 확인 시작 */
  autoStart?: boolean;
}

const PHASE_LABEL: Record<DesktopUpdatePhase, string> = {
  checking: '업데이트 확인 중',
  uptodate: '최신 버전',
  available: '새 버전 발견',
  downloading: '다운로드 중',
  installing: '설치 중',
  relaunching: '재시작 중',
  error: '업데이트 실패',
};

function formatBytes(n?: number): string {
  if (n == null || !Number.isFinite(n) || n <= 0) return '';
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
  return `${(n / (1024 * 1024)).toFixed(1)} MB`;
}

const DesktopUpdateModal: React.FC<Props> = ({ open, onClose, autoStart = true }) => {
  const [appVersion, setAppVersion] = useState('—');
  const [progress, setProgress] = useState<DesktopUpdateProgress | null>(null);
  const [busy, setBusy] = useState(false);
  const startedRef = useRef(false);

  const isActive = busy || progress?.phase === 'checking' || progress?.phase === 'downloading'
    || progress?.phase === 'installing' || progress?.phase === 'relaunching';

  const panelClass = progress?.phase === 'error'
    ? 'desktop-update-progress--failed'
    : progress?.phase === 'uptodate'
      ? 'desktop-update-progress--success'
      : isActive
        ? 'desktop-update-progress--active'
        : '';

  const handleRun = useCallback(async (opts?: { bypassLoopGuard?: boolean }) => {
    if (busy) return;
    setBusy(true);
    setProgress({ phase: 'checking', message: '서버에서 새 버전을 확인하는 중…', currentVersion: appVersion });
    try {
      await runDesktopUpdate(p => setProgress(p), {
        bypassLoopGuard: opts?.bypassLoopGuard ?? true,
      });
    } finally {
      setBusy(false);
    }
  }, [appVersion, busy]);

  useEffect(() => {
    if (!open) {
      startedRef.current = false;
      setProgress(null);
      setBusy(false);
      return;
    }
    void getDesktopAppVersion().then(v => {
      if (v) setAppVersion(v);
    });
    if (autoStart && !startedRef.current) {
      startedRef.current = true;
      void handleRun({ bypassLoopGuard: true });
    }
  }, [open, autoStart, handleRun]);

  if (!open) return null;

  const phase = progress?.phase ?? 'checking';
  const showBar = phase === 'downloading' || phase === 'installing' || phase === 'available';
  const barPct = phase === 'installing'
    ? 100
    : progress?.downloadPercent;

  const platformBrand: PlatformBrandIconId | null = isMacDesktop()
    ? 'mac'
    : isWindowsDesktop()
      ? 'windows'
      : null;

  return (
    <DraggableModalFrame
      title="PC 프로그램 업데이트"
      onClose={isActive ? () => {} : onClose}
      closeOnBackdrop={!isActive}
      width={440}
      zIndex={DESKTOP_UPDATE_MODAL_Z}
      dialogClassName="sp-modal desktop-update-modal"
    >
      <div className="desktop-update-modal-body">
        {platformBrand && (
          <div className="desktop-update-platform-brand">
            <PlatformBrandIcon platform={platformBrand} />
          </div>
        )}
        <p className="desktop-update-version">
          현재 버전 <strong>v{progress?.currentVersion ?? appVersion}</strong>
          {progress?.newVersion && progress.newVersion !== progress.currentVersion && (
            <> → <strong>v{progress.newVersion}</strong></>
          )}
        </p>

        <div className={`desktop-update-progress ${panelClass}`.trim()}>
          <div className="desktop-update-progress-head">
            <span className="desktop-update-progress-title">
              {(phase === 'checking' || phase === 'downloading' || phase === 'installing' || phase === 'relaunching') && (
                <span className="desktop-update-spinner" aria-hidden />
              )}
              {PHASE_LABEL[phase]}
            </span>
            {phase === 'downloading' && progress?.downloadPercent != null && (
              <span className="desktop-update-pct">{progress.downloadPercent}%</span>
            )}
          </div>

          {showBar && (
            <div
              className="desktop-update-bar"
              role="progressbar"
              aria-valuemin={0}
              aria-valuemax={100}
              aria-valuenow={barPct ?? undefined}
            >
              {barPct != null ? (
                <div className="desktop-update-bar-fill desktop-update-bar-fill--determinate" style={{ width: `${barPct}%` }} />
              ) : (
                <div className="desktop-update-bar-fill desktop-update-bar-fill--indeterminate" />
              )}
            </div>
          )}

          <ul className="desktop-update-steps">
            <li className={`desktop-update-step${phase === 'checking' ? ' active' : phase !== 'error' ? ' done' : ''}`}>
              <span className="desktop-update-step-dot" aria-hidden />
              서버에서 버전 확인
            </li>
            <li className={`desktop-update-step${
              phase === 'available' || phase === 'downloading' ? ' active'
                : ['installing', 'relaunching', 'uptodate'].includes(phase) ? ' done' : ''
            }`}>
              <span className="desktop-update-step-dot" aria-hidden />
              업데이트 다운로드
              {progress?.downloadedBytes && progress.totalBytes ? (
                <span className="desktop-update-bytes">
                  {formatBytes(progress.downloadedBytes)} / {formatBytes(progress.totalBytes)}
                </span>
              ) : null}
            </li>
            <li className={`desktop-update-step${
              phase === 'installing' ? ' active' : phase === 'relaunching' || phase === 'uptodate' ? ' done' : ''
            }`}>
              <span className="desktop-update-step-dot" aria-hidden />
              설치 및 재시작
            </li>
          </ul>

          {progress?.message && (
            <p className={`desktop-update-message${phase === 'error' ? ' desktop-update-message--error' : ''}`}>
              {progress.message}
            </p>
          )}

          {phase === 'error' && (
            <p className="desktop-update-hint">
              {isWindowsDesktop() ? (
                <>
                  자동 업데이트가 설정되지 않았을 수 있습니다. 웹 <strong>PC 프로그램</strong> 탭에서
                  <strong> exe</strong> 설치 파일을 받아 다시 설치해 주세요.
                </>
              ) : isMacDesktop() ? (
                <>
                  자동 업데이트가 설정되지 않았을 수 있습니다. 웹 <strong>PC 프로그램</strong> 탭에서
                  <strong> dmg</strong>를 받아 Applications에 다시 설치해 주세요.
                </>
              ) : (
                <>
                  자동 업데이트가 설정되지 않았을 수 있습니다. 웹 <strong>PC 프로그램</strong> 탭에서
                  macOS는 dmg, Windows는 exe 설치 파일을 받아 다시 설치해 주세요.
                </>
              )}
            </p>
          )}

          {phase === 'uptodate' && (
            <p className="desktop-update-done-note">현재 설치된 버전이 서버와 같습니다.</p>
          )}
        </div>

        <div className="desktop-update-actions">
          {phase === 'error' && (
            <button type="button" className="stg-btn-secondary" disabled={busy} onClick={() => { void handleRun({ bypassLoopGuard: true }); }}>
              다시 시도
            </button>
          )}
          <button
            type="button"
            className="stg-btn-secondary"
            disabled={isActive}
            onClick={onClose}
          >
            {isActive ? '진행 중…' : '닫기'}
          </button>
        </div>
      </div>
    </DraggableModalFrame>
  );
};

export default DesktopUpdateModal;
