<script>
  /**
   * /study/write/[id] — 학습 세션 작업 화면.
   *
   * 캔버스 풀스크린 + 좌측 floating panel (default closed). iPad portrait /
   * 모바일에서도 캔버스가 화면을 거의 전부 차지하도록.
   * 메타 편집 / asset 목록은 헤더 "패널" 버튼으로 열고 닫는다.
   */
  import { onMount, onDestroy } from 'svelte';
  import { page } from '$app/stores';
  import { api, uploadFile } from '$lib/api';
  import { addToast } from '$lib/stores/toast';
  import { ArrowLeft, RotateCw, PanelLeftOpen, PanelLeftClose, X } from 'lucide-svelte';
  import Button from '$lib/components/ui/Button.svelte';
  import Skeleton from '$lib/components/ui/Skeleton.svelte';
  import HandwriteCanvas from '$lib/components/HandwriteCanvas.svelte';
  import SourceTextPanel from '$lib/components/SourceTextPanel.svelte';
  import StudyMetaEditor from '$lib/components/StudyMetaEditor.svelte';
  import AssetList from '$lib/components/AssetList.svelte';

  let sessionId = $derived(parseInt($page.params.id, 10));
  let sess = $state(null);
  let loading = $state(true);
  let repCount = $state(0);
  let panelOpen = $state(false);

  async function load() {
    loading = true;
    try {
      sess = await api(`/study-sessions/${sessionId}`);
      repCount = sess.repetition_count;
    } catch (err) {
      addToast('error', err.detail || '세션 로딩 실패');
    } finally {
      loading = false;
    }
  }

  // iOS Safari 핀치/zoom gesture 차단 — document level 등록 (element-level
  // ongesturestart 가 일부 iPad Safari 빌드에서 미발화하는 케이스 방어).
  // 페이지 unmount 시 onDestroy 에서 cleanup.
  function blockGesture(e) { e.preventDefault(); }
  // 추가: touchstart 가 두 손가락이면 preventDefault — 일부 Safari 빌드에서
  // gesture 이벤트 미발화 시에도 핀치 제스처 자체를 차단.
  // 데스크톱 Chrome/Firefox: trackpad 핀치 = wheel + ctrlKey 로 디스패치.
  function blockPinchWheel(e) {
    if (e.ctrlKey || e.metaKey) e.preventDefault();
  }
  // 주의: blockMultiTouch (touchstart 의 length>1 preventDefault) 는 제거함.
  // iOS Safari 의 자체 palm rejection (Apple Pencil 닿으면 손가락 touch 자동 무시) 이
  // touch 이벤트 default 처리에 의존하는데, preventDefault 가 그 메커니즘을 망가뜨려
  // pointercancel 발생률 증가시킴 (사용자 디버그 패널에서 cancel:4 관측).

  // 모든 button 공통 inline style — 더블탭 줌 / long-press 메뉴 / 텍스트 선택 차단.
  const BTN_STYLE =
    'touch-action: manipulation; user-select: none; -webkit-user-select: none; ' +
    '-webkit-touch-callout: none; -webkit-tap-highlight-color: transparent;';

  // button click 직후 즉시 blur — iPad/Chrome 의 button focus zoom 의 2차 안전망.
  function clickThenBlur(fn) {
    return (e) => {
      const target = e?.currentTarget;
      if (target?.blur) target.blur();
      fn();
    };
  }
  // iOS Safari 의 button focus 가 mousedown/pointerdown 단계에 발동 → 그 영역으로
  // 자동 zoom (사용자 보고: "펜/지우개 누르면 해당 부분 확대"). focus 자체를 막아야
  // zoom trigger 안 됨. click 이벤트는 별도라 onclick 정상 작동.
  function blockBtnFocus(e) { e.preventDefault(); }

  // iPadOS Apple Pencil Scribble / Apple Intelligence 가 펜 stroke 를 텍스트 선택
  // 제스처로 해석해 callout 메뉴 ("복사하기 / 선택 영역 찾기 / 찾아보기 / 번역") 를
  // 띄우면 펜 입력이 메뉴 인터랙션으로 흡수되어 캔버스 stroke 가 막힘 (ㄱ→ㅏ hang
  // 의 진짜 원인). element CSS user-select:none 만으로는 OS 레벨 동작을 못 막음.
  // → document 레벨 selectstart capture 차단 + selectionchange clear 로 selection
  // 이 어떤 경로로든 잡히면 즉시 해제.
  function clearSelection() {
    const sel = document.getSelection?.();
    if (sel && sel.rangeCount > 0) { try { sel.removeAllRanges(); } catch {} }
  }
  function blockSelectStart(e) {
    e.preventDefault();
    clearSelection();
  }

  onMount(() => {
    load();
    document.addEventListener('gesturestart', blockGesture, { passive: false });
    document.addEventListener('gesturechange', blockGesture, { passive: false });
    document.addEventListener('gestureend', blockGesture, { passive: false });
    document.addEventListener('wheel', blockPinchWheel, { passive: false });
    // capture phase 로 모든 자식 element 의 selectstart 차단.
    document.addEventListener('selectstart', blockSelectStart, { capture: true, passive: false });
    document.addEventListener('selectionchange', clearSelection);
    // html/body 까지 user-select:none 강제 — Svelte 컴포넌트 스코프가 닿지 않는
    // root element 가 selection origin 이 되는 케이스 차단. onDestroy 에서 복원.
    document.documentElement.style.webkitUserSelect = 'none';
    document.documentElement.style.userSelect = 'none';
    document.body.style.webkitUserSelect = 'none';
    document.body.style.userSelect = 'none';
    // @ts-ignore - vendor prop
    document.documentElement.style.webkitTouchCallout = 'none';
    // @ts-ignore
    document.body.style.webkitTouchCallout = 'none';
  });
  onDestroy(() => {
    if (typeof document !== 'undefined') {
      document.removeEventListener('gesturestart', blockGesture);
      document.removeEventListener('gesturechange', blockGesture);
      document.removeEventListener('gestureend', blockGesture);
      document.removeEventListener('wheel', blockPinchWheel);
      document.removeEventListener('selectstart', blockSelectStart, { capture: true });
      document.removeEventListener('selectionchange', clearSelection);
      // 다른 페이지에서 selection 가능하도록 원복.
      document.documentElement.style.webkitUserSelect = '';
      document.documentElement.style.userSelect = '';
      document.body.style.webkitUserSelect = '';
      document.body.style.userSelect = '';
      // @ts-ignore
      document.documentElement.style.webkitTouchCallout = '';
      // @ts-ignore
      document.body.style.webkitTouchCallout = '';
    }
  });

  async function patchSession(partial) {
    try {
      const updated = await api(`/study-sessions/${sessionId}`, {
        method: 'PATCH',
        body: JSON.stringify(partial),
      });
      sess = updated;
    } catch (err) {
      addToast('error', err.detail || '저장 실패');
    }
  }

  async function onStrokesChange(strokesJson) {
    try {
      await api(`/study-sessions/${sessionId}`, {
        method: 'PATCH',
        body: JSON.stringify({ strokes_json: strokesJson }),
      });
    } catch (err) {
      console.warn('strokes 저장 실패', err);
    }
  }

  async function onSnapshot(blob) {
    const fd = new FormData();
    fd.append('file', blob, `session_${sessionId}_snapshot.png`);
    fd.append('sort_order', '0');
    try {
      await uploadFile(`/study-sessions/${sessionId}/snapshot`, fd);
      addToast('success', 'PNG 스냅샷 저장됨');
      await load();
    } catch (err) {
      addToast('error', err.detail || '스냅샷 저장 실패');
      throw err;
    }
  }

  async function bumpRep() {
    repCount += 1;
    await patchSession({ repetition_count: repCount });
  }

  async function unlinkAsset(assetId) {
    try {
      await api(`/study-sessions/${sessionId}/assets/${assetId}`, { method: 'DELETE' });
      sess = { ...sess, assets: sess.assets.filter((a) => a.id !== assetId) };
    } catch (err) {
      addToast('error', err.detail || '연결 해제 실패');
    }
  }

  async function linkAsset({ document_id, asset_type, role }) {
    try {
      const asset = await api(`/study-sessions/${sessionId}/assets`, {
        method: 'POST',
        body: JSON.stringify({ document_id, asset_type, role }),
      });
      sess = { ...sess, assets: [...(sess.assets || []), asset] };
      addToast('success', '문서가 연결되었습니다');
    } catch (err) {
      const code = err.errorCode;
      if (code === 'asset_already_linked') {
        addToast('warning', '이미 연결된 문서입니다');
      } else {
        addToast('error', err.detail || '연결 실패');
      }
    }
  }
</script>

<svelte:head>
  <!-- 학습 캔버스: 핀치줌이 의도 사용 패턴 아님. element-level gesturestart 차단이
       일부 iOS Safari 빌드에서 흡수만 되고 줌이 진행되는 케이스 (페이지가 1사분면으로
       확대되는 회귀) 를 viewport 레벨에서 원천 차단. iOS 13+ 에서 maximum-scale=1 +
       user-scalable=no 조합이 OS 핀치줌 차단으로 동작. 접근성 zoom 은 별도 zoom
       버튼으로 Phase 2 에서 제공. 페이지별 meta 라 다른 페이지 영향 없음. -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" />
</svelte:head>

<div
  class="h-full flex flex-col relative"
  style="touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent;"
  ongesturestart={blockGesture}
  ongesturechange={blockGesture}
  ongestureend={blockGesture}
>
  <!-- 상단 헤더 — 모든 button 에 BTN_STYLE 직접 적용 -->
  <div class="flex items-center justify-between gap-2 px-3 py-2 border-b border-default bg-surface shrink-0 z-10" style="touch-action: manipulation;">
    <Button href="/study/write" size="sm" variant="ghost" icon={ArrowLeft} style={BTN_STYLE}>목록</Button>

    <button
      type="button"
      onmousedown={blockBtnFocus}
      onpointerdown={blockBtnFocus}
      onclick={clickThenBlur(() => (panelOpen = !panelOpen))}
      style={BTN_STYLE}
      tabindex={-1}
      class="flex items-center gap-1 px-2 py-1 rounded text-xs text-text hover:bg-bg"
      aria-label="메타 패널 토글"
    >
      {#if panelOpen}
        <PanelLeftClose size={14} />
      {:else}
        <PanelLeftOpen size={14} />
      {/if}
      패널
    </button>

    <div class="flex-1 min-w-0 text-center text-xs text-dim truncate">
      {#if sess}
        {sess.study_type === 'language' ? `${sess.language_code || '?'} · ${sess.learning_level || ''}` : (sess.certification || '자격증')}
        · {sess.subject || ''} · {sess.topic || ''}
      {/if}
      {#if import.meta.env.DEV}
        <span class="ml-2 text-[10px] text-dim/60">build {__BUILD_TIME__}</span>
      {/if}
    </div>

    <button
      type="button"
      onmousedown={blockBtnFocus}
      onpointerdown={blockBtnFocus}
      onclick={clickThenBlur(bumpRep)}
      style={BTN_STYLE}
      tabindex={-1}
      class="flex items-center gap-1 px-2 py-1 rounded text-xs bg-accent/10 text-accent hover:bg-accent/15"
    >
      <RotateCw size={12} /> 다음 시도 ({repCount})
    </button>
  </div>

  {#if loading}
    <div class="p-4 flex-1">
      <Skeleton class="h-96" />
    </div>
  {:else if !sess}
    <div class="p-4 text-sm text-error">세션을 찾을 수 없습니다.</div>
  {:else}
    <!-- 본문: 캔버스 풀 영역 -->
    <div class="flex-1 min-h-0 relative">
      <HandwriteCanvas
        sessionId={sess.id}
        initialStrokes={sess.strokes_json}
        traceText={sess.mode === 'trace' ? sess.source_text : null}
        baseSize={sess.metadata?.unit_type === 'kanji' ? 9 : 7}
        onChange={onStrokesChange}
        onSnapshot={onSnapshot}
      />

      <!-- 좌측 floating panel (toggle) -->
      {#if panelOpen}
        <button
          type="button"
          aria-label="패널 닫기"
          onclick={() => (panelOpen = false)}
          class="absolute inset-0 bg-black/30 z-20"
        ></button>
        <aside
          class="absolute top-0 left-0 bottom-0 w-80 max-w-[85vw] bg-surface border-r border-default z-30
                 overflow-y-auto shadow-2xl"
        >
          <div class="flex items-center justify-between px-3 py-2 border-b border-default sticky top-0 bg-surface z-10">
            <span class="text-sm font-semibold text-text">세션 정보</span>
            <button
              type="button"
              onclick={() => (panelOpen = false)}
              class="p-1 rounded hover:bg-bg text-dim"
              aria-label="닫기"
            ><X size={16} /></button>
          </div>
          <SourceTextPanel
            sourceText={sess.source_text}
            sourcePage={sess.source_page}
            metadata={sess.metadata}
            mode={sess.mode}
            studyType={sess.study_type}
          />
          <details class="border-b border-default">
            <summary class="px-3 py-2 text-xs text-dim cursor-pointer hover:bg-bg">메타 편집</summary>
            <StudyMetaEditor meta={sess} onPatch={patchSession} />
          </details>
          <AssetList assets={sess.assets || []} onLink={linkAsset} onUnlink={unlinkAsset} />
        </aside>
      {/if}
    </div>
  {/if}
</div>
