/**
 * @license
 * SPDX-License-Identifier: Apache-2.0
 */

import React, { useState, useEffect, createContext, useContext, useCallback } from "react";
import { motion, AnimatePresence } from "motion/react";
import {
  Search,
  Globe,
  Cpu,
  ChevronDown,
  Plus,
  HelpCircle,
  Loader2,
  ExternalLink,
  Filter,
  Download,
  Share2,
  X,
  ChevronLeft,
  ChevronRight,
  Sparkles,
  Maximize2,
  Minimize2,
  LogOut,
  User,
  Lock,
  AlertCircle,
} from "lucide-react";
import { SearchResult, SearchMeta, ViewState, SearchSession, AuthUser } from "./types";

// ─────────────────────────────────────────────────────────────
// Auth Context — 전역 인증 상태
// ─────────────────────────────────────────────────────────────
interface AuthContextType {
  user: AuthUser | null;
  isLoading: boolean;
  login: (username: string, password: string) => Promise<void>;
  logout: () => Promise<void>;
}

const AuthContext = createContext<AuthContextType>({
  user: null,
  isLoading: true,
  login: async () => {},
  logout: async () => {},
});

const useAuth = () => useContext(AuthContext);

const AUTH_STORAGE_KEY = "snap_user";

function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<AuthUser | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  // 첫 마운트 시 sessionStorage에서 사용자 정보 복원
  useEffect(() => {
    try {
      const stored = sessionStorage.getItem(AUTH_STORAGE_KEY);
      if (stored) {
        setUser(JSON.parse(stored));
      }
    } catch {
      sessionStorage.removeItem(AUTH_STORAGE_KEY);
    }
    setIsLoading(false);
  }, []);

  const login = useCallback(async (username: string, password: string) => {
    const res = await fetch("/api/auth/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username, password }),
    });

    const data = await res.json();

    if (!res.ok) {
      throw new Error(data.detail || "로그인에 실패했습니다");
    }

    // 인증 서버 응답에서 사용자 정보 저장
    const authUser: AuthUser = {
      name: data.name || "",
      username: data.username || "",
      department: data.department || "",
      company: data.company || "",
      email: data.email || "",
      section: data.section || "",
      title: data.title || "",
    };

    sessionStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(authUser));
    setUser(authUser);
  }, []);

  const logout = useCallback(async () => {
    try {
      await fetch("/api/auth/logout", { method: "POST" });
    } catch {
      // 서버 호출 실패해도 로컬은 정리
    }
    sessionStorage.removeItem(AUTH_STORAGE_KEY);
    setUser(null);
  }, []);

  return (
    <AuthContext.Provider value={{ user, isLoading, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// ─────────────────────────────────────────────────────────────
// LoginScreen — 전체화면 로그인 (게이팅)
// ─────────────────────────────────────────────────────────────
function LoginScreen() {
  const { login } = useAuth();
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!username.trim() || !password.trim()) {
      setError("사번과 비밀번호를 입력하세요");
      return;
    }

    setError("");
    setIsSubmitting(true);

    try {
      await login(username.trim(), password);
    } catch (err: any) {
      setError(err.message || "로그인에 실패했습니다");
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <div className="min-h-screen bg-surface flex items-center justify-center p-4">
      <motion.div
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.5 }}
        className="w-full max-w-md"
      >
        {/* 로고 */}
        <div className="text-center mb-10">
          <h1 className="text-[#E1251B] font-headline font-black text-5xl tracking-tighter mb-2">
            SNAP
          </h1>
          <p className="text-on-surface-variant text-sm tracking-wide">
            SK실트론 사내 지침서 검색 시스템
          </p>
        </div>

        {/* 로그인 폼 */}
        <div className="glass-panel rounded-lg p-8 shadow-xl border border-outline-variant/30">
          <form onSubmit={handleSubmit} className="space-y-5">
            {/* 사번 */}
            <div>
              <label className="block text-xs font-bold text-on-surface-variant uppercase tracking-widest mb-2">
                사번
              </label>
              <div className="relative">
                <User
                  size={16}
                  className="absolute left-3 top-1/2 -translate-y-1/2 text-on-surface-variant"
                />
                <input
                  type="text"
                  value={username}
                  onChange={(e) => setUsername(e.target.value)}
                  placeholder="사번을 입력하세요"
                  autoComplete="username"
                  autoFocus
                  className="w-full pl-10 pr-4 py-3 bg-surface-container-low border border-outline-variant/50 rounded-md text-on-surface placeholder:text-on-surface-variant/50 focus:outline-none focus:border-[#E1251B] focus:ring-1 focus:ring-[#E1251B]/30 transition-all text-sm"
                />
              </div>
            </div>

            {/* 비밀번호 */}
            <div>
              <label className="block text-xs font-bold text-on-surface-variant uppercase tracking-widest mb-2">
                비밀번호
              </label>
              <div className="relative">
                <Lock
                  size={16}
                  className="absolute left-3 top-1/2 -translate-y-1/2 text-on-surface-variant"
                />
                <input
                  type="password"
                  value={password}
                  onChange={(e) => setPassword(e.target.value)}
                  placeholder="비밀번호를 입력하세요"
                  autoComplete="current-password"
                  className="w-full pl-10 pr-4 py-3 bg-surface-container-low border border-outline-variant/50 rounded-md text-on-surface placeholder:text-on-surface-variant/50 focus:outline-none focus:border-[#E1251B] focus:ring-1 focus:ring-[#E1251B]/30 transition-all text-sm"
                />
              </div>
            </div>

            {/* 에러 메시지 */}
            <AnimatePresence>
              {error && (
                <motion.div
                  initial={{ opacity: 0, height: 0 }}
                  animate={{ opacity: 1, height: "auto" }}
                  exit={{ opacity: 0, height: 0 }}
                  className="flex items-center gap-2 text-[#E1251B] text-sm bg-[#E1251B]/10 rounded-md px-3 py-2"
                >
                  <AlertCircle size={14} />
                  {error}
                </motion.div>
              )}
            </AnimatePresence>

            {/* 로그인 버튼 */}
            <button
              type="submit"
              disabled={isSubmitting}
              className="w-full py-3 primary-gradient text-white font-headline font-bold text-sm rounded-md flex items-center justify-center gap-2 hover:shadow-lg transition-all disabled:opacity-60 disabled:cursor-not-allowed"
            >
              {isSubmitting ? (
                <>
                  <Loader2 size={16} className="animate-spin" />
                  인증 중...
                </>
              ) : (
                "로그인"
              )}
            </button>
          </form>

          <p className="text-center text-on-surface-variant/60 text-xs mt-6">
            사내 AD 계정(사번)으로 로그인합니다
          </p>
        </div>

        <p className="text-center text-on-surface-variant/40 text-xs mt-8">
          © SK siltron · Generative AI Team
        </p>
      </motion.div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// UserMenu — Navbar 우측 사용자 메뉴
// ─────────────────────────────────────────────────────────────
function UserMenu() {
  const { user, logout } = useAuth();
  const [open, setOpen] = useState(false);

  if (!user) return null;

  return (
    <div className="relative">
      <button
        onClick={() => setOpen(!open)}
        className="flex items-center gap-2 px-3 py-2 rounded-md hover:bg-surface-container-high/30 transition-colors"
      >
        <div className="w-7 h-7 rounded-full bg-[#E1251B] flex items-center justify-center text-white text-xs font-bold">
          {user.name.charAt(0)}
        </div>
        <span className="hidden md:inline text-sm text-on-surface font-medium">
          {user.name}
        </span>
        <ChevronDown
          size={14}
          className={`text-on-surface-variant transition-transform ${open ? "rotate-180" : ""}`}
        />
      </button>

      <AnimatePresence>
        {open && (
          <>
            {/* 클릭 외부 닫기용 오버레이 */}
            <div className="fixed inset-0 z-40" onClick={() => setOpen(false)} />

            <motion.div
              initial={{ opacity: 0, y: -8, scale: 0.95 }}
              animate={{ opacity: 1, y: 0, scale: 1 }}
              exit={{ opacity: 0, y: -8, scale: 0.95 }}
              transition={{ duration: 0.15 }}
              className="absolute right-0 top-full mt-2 w-64 z-50 glass-panel rounded-lg shadow-xl border border-outline-variant/30 overflow-hidden"
            >
              {/* 사용자 정보 */}
              <div className="px-4 py-4 border-b border-outline-variant/20">
                <p className="font-bold text-on-surface text-sm">{user.name}</p>
                <p className="text-on-surface-variant text-xs mt-0.5">
                  {user.department} · {user.title}
                </p>
                <p className="text-on-surface-variant/60 text-xs mt-0.5">
                  {user.email}
                </p>
              </div>

              {/* 로그아웃 */}
              <button
                onClick={async () => {
                  setOpen(false);
                  await logout();
                }}
                className="w-full px-4 py-3 flex items-center gap-2 text-sm text-on-surface hover:bg-surface-container-high/30 transition-colors"
              >
                <LogOut size={14} />
                로그아웃
              </button>
            </motion.div>
          </>
        )}
      </AnimatePresence>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 링크 추출 헬퍼 — attach_link/doc_page_link 매핑 단일화
// ─────────────────────────────────────────────────────────────
// 백엔드 매핑:
//   - result.url         ← attach_link  (다운로드)
//   - result.docPageLink ← doc_page_link  (QMS 시스템 링크)
function getDownloadUrl(result: SearchResult): string | null {
  const url = (result as any).url;
  if (!url || url === "#") return null;
  return url;
}

function getSystemLinkUrl(result: SearchResult): string | null {
  const link = (result as any).docPageLink;
  if (!link) return null;
  return link;
}

function openExternal(url: string | null) {
  if (!url) return;
  window.open(url, "_blank", "noopener,noreferrer");
}

// ─────────────────────────────────────────────────────────────
// snap_data 경로 헬퍼
// 검색된 페이지의 display_rel_path를 템플릿으로 임의 페이지 URL 생성
//   파일명 규칙: <doc_id>_<title>_<page>.jpg  (패딩 없는 정수)
//   예: ".../<doc>_<title>_3.jpg" → ".../<doc>_<title>_7.jpg"
// ─────────────────────────────────────────────────────────────
function buildPageThumbUrl(
  templateRelPath: string | null | undefined,
  targetPage: number,
): string | null {
  if (!templateRelPath) return null;
  const replaced = templateRelPath.replace(
    /_(\d+)\.(jpg|jpeg|png)$/i,
    `_${targetPage}.$2`,
  );
  if (replaced === templateRelPath) return null; // 패턴 매칭 실패
  const encoded = replaced.split("/").map(encodeURIComponent).join("/");
  return `/snap_data/${encoded}`;
}

// ─────────────────────────────────────────────────────────────
// Navbar
// ─────────────────────────────────────────────────────────────
const Navbar = ({ onHome }: { onHome: () => void }) => (
  <header className="glass-panel sticky top-0 z-50 px-6 py-3 flex justify-between items-center transition-all duration-300">
    <div className="flex items-center gap-8">
      <h1
        onClick={onHome}
        className="text-[#E1251B] font-headline font-black text-2xl tracking-tighter cursor-pointer hover:opacity-80 transition-opacity"
      >
        SNAP
      </h1>
      <nav className="hidden md:flex items-center gap-6">
        <a
          href="https://soni.sksiltron.co.kr/"
          target="_blank"
          rel="noopener noreferrer"
          className="text-on-surface hover:text-[#E1251B] transition-colors font-headline text-sm font-bold tracking-wide px-3 py-2 rounded-sm hover:bg-surface-container-high/30"
        >
          SONI
        </a>
        <a
          href="https://mail.sksiltron.co.kr/Covi/QMSN/default.asp"
          target="_blank"
          rel="noopener noreferrer"
          className="text-on-surface hover:text-[#E1251B] transition-colors font-headline text-sm font-bold tracking-wide px-3 py-2 rounded-sm hover:bg-surface-container-high/30"
        >
          QMS
        </a>
      </nav>
    </div>
    <UserMenu />
  </header>
);

// ─────────────────────────────────────────────────────────────
// 필터 옵션 (실데이터 doc_cate 분포 기반)
// ─────────────────────────────────────────────────────────────
const REGION_OPTIONS = [
  "전체",
  "3공장",
  "전사공통",
  "2공장",
  "2공장SiC",
  "공장공통",
  "1공장",
  "청주공장",
  "서울",
];
const PRODUCT_OPTIONS = [
  "전체",
  "300mm",
  "지원부문",
  "제품공통",
  "200mm",
  "150mm",
];

const FilterCard = ({
  icon: Icon,
  title,
  options,
  value,
  onChange,
}: {
  icon: any;
  title: string;
  options: string[];
  value: string;
  onChange: (v: string) => void;
}) => (
  <div className="bg-surface-container-low rounded-xl p-6 transition-all duration-300 hover:shadow-ambient">
    <div className="flex items-center mb-5 pb-3">
      <Icon className="text-[#8B1A1A] mr-2" size={20} />
      <h3 className="font-headline font-semibold text-on-surface text-[14px]">
        {title}
      </h3>
    </div>
    <div className="relative">
      <select
        value={value}
        onChange={(e) => onChange(e.target.value)}
        className="w-full appearance-none bg-white border border-outline-variant text-on-surface font-sans text-sm rounded-lg px-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-primary/10 focus:border-primary cursor-pointer transition-all"
      >
        {options.map((opt) => (
          <option key={opt} value={opt}>
            {opt}
          </option>
        ))}
      </select>
      <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-4 text-tertiary">
        <ChevronDown size={14} />
      </div>
    </div>
  </div>
);

// ─────────────────────────────────────────────────────────────
// ResultCard
//   - 시스템 링크: doc_page_link → result.docPageLink
//   - 다운로드:    attach_link → result.url
// ─────────────────────────────────────────────────────────────
interface ResultCardProps {
  key?: string | number;
  result: SearchResult;
  onSelect: (result: SearchResult) => void;
  onDownloadLog?: (result: SearchResult, downloadUrl: string) => void;
}

const ResultCard = ({ result, onSelect, onDownloadLog }: ResultCardProps) => {
  const downloadUrl = getDownloadUrl(result);
  const systemUrl = getSystemLinkUrl(result);

  const handleDownload = (e: React.MouseEvent) => {
    e.stopPropagation();
    if (!downloadUrl) {
      console.warn("다운로드 링크 없음", result.id);
      return;
    }
    onDownloadLog?.(result, downloadUrl);
    openExternal(downloadUrl);
  };

  const handleSystemLink = (e: React.MouseEvent) => {
    e.stopPropagation();
    if (!systemUrl) {
      console.warn("시스템 링크 없음", result.id);
      return;
    }
    openExternal(systemUrl);
  };

  const handleImgError = (e: React.SyntheticEvent<HTMLImageElement>) => {
    e.currentTarget.src =
      "data:image/svg+xml;utf8," +
      encodeURIComponent(
        `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300"><rect fill="#eeeeee" width="400" height="300"/><text x="200" y="150" font-size="14" fill="#999" text-anchor="middle" font-family="sans-serif">No Preview</text></svg>`,
      );
  };

  return (
    <motion.div
      layout
      initial={{ opacity: 0, y: 10 }}
      animate={{ opacity: 1, y: 0 }}
      onClick={() => onSelect(result)}
      className="bg-surface-container-lowest rounded-sm overflow-hidden group hover:shadow-ambient transition-all duration-500 border border-outline-variant/30 cursor-pointer"
    >
      <div className="relative aspect-[4/3] overflow-hidden bg-surface-container-low">
        <img
          src={result.thumbnail}
          alt={result.title}
          referrerPolicy="no-referrer"
          onError={handleImgError}
          className="w-full h-full object-contain transition-transform duration-700 group-hover:scale-[1.02]"
        />
        <div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center gap-4">
          <button
            onClick={handleDownload}
            disabled={!downloadUrl}
            className="w-8 h-8 rounded-full bg-white/20 backdrop-blur-md flex items-center justify-center text-white hover:bg-white hover:text-primary transition-all disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-white/20 disabled:hover:text-white"
            title={downloadUrl ? "원본 문서 다운로드" : "다운로드 링크 없음"}
          >
            <Download size={16} />
          </button>
          <button
            onClick={handleSystemLink}
            disabled={!systemUrl}
            className="w-8 h-8 rounded-full bg-white/20 backdrop-blur-md flex items-center justify-center text-white hover:bg-white hover:text-primary transition-all disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-white/20 disabled:hover:text-white"
            title={systemUrl ? "QMS 시스템에서 열기" : "시스템 링크 없음"}
          >
            <ExternalLink size={16} />
          </button>
        </div>
        {result.relevance > 90 && (
          <div className="absolute top-2 left-2 bg-primary px-2 py-0.5 text-[9px] font-bold text-white uppercase tracking-tighter shadow-lg">
            Top Insight
          </div>
        )}
      </div>
      <div className="p-2">
        <div className="flex justify-between items-center gap-2 mb-1.5">
          <h3 className="font-headline font-bold text-on-surface text-[13px] leading-tight group-hover:text-primary transition-colors line-clamp-1 flex-grow">
            {result.title}
          </h3>
          <span className="shrink-0 bg-surface-container-high px-1 py-0.5 rounded-[3px] text-[9px] font-bold text-tertiary">
            P.{(result.relatedPages?.slice(0, 3) || [result.page]).join(", ")}
          </span>
        </div>

        <div className="flex justify-between items-center pt-1.5 border-t border-outline-variant/10">
          <div className="flex items-center gap-1.5">
            <span className="text-[8px] font-bold text-tertiary uppercase">
              적합성
            </span>
            <span className="text-[11px] font-bold text-[#B00020]">
              {result.relevance}%
            </span>
          </div>

          <div className="flex items-center gap-1">
            <button
              onClick={handleSystemLink}
              disabled={!systemUrl}
              className="flex items-center gap-1 px-1.5 py-0.5 bg-surface-container-high hover:bg-surface-container-highest rounded-sm border border-outline-variant/20 transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-surface-container-high"
              title={systemUrl ? "QMS 시스템에서 열기" : "시스템 링크 없음"}
            >
              <ExternalLink size={11} className="text-on-surface-variant" />
              <span className="text-[9px] font-bold text-on-surface-variant uppercase">
                시스템 링크
              </span>
            </button>
            <button
              onClick={handleDownload}
              disabled={!downloadUrl}
              className="flex items-center gap-1 px-1.5 py-0.5 bg-[#E1251B] hover:bg-[#B00020] rounded-sm transition-colors shadow-sm disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-[#E1251B]"
              title={downloadUrl ? "원본 문서 다운로드" : "다운로드 링크 없음"}
            >
              <Download size={11} className="text-white" />
              <span className="text-[9px] font-bold text-white uppercase">
                다운로드
              </span>
            </button>
          </div>
        </div>
      </div>
    </motion.div>
  );
};

// ─────────────────────────────────────────────────────────────
// DetailModal
//   - /api/doc-detail로 LLM 종합 답변 표시
//   - 페이지 네비게이션: 1~totalPages 전체 순회 (snap_data 직접 접근)
//   - 초기 페이지: 가장 적합한 result.page
//   - RELATED PAGES: 검색에 매칭된 페이지 빠른 점프
// ─────────────────────────────────────────────────────────────
const DetailModal = ({
  result,
  query,
  onClose,
  onDownloadLog,
}: {
  result: SearchResult;
  query: string;
  onClose: () => void;
  onDownloadLog?: (result: SearchResult, downloadUrl: string) => void;
}) => {
  const [currentPage, setCurrentPage] = useState<number>(result.page);
  const [isExpanded, setIsExpanded] = useState(false);
  const [imgError, setImgError] = useState(false);
  const [totalPages, setTotalPages] = useState<number>(0);

  const [vlmAnswer, setVlmAnswer] = useState<string | null>(null);
  const [vlmLoading, setVlmLoading] = useState(false);
  const [vlmError, setVlmError] = useState<string | null>(null);

  const relatedPages = result.relatedPages || [result.page];
  const sortedMatched = [...new Set(relatedPages)].sort((a, b) => a - b);
  const matchedCount = result.matchedPageCount ?? sortedMatched.length;

  const downloadUrl = getDownloadUrl(result);
  const systemUrl = getSystemLinkUrl(result);

  const pages = (result as any).pages as
    | Array<{
        page: number | null;
        thumbnail: string;
        content: string | null;
        score?: number;
        rerank_score?: number | null;
        source?: string;
        image_name?: string | null;
        display_rel_path?: string | null;
      }>
    | undefined;

  // 검색된 페이지 중 display_rel_path를 가진 첫 항목을 템플릿으로 사용
  const templateRelPath =
    pages?.find((p) => p.display_rel_path)?.display_rel_path ?? null;

  // 1순위: 검색된 페이지에 currentPage 매칭이 있으면 그 썸네일
  // 2순위: 템플릿 기반 동적 URL 생성
  // 3순위: 대표 썸네일
  const matched = pages?.find((p) => p.page === currentPage);
  const pageThumb =
    matched?.thumbnail ||
    buildPageThumbUrl(templateRelPath, currentPage) ||
    result.thumbnail;

  // 페이지 변경 시 이미지 에러 상태 리셋
  useEffect(() => {
    setImgError(false);
  }, [currentPage]);

  // 문서 전체 페이지 수 조회 (snap_data 폴더 스캔)
  useEffect(() => {
    const docId = (result as any).docId || result.id;
    if (!docId) return;
    let cancelled = false;
    fetch(`/api/doc-pages?docId=${encodeURIComponent(docId)}`)
      .then((r) => r.json())
      .then((data) => {
        if (!cancelled && typeof data?.totalPages === "number") {
          setTotalPages(data.totalPages);
        }
      })
      .catch(() => {
        /* 실패 시 0 유지 — 좌우 이동 비활성화 효과 */
      });
    return () => {
      cancelled = true;
    };
  }, [result.id]);

  // ESC + 화살표 키 핸들러 (모달/전체화면 공용)
  useEffect(() => {
    const handleKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        if (isExpanded) {
          setIsExpanded(false);
        } else {
          onClose();
        }
        return;
      }
      if (e.key === "ArrowLeft") {
        e.preventDefault();
        handlePrevPage();
      } else if (e.key === "ArrowRight") {
        e.preventDefault();
        handleNextPage();
      }
    };
    window.addEventListener("keydown", handleKey);
    return () => window.removeEventListener("keydown", handleKey);
  }, [onClose, isExpanded]);

  // LLM 답변 호출
  useEffect(() => {
    let cancelled = false;
    const docId = (result as any).docId || result.id;
    if (!docId || !pages || pages.length === 0) return;

    setVlmLoading(true);
    setVlmError(null);
    setVlmAnswer(null);

    fetch("/api/doc-detail", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        query,
        docId,
        pages: pages.map((p) => ({
          page: p.page,
          score: p.score,
          rerank_score: p.rerank_score,
          content: p.content,
          source: p.source,
          image_name: p.image_name,
          display_rel_path: p.display_rel_path,
        })),
      }),
    })
      .then(async (r) => {
        if (!r.ok) throw new Error(`HTTP ${r.status}`);
        return r.json();
      })
      .then((data) => {
        if (!cancelled) {
          setVlmAnswer(data.vlm_answer || null);
          setVlmLoading(false);
        }
      })
      .catch((e) => {
        if (!cancelled) {
          console.error("doc-detail 호출 실패", e);
          setVlmError(String(e));
          setVlmLoading(false);
        }
      });

    return () => {
      cancelled = true;
    };
  }, [result.id, query]);

  // 문서 전체 페이지 순회 (1↔totalPages). totalPages 미확정 시 매칭 페이지 사이만 순회.
  const handlePrevPage = () => {
    if (totalPages > 0) {
      setCurrentPage((prev) => (prev > 1 ? prev - 1 : totalPages));
      return;
    }
    if (sortedMatched.length === 0) return;
    setCurrentPage((prev) => {
      const idx = sortedMatched.indexOf(prev);
      if (idx === -1) return sortedMatched[0];
      return idx > 0
        ? sortedMatched[idx - 1]
        : sortedMatched[sortedMatched.length - 1];
    });
  };

  const handleNextPage = () => {
    if (totalPages > 0) {
      setCurrentPage((prev) => (prev < totalPages ? prev + 1 : 1));
      return;
    }
    if (sortedMatched.length === 0) return;
    setCurrentPage((prev) => {
      const idx = sortedMatched.indexOf(prev);
      if (idx === -1) return sortedMatched[0];
      return idx < sortedMatched.length - 1
        ? sortedMatched[idx + 1]
        : sortedMatched[0];
    });
  };

  // 검색에 매칭된 페이지인지 표시 (UI 강조용)
  const isMatchedPage = relatedPages.includes(currentPage);

  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
      className="fixed inset-0 z-[100] flex items-center justify-center p-4 sm:p-10"
    >
      <div
        className="absolute inset-0 bg-on-surface/40 backdrop-blur-md"
        onClick={onClose}
      />

      <AnimatePresence>
        {isExpanded && (
          <motion.div
            initial={{ opacity: 0, scale: 0.9 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.9 }}
            className="fixed inset-0 z-[150] bg-black/95 flex items-center justify-center p-2 md:p-4"
            onClick={() => setIsExpanded(false)}
          >
            <div
              className="relative w-full h-full flex items-center justify-center"
              onClick={(e) => e.stopPropagation()}
            >
              <img
                src={pageThumb}
                alt={result.title}
                referrerPolicy="no-referrer"
                className="w-full h-full object-contain shadow-2xl scale-[0.98]"
              />
              <button
                onClick={(e) => {
                  e.stopPropagation();
                  setIsExpanded(false);
                }}
                className="absolute top-0 right-0 p-3 bg-white/10 hover:bg-white/20 text-white rounded-full transition-colors"
                title="축소 (ESC)"
              >
                <Minimize2 size={32} />
              </button>

              {/* 좌측 이전 페이지 버튼 */}
              <button
                onClick={(e) => {
                  e.stopPropagation();
                  handlePrevPage();
                }}
                className="absolute left-4 top-1/2 -translate-y-1/2 p-4 bg-white/10 hover:bg-white/20 text-white rounded-full transition-colors backdrop-blur-sm"
                title="이전 페이지 (←)"
              >
                <ChevronLeft size={36} />
              </button>

              {/* 우측 다음 페이지 버튼 */}
              <button
                onClick={(e) => {
                  e.stopPropagation();
                  handleNextPage();
                }}
                className="absolute right-4 top-1/2 -translate-y-1/2 p-4 bg-white/10 hover:bg-white/20 text-white rounded-full transition-colors backdrop-blur-sm"
                title="다음 페이지 (→)"
              >
                <ChevronRight size={36} />
              </button>

              <div className="absolute bottom-4 left-1/2 -translate-x-1/2 bg-black/60 text-white px-6 py-2 rounded-full border border-white/10 backdrop-blur-md flex items-center gap-4">
                <span className="text-sm font-bold uppercase tracking-widest">
                  {result.title}
                </span>
                <div className="w-px h-4 bg-white/20" />
                <span className="text-sm font-bold">
                  Page : {currentPage}
                  {totalPages > 0 && ` / ${totalPages}`}
                </span>
              </div>
            </div>
          </motion.div>
        )}
      </AnimatePresence>

      <motion.div
        initial={{ opacity: 0, scale: 0.95, y: 20 }}
        animate={{ opacity: 1, scale: 1, y: 0 }}
        exit={{ opacity: 0, scale: 0.95, y: 20 }}
        className="relative bg-surface-container-lowest w-full max-w-6xl overflow-hidden rounded-lg shadow-2xl flex flex-col lg:flex-row border border-outline-variant"
      >
        <button
          onClick={onClose}
          className="absolute top-6 right-6 z-20 p-2 rounded-full bg-black/40 text-white hover:bg-black/60 transition-colors shadow-lg"
        >
          <X size={24} />
        </button>

        {/* 좌측: 페이지 이미지 */}
        <div className="w-full lg:w-[60%] flex flex-col bg-surface-container-highest group">
          <div className="w-full aspect-[4/3] overflow-hidden relative bg-surface-container-low">
            <img
              src={pageThumb}
              alt={`${result.title} - p.${currentPage}`}
              referrerPolicy="no-referrer"
              onError={() => setImgError(true)}
              className="w-full h-full object-contain"
            />
            {imgError && (
              <div className="absolute inset-0 flex flex-col items-center justify-center bg-surface-container-high text-tertiary gap-2">
                <p className="text-sm font-bold">
                  페이지 {currentPage} 이미지를 불러올 수 없습니다
                </p>
                <p className="text-xs text-tertiary/70">
                  존재하지 않거나 접근 불가한 페이지일 수 있습니다
                </p>
              </div>
            )}
            <button
              onClick={() => setIsExpanded(true)}
              className="absolute top-4 right-4 px-4 py-3 rounded-sm bg-black/50 text-white opacity-0 group-hover:opacity-100 transition-all hover:bg-[#E1251B] flex items-center gap-2.5 shadow-lg backdrop-blur-sm border border-white/10"
              title="전체화면 확대"
            >
              <Maximize2 size={20} />
              <span className="text-[13px] font-bold">전체화면 확대</span>
            </button>

            {/* 검색 매칭 페이지 표시 */}
            {isMatchedPage && (
              <div className="absolute top-4 left-4 bg-primary/90 text-white px-2.5 py-1 rounded-sm text-[10px] font-bold uppercase tracking-widest shadow-lg">
                Matched Page
              </div>
            )}
          </div>

          <div className="bg-surface-container-highest p-4 flex items-center justify-between border-t border-outline-variant/10">
            <div className="flex items-center gap-1.5 px-2">
              <div className="w-48 h-1.5 bg-outline-variant/30 rounded-full overflow-hidden">
                <div
                  className="h-full bg-primary transition-all duration-300"
                  style={{
                    width: `${
                      totalPages > 0
                        ? (currentPage / totalPages) * 100
                        : sortedMatched.length > 0
                          ? ((Math.max(0, sortedMatched.indexOf(currentPage)) +
                              1) /
                              sortedMatched.length) *
                            100
                          : 0
                    }%`,
                  }}
                />
              </div>
            </div>
            <div className="flex items-center gap-6 bg-black/80 text-white px-6 py-2 rounded-sm border border-white/10 shadow-lg">
              <button
                onClick={handlePrevPage}
                className="p-1 hover:text-primary transition-colors cursor-pointer"
                title="이전 매칭 페이지"
              >
                <ChevronLeft size={20} />
              </button>
              <span className="text-[13px] font-headline font-bold uppercase tracking-widest min-w-[160px] text-center">
                PAGE {currentPage}
                {totalPages > 0 && <> / {totalPages}</>}
                <span className="text-tertiary"> · 관련 {matchedCount}건</span>
              </span>
              <button
                onClick={handleNextPage}
                className="p-1 hover:text-primary transition-colors cursor-pointer"
                title="다음 페이지"
              >
                <ChevronRight size={20} />
              </button>
            </div>
          </div>
        </div>

        {/* 우측: 콘텐츠 */}
        <div className="w-full lg:w-[40%] p-6 flex flex-col bg-surface-container-lowest border-l border-outline-variant/30">
          <div className="mb-6 pb-4 border-b border-outline-variant/10">
            <span className="text-[10px] font-bold text-tertiary uppercase tracking-widest block mb-2">
              검색어
            </span>
            <div className="text-xl font-headline font-black text-[#E1251B] bg-[#E1251B]/5 px-4 py-3 rounded-sm border-l-4 border-[#E1251B] shadow-sm">
              "{query}"
            </div>
          </div>

          <h2 className="text-2xl font-headline font-black text-on-surface mb-6 leading-tight tracking-tight">
            {result.title}
          </h2>

          <div className="space-y-4 flex-grow flex flex-col min-h-0">
            <div className="flex items-center justify-between pb-4 border-b border-outline-variant/20">
              <div className="flex items-start gap-3">
                <div className="w-1 bg-[#E1251B] h-8 rounded-full" />
                <div>
                  <p className="text-[10px] font-bold text-tertiary uppercase mb-0.5 tracking-wider">
                    선택 페이지
                  </p>
                  <p className="text-xl font-headline font-black text-on-surface">
                    Page : {currentPage}
                    {totalPages > 0 && (
                      <span className="text-base font-bold text-tertiary">
                        {" / "}
                        {totalPages}
                      </span>
                    )}
                    <span className="text-xs font-normal text-tertiary ml-1">
                      (관련 {matchedCount}건)
                    </span>
                  </p>
                </div>
              </div>
              <div className="text-right">
                <p className="text-[10px] font-bold text-tertiary uppercase mb-0.5 tracking-wider">
                  관련 페이지
                </p>
                <div className="flex flex-wrap gap-1.5 justify-end max-w-[180px]">
                  {relatedPages.map((p, i) => (
                    <button
                      key={i}
                      onClick={() => setCurrentPage(p)}
                      className={`text-[10px] font-bold px-1.5 py-0.5 rounded transition-colors ${
                        currentPage === p
                          ? "bg-primary text-white font-black scale-110"
                          : "bg-surface-container-high text-tertiary hover:bg-surface-container-highest"
                      }`}
                      title={`페이지 ${p}로 이동 (검색 매칭)`}
                    >
                      {p}
                    </button>
                  ))}
                </div>
              </div>
            </div>

            <div className="bg-surface-container-low p-5 rounded-sm border border-outline-variant/20 shadow-sm flex-grow overflow-y-auto">
              <p className="text-[10px] font-bold text-tertiary uppercase mb-3 tracking-wider flex items-center gap-2">
                <Sparkles size={12} className="text-primary" />
                AI 종합 답변 (LLM Synthesis)
              </p>

              {vlmLoading && (
                <div className="flex items-center gap-2 text-[12px] text-tertiary py-4">
                  <Loader2 size={14} className="animate-spin text-primary" />
                  <span>문서 페이지를 종합하여 답변을 생성 중입니다...</span>
                </div>
              )}

              {vlmError && !vlmLoading && (
                <p className="text-[12px] text-[#B00020] py-2">
                  답변 생성 중 오류가 발생했습니다. 좌측 페이지 이미지를
                  참고하세요.
                </p>
              )}

              {vlmAnswer && !vlmLoading && (
                <div className="text-[13px] text-on-surface leading-relaxed whitespace-pre-line">
                  {vlmAnswer}
                </div>
              )}

              {!vlmAnswer && !vlmLoading && !vlmError && (
                <p className="text-[12px] text-tertiary italic">
                  답변이 생성되지 않았습니다. 좌측 페이지 이미지를 참고하세요.
                </p>
              )}
            </div>
          </div>

          <div className="mt-6 pt-6 border-t border-outline-variant/20 flex flex-row gap-2">
            <button
              onClick={() => openExternal(systemUrl)}
              disabled={!systemUrl}
              className="flex-grow py-2.5 bg-surface-container-high text-on-surface font-headline font-bold text-[11px] rounded-sm hover:bg-surface-container-highest transition-all flex items-center justify-center gap-2 uppercase tracking-wider border border-outline-variant/30 disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-surface-container-high"
              title={systemUrl ? "QMS 시스템에서 열기" : "시스템 링크 없음"}
            >
              <ExternalLink size={16} /> <span>시스템 링크</span>
            </button>
            <button
              onClick={() => {
                if (downloadUrl) onDownloadLog?.(result, downloadUrl);
                openExternal(downloadUrl);
              }}
              disabled={!downloadUrl}
              className="flex-grow py-2.5 bg-[#E1251B] text-white font-headline font-bold text-[11px] rounded-sm hover:bg-[#B00020] shadow-sm transition-all flex items-center justify-center gap-2 uppercase tracking-wider disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-[#E1251B]"
              title={downloadUrl ? "원본 문서 다운로드" : "다운로드 링크 없음"}
            >
              <Download size={16} /> <span>다운로드</span>
            </button>
          </div>
        </div>
      </motion.div>
    </motion.div>
  );
};

// ─────────────────────────────────────────────────────────────
// App
// ─────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────
// AppContent — 기존 App 로직 (인증 게이팅 후 렌더)
// ─────────────────────────────────────────────────────────────
function AppContent() {
  const { user } = useAuth();
  const [view, setView] = useState<ViewState>("search");
  const [query, setQuery] = useState("");
  const [showFilters, setShowFilters] = useState(false);
  const [showSidebarFilters, setShowSidebarFilters] = useState(false);
  const [isSearching, setIsSearching] = useState(false);
  const [showMore, setShowMore] = useState(false);
  const [results, setResults] = useState<SearchResult[]>([]);
  const [meta, setMeta] = useState<SearchMeta | null>(null);
  const [sessions, setSessions] = useState<SearchSession[]>([]);
  const [selectedResult, setSelectedResult] = useState<SearchResult | null>(
    null,
  );
  const [region, setRegion] = useState<string>("전체");
  const [product, setProduct] = useState<string>("전체");

  // ─── 활동 로그 전송 유틸 ───
  const sendLog = useCallback(
    (type: "detail" | "download", payload: Record<string, string>) => {
      fetch(`/api/log/${type}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ username: user?.username || "anonymous", ...payload }),
      }).catch(() => {}); // fire-and-forget
    },
    [user],
  );

  // 카드 클릭 → 상세 모달 열기 + 로그
  const handleSelectResult = useCallback(
    (result: SearchResult) => {
      setSelectedResult(result);
      sendLog("detail", {
        docId: result.docId || result.id,
        query,
      });
    },
    [query, sendLog],
  );

  // 다운로드 클릭 로그 (ResultCard / DetailModal에서 호출)
  const handleDownloadLog = useCallback(
    (result: SearchResult, downloadUrl: string) => {
      sendLog("download", {
        docId: result.docId || result.id,
        query,
        downloadUrl,
      });
    },
    [query, sendLog],
  );

  const handleSearch = async () => {
    if (!query.trim()) return;

    setIsSearching(true);
    setShowMore(false);

    if (view !== "results") {
      setView("results");
    }

    try {
      const res = await fetch(`/api/search`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          query,
          region,
          product,
          topK: 10,
          username: user?.username || "anonymous",
        }),
      });
      if (!res.ok) {
        const text = await res.text();
        throw new Error(`Search failed [${res.status}]: ${text}`);
      }
      const data = await res.json();
      setResults(data.results || []);
      setMeta(data.meta);

      const newSession: SearchSession = {
        id:
          typeof crypto !== "undefined" && "randomUUID" in crypto
            ? crypto.randomUUID()
            : `s-${Date.now()}-${Math.random().toString(36).slice(2)}`,
        query: query,
        timestamp: Date.now(),
        results: data.results || [],
        meta: data.meta,
      };
      setSessions((prev) => [newSession, ...prev]);
    } catch (error) {
      console.error("Search failed", error);
      setResults([]);
    } finally {
      setIsSearching(false);
    }
  };

  const handleExportBatch = () => {
    if (results.length === 0) return;
    const dataStr =
      "data:text/json;charset=utf-8," +
      encodeURIComponent(JSON.stringify(results, null, 2));
    const downloadAnchorNode = document.createElement("a");
    downloadAnchorNode.setAttribute("href", dataStr);
    downloadAnchorNode.setAttribute(
      "download",
      `QP_Search_Results_${Date.now()}.json`,
    );
    document.body.appendChild(downloadAnchorNode);
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
  };

  const handleRestoreSession = (session: SearchSession) => {
    setQuery(session.query);
    setResults(session.results);
    setMeta(session.meta);
    setShowMore(false);
    setView("results");
  };

  const handleNewSearch = () => {
    setView("search");
    setQuery("");
    setResults([]);
    setShowMore(false);
  };

  return (
    <div className="min-h-screen flex flex-col bg-surface overflow-x-hidden">
      <Navbar onHome={handleNewSearch} />

      <main className="grow flex overflow-hidden">
        {view === "results" && (
          <aside className="w-60 shrink-0 border-r border-outline-variant bg-surface-container-low p-4 overflow-y-auto hidden lg:block">
            <button
              onClick={handleNewSearch}
              className="w-full mb-8 py-3 primary-gradient text-white font-headline font-bold text-sm rounded-sm flex items-center justify-center gap-2 hover:shadow-lg transition-shadow"
            >
              <Plus size={18} /> 새 검색
            </button>

            <div className="space-y-8">
              <div>
                <div className="flex items-center justify-between mb-5">
                  <h4 className="text-[15px] font-bold text-on-surface uppercase tracking-tight">
                    검색 조건 설정
                  </h4>
                  <button
                    onClick={() => setShowSidebarFilters(!showSidebarFilters)}
                    className={`flex items-center gap-2 text-[12px] font-bold font-[Arial] leading-3.5 [border-style:ridge] transition-all duration-300 px-4 py-2 rounded-[4px] uppercase tracking-widest shadow-sm hover:shadow-md ${
                      showSidebarFilters
                        ? "bg-[#E5E5E5] text-on-surface border-[#D1D1D1]"
                        : "bg-white text-on-surface-variant border-outline-variant hover:bg-[#F5F5F5]"
                    } border`}
                  >
                    <Filter
                      size={16}
                      className={
                        showSidebarFilters ? "text-on-surface" : "text-tertiary"
                      }
                    />
                    조건
                    <ChevronDown
                      size={14}
                      className={`transition-transform duration-300 ${
                        showSidebarFilters ? "rotate-180" : ""
                      }`}
                    />
                  </button>
                </div>

                <AnimatePresence>
                  {showSidebarFilters && (
                    <motion.div
                      initial={{ opacity: 0, height: 0 }}
                      animate={{ opacity: 1, height: "auto" }}
                      exit={{ opacity: 0, height: 0 }}
                      transition={{ duration: 0.3 }}
                      className="overflow-hidden border border-[#E1251B]/20 p-4 bg-[#FFF5F5]/50 rounded-[4px]"
                    >
                      <div className="space-y-6">
                        <div>
                          <label className="text-[14px] font-bold text-on-surface uppercase mb-3 flex items-center gap-2">
                            <Globe size={16} className="text-[#8B1A1A]" />
                            공장정보
                          </label>
                          <select
                            value={region}
                            onChange={(e) => setRegion(e.target.value)}
                            className="w-full bg-white border border-outline-variant text-[12px] rounded-sm p-2 focus:ring-1 focus:ring-[#B00020] focus:border-[#B00020] outline-none"
                          >
                            {REGION_OPTIONS.map((opt) => (
                              <option key={opt} value={opt}>
                                {opt}
                              </option>
                            ))}
                          </select>
                        </div>
                        <div>
                          <label className="text-[14px] font-bold text-on-surface uppercase mb-3 flex items-center gap-2">
                            <Cpu size={16} className="text-[#8B1A1A]" />
                            제품 정보
                          </label>
                          <select
                            value={product}
                            onChange={(e) => setProduct(e.target.value)}
                            className="w-full bg-white border border-outline-variant text-[12px] rounded-sm p-2 focus:ring-1 focus:ring-[#B00020] focus:border-[#B00020] outline-none"
                          >
                            {PRODUCT_OPTIONS.map((opt) => (
                              <option key={opt} value={opt}>
                                {opt}
                              </option>
                            ))}
                          </select>
                        </div>
                      </div>
                    </motion.div>
                  )}
                </AnimatePresence>
              </div>

              <div className="pt-10 border-t border-outline-variant mt-10">
                <h4 className="text-[10px] font-bold text-tertiary uppercase tracking-widest mb-4">
                  이전 검색 기록
                </h4>
                <div className="space-y-4">
                  {sessions.length === 0 && (
                    <p className="text-[10px] text-tertiary/60 italic">
                      이전 기록이 존재하지 않음
                    </p>
                  )}
                  {sessions.map((session) => (
                    <div
                      key={session.id}
                      onClick={() => handleRestoreSession(session)}
                      className={`group cursor-pointer p-3 rounded-sm transition-all duration-300 ${
                        query === session.query
                          ? "bg-surface-container-highest border-l-2 border-primary"
                          : "hover:bg-surface-container-highest/50 border-l border-transparent"
                      }`}
                    >
                      <p className="text-xs font-bold text-on-surface line-clamp-1 mb-1 group-hover:text-primary transition-colors">
                        {session.query}
                      </p>
                      <div className="flex justify-between items-center gap-4">
                        <span className="shrink-0 text-[9px] text-tertiary">
                          {new Date(session.timestamp).toLocaleTimeString([], {
                            hour: "2-digit",
                            minute: "2-digit",
                          })}
                        </span>
                        <span
                          className="text-[9px] text-tertiary font-bold line-clamp-1 flex-grow text-right"
                          title={
                            [...session.results].sort(
                              (a, b) => b.relevance - a.relevance,
                            )[0]?.title
                          }
                        >
                          {
                            [...session.results].sort(
                              (a, b) => b.relevance - a.relevance,
                            )[0]?.title
                          }
                        </span>
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            </div>
          </aside>
        )}

        <section className="flex-grow min-w-0 overflow-y-auto bg-surface-container-low/30 scroll-smooth pb-32">
          <AnimatePresence mode="wait">
            {view === "search" ? (
              <motion.div
                key="search-view"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0, y: -20 }}
                className="max-w-4xl mx-auto pt-24 pb-32 px-6 h-full"
              >
                <div className="absolute top-0 right-0 w-[600px] h-[600px] bg-surface-container-low rounded-full filter blur-3xl opacity-50 -translate-y-1/2 translate-x-1/3 pointer-events-none" />

                <div className="text-center mb-16 relative z-10">
                  <motion.h2
                    initial={{ opacity: 0, y: 20 }}
                    animate={{ opacity: 1, y: 0 }}
                    transition={{ delay: 0.2 }}
                    className="font-headline font-extrabold text-[3.5rem] leading-tight tracking-[-0.02em] text-on-surface mb-3 items-baseline"
                  >
                    <span className="text-[#black] font-bold ml-3">
                      지침서 검색 시스템
                    </span>
                    <span className="text-[#E1251B]/90"> (SNAP)</span>
                  </motion.h2>

                <motion.p
                    initial={{ opacity: 0, y: 25 }}
                    animate={{ opacity: 1, y: 0 }}
                    transition={{ delay: 0.25 }}
                    className="font-sans text-2xl  text-on-surface-variant/70 tracking-widest mb-3"
                  >
                    <span className="text-[#E1251B]/90 font-bold">
                      Standard Navigation & Preview
                    </span>
                  </motion.p>

                  <motion.p
                    initial={{ opacity: 0, y: 30 }}
                    animate={{ opacity: 1, y: 0 }}
                    transition={{ delay: 0.3 }}
                    className="font-bold text-lg text-on-surface-variant max-w-2xl mx-auto leading-relaxed"
                  >
                    SK 실트론의 메뉴얼 및 지침서, 행동 규칙 등을 검색해주세요.
                  </motion.p>
                </div>

                <div className="flex justify-center mb-4 relative z-10">
                  <motion.p
                    initial={{ opacity: 0 }}
                    animate={{ opacity: 1 }}
                    transition={{ delay: 0.4 }}
                    className="text-blue-600 text-[17px] font-normal tracking-tight"
                  >
                    “제품Size와 공정명을 함께 기재하면 정보탐색의 정확도를 올릴
                    수 있습니다”
                  </motion.p>
                </div>

                <div className="bg-surface-container-lowest shadow-ambient rounded-xl p-4 mb-4 relative z-10">
                  <div className="flex items-center bg-surface-container-highest rounded-lg px-4 py-3 focus-within:ring-2 focus-within:ring-primary/20 transition-all">
                    <Search className="text-tertiary mr-3" size={24} />
                    <input
                      type="text"
                      value={query}
                      onChange={(e) => setQuery(e.target.value)}
                      onKeyDown={(e) => e.key === "Enter" && handleSearch()}
                      placeholder="예시 : 300mm EPI 공정 Reactor 점검 에서 wafer curling 제어 프로세스는?"
                      className="w-full bg-transparent border-none text-on-surface font-sans text-lg focus:ring-0 placeholder:text-tertiary/60"
                    />
                    <button
                      onClick={handleSearch}
                      disabled={isSearching}
                      className="ml-2 px-8 py-3 font-headline font-bold text-sm tracking-wide bg-[#E1251B] hover:bg-[#C01F18] text-white rounded-[4px] transition-all duration-300 hover:scale-[1.02] active:scale-95 flex items-center gap-2 whitespace-nowrap shrink-0"
                    >
                      {isSearching ? (
                        <Loader2 className="animate-spin" size={18} />
                      ) : null}
                      검 색
                    </button>
                  </div>
                </div>

                <div className="flex justify-end mb-8 relative z-10">
                  <button
                    onClick={() => setShowFilters(!showFilters)}
                    className={`flex items-center gap-2 text-[12px] font-bold font-[Arial] leading-[15px] [border-style:ridge] transition-all duration-300 px-5 py-2.5 rounded-[4px] uppercase tracking-widest shadow-sm hover:shadow-md border ${
                      showFilters
                        ? "bg-[#E5E5E5] text-on-surface border-[#D1D1D1]"
                        : "bg-white text-on-surface-variant border-outline-variant hover:bg-[#F5F5F5]"
                    }`}
                  >
                    <Filter
                      size={18}
                      className={
                        showFilters ? "text-on-surface" : "text-tertiary"
                      }
                    />
                    검색조건
                    <ChevronDown
                      size={14}
                      className={`transition-transform duration-300 ${
                        showFilters ? "rotate-180" : ""
                      }`}
                    />
                  </button>
                </div>

                <AnimatePresence>
                  {showFilters && (
                    <motion.div
                      initial={{ opacity: 0, y: -10, height: 0 }}
                      animate={{ opacity: 1, y: 0, height: "auto" }}
                      exit={{ opacity: 0, y: -10, height: 0 }}
                      transition={{ duration: 0.3 }}
                      className="overflow-hidden border border-[#E1251B]/20 p-4 bg-[#FFF5F5]/50 rounded-[4px]"
                    >
                      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                        <FilterCard
                          icon={Globe}
                          title="공장정보"
                          options={REGION_OPTIONS}
                          value={region}
                          onChange={setRegion}
                        />
                        <FilterCard
                          icon={Cpu}
                          title="제품 정보"
                          options={PRODUCT_OPTIONS}
                          value={product}
                          onChange={setProduct}
                        />
                      </div>
                    </motion.div>
                  )}
                </AnimatePresence>
              </motion.div>
            ) : (
              <motion.div
                key="results-view"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                className="flex h-full overflow-hidden"
              >
                <section className="flex-grow min-w-0 overflow-y-auto bg-surface-container-low/30 scroll-smooth pb-32">
                  <div className="sticky top-0 z-20 bg-surface-container-lowest/80 backdrop-blur-md border-b border-outline-variant px-8 py-2 flex items-center justify-between">
                    <div className="flex items-center gap-4 bg-surface-container-highest px-4 py-1.5 rounded-lg w-full max-w-lg">
                      <Search size={16} className="text-tertiary" />
                      <input
                        type="text"
                        value={query}
                        onChange={(e) => setQuery(e.target.value)}
                        onKeyDown={(e) => e.key === "Enter" && handleSearch()}
                        className="bg-transparent border-none text-sm w-full focus:ring-0"
                      />
                    </div>
                    <div className="flex items-center gap-4">
                      <HelpCircle
                        className="text-tertiary cursor-pointer"
                        size={20}
                      />
                    </div>
                  </div>

                  <div className="p-6 pt-4">
                    <div className="flex justify-between items-center mb-4 flex-wrap gap-3">
                      <div className="flex items-center gap-4 flex-wrap">
                        {isSearching ? (
                          <div className="flex items-center gap-3">
                            <Loader2
                              size={24}
                              className="text-primary animate-spin"
                            />
                            <h2 className="text-xl font-headline font-bold text-on-surface">
                              검색중...
                            </h2>
                          </div>
                        ) : (
                          <div className="flex items-center gap-3 flex-wrap">
                            <h2 className="text-xl font-headline font-bold text-on-surface flex items-center gap-3">
                              검색 결과
                              <span className="bg-primary/10 text-primary px-3 py-0.5 rounded-full text-[10px] uppercase tracking-wide">
                                {query}
                              </span>
                            </h2>
                            <span className="text-[10px] text-tertiary">
                              Found {meta?.total} relevant docs
                            </span>

                            {meta && (
                              <div className="flex items-center gap-2 ml-2 pl-3 border-l border-outline-variant/40 flex-wrap">
                                <span className="flex items-center gap-1.5 text-[10px] bg-surface-container-high px-2 py-1 rounded-sm">
                                  <span className="text-tertiary uppercase tracking-wider">
                                    Complexity
                                  </span>
                                  <span className="font-bold text-on-surface">
                                    {meta.complexity}
                                  </span>
                                </span>
                                <span className="flex items-center gap-1.5 text-[10px] bg-surface-container-high px-2 py-1 rounded-sm">
                                  <span className="text-tertiary uppercase tracking-wider">
                                    Mode
                                  </span>
                                  <span className="font-bold text-on-surface">
                                    {meta.strategy}
                                  </span>
                                </span>
                                <span className="flex items-center gap-1.5 text-[10px] bg-surface-container-high px-2 py-1 rounded-sm">
                                  <span className="text-tertiary uppercase tracking-wider">
                                    Answerability
                                  </span>
                                  <div className="w-16 bg-surface-container-highest h-1.5 rounded-full overflow-hidden">
                                    <div
                                      className="bg-primary h-full transition-all duration-1000"
                                      style={{
                                        width: `${(meta.answerabilityScore || 0) * 100}%`,
                                      }}
                                    />
                                  </div>
                                  <span className="font-bold text-on-surface">
                                    {(
                                      (meta.answerabilityScore || 0) * 100
                                    ).toFixed(0)}
                                    %
                                  </span>
                                </span>
                              </div>
                            )}
                          </div>
                        )}
                      </div>
                      {!isSearching && (
                        <div className="flex items-center gap-3">
                          <button
                            onClick={handleExportBatch}
                            className="flex items-center gap-2 text-[9px] font-bold uppercase text-tertiary hover:text-primary transition-colors tracking-widest bg-surface-container-highest/20 px-3 py-1 rounded-sm border border-outline-variant/30"
                          >
                            <Share2 size={12} /> EXPORT
                          </button>
                        </div>
                      )}
                    </div>

                    {isSearching ? (
                      <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">
                        {[...Array(6)].map((_, i) => (
                          <div
                            key={i}
                            className="bg-surface-container-highest/10 rounded-sm aspect-[4/3] animate-pulse border border-outline-variant/30"
                          />
                        ))}
                      </div>
                    ) : (
                      <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">
                        {results.slice(0, showMore ? 9 : 6).map((r) => (
                          <ResultCard
                            key={r.id}
                            result={r}
                            onSelect={handleSelectResult}
                            onDownloadLog={handleDownloadLog}
                          />
                        ))}
                      </div>
                    )}

                    {!isSearching && results.length > 6 && (
                      <div className="mt-8 flex justify-center">
                        <button
                          onClick={() => setShowMore(!showMore)}
                          className="flex items-center gap-2 px-6 py-1.5 border border-outline-variant rounded-[4px] text-[11px] font-bold text-tertiary hover:bg-surface-container-highest/30 transition-all uppercase tracking-widest"
                        >
                          {showMore ? "접기" : "더보기 (+3)"}
                          <ChevronDown
                            size={14}
                            className={showMore ? "rotate-180" : ""}
                          />
                        </button>
                      </div>
                    )}

                    {!isSearching && results.length > 0 && (
                      <div className="mt-10 text-center py-6 border-t border-outline-variant/30">
                        <p className="text-xs text-tertiary">
                          All {results.length} results have been synthesized by
                          the SNAP engine.
                        </p>
                        <button
                          onClick={handleNewSearch}
                          className="mt-4 text-primary font-bold text-xs uppercase tracking-widest hover:underline"
                        >
                          New Search
                        </button>
                      </div>
                    )}
                  </div>
                </section>
              </motion.div>
            )}
          </AnimatePresence>

          <AnimatePresence>
            {selectedResult && (
              <DetailModal
                result={selectedResult}
                query={query}
                onClose={() => setSelectedResult(null)}
                onDownloadLog={handleDownloadLog}
              />
            )}
          </AnimatePresence>
        </section>
      </main>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// App — AuthProvider + 로그인 게이팅 래퍼
// ─────────────────────────────────────────────────────────────
export default function App() {
  return (
    <AuthProvider>
      <AuthGate />
    </AuthProvider>
  );
}

function AuthGate() {
  const { user, isLoading } = useAuth();

  // 초기 로딩 중 (sessionStorage 복원 대기)
  if (isLoading) {
    return (
      <div className="min-h-screen bg-surface flex items-center justify-center">
        <Loader2 size={32} className="animate-spin text-[#E1251B]" />
      </div>
    );
  }

  // 비로그인 → 로그인 화면
  if (!user) {
    return <LoginScreen />;
  }

  // 로그인됨 → SNAP UI
  return <AppContent />;
}