/* ============================================================
   app.jsx — 루트: 서버 연동 스토어 / 라우팅 / 폴링 동기화
   학생은 PIN+이름으로 입장. localStorage에 pin·name 저장해 재접속 지원.
   ============================================================ */
const RECONNECT_KEY = "guro_gwonyeok_conn_v4";
const TEACHER_KEY = "guro_teacher_token_v1";
const POLL_MS = 2000;

function loadReconnect() {
  try {
    const raw = localStorage.getItem(RECONNECT_KEY);
    if (raw) return JSON.parse(raw);
  } catch (e) {}
  return {};
}

/* ---- URL 라우팅: 경로 ↔ 화면 매핑 (뒤로가기·새로고침·링크 공유 대응) ---- */
function pathToState(pathname) {
  const p = (pathname || "/").replace(/\/+$/, "") || "/";
  if (p === "/") return { screen: "home", role: null };
  if (p === "/teacher") return { screen: "tlist", role: "teacher" };
  if (p === "/teacher/new") return { screen: "T1", role: "teacher" };
  if (p === "/teacher/intro") return { screen: "intro", role: "teacher" };
  if (p === "/teacher/dashboard") return { screen: "T2", role: "teacher" };
  if (p === "/teacher/present") return { screen: "T3", role: "teacher" };
  let m = p.match(/^\/teacher\/class\/([A-Za-z0-9]+)\/present$/);
  if (m) return { screen: "T3", role: "teacher", code: m[1].toUpperCase() };
  m = p.match(/^\/teacher\/class\/([A-Za-z0-9]+)$/);
  if (m) return { screen: "T2", role: "teacher", code: m[1].toUpperCase() };
  if (p === "/join") return { screen: "S1", role: "student" };
  if (p === "/play") return { screen: "S2", role: "student" };
  if (p === "/play/opinion") return { screen: "S4", role: "student" };
  if (p === "/play/done") return { screen: "S5", role: "student" };
  return { screen: "home", role: null };
}
function stateToPath(screen, code) {
  switch (screen) {
    case "home": return "/";
    case "tlist": return "/teacher";
    case "T1": return "/teacher/new";
    case "intro": return "/teacher/intro";
    case "T2": return code ? "/teacher/class/" + code : "/teacher/dashboard";
    case "T3": return code ? "/teacher/class/" + code + "/present" : "/teacher/present";
    case "S1": return "/join";
    case "S2": return "/play";
    case "S4": return "/play/opinion";
    case "S5": return "/play/done";
    default: return "/";
  }
}

function App() {
  const rc = loadReconnect();
  const initial = pathToState(window.location.pathname); // URL 우선
  const [role, setRoleState] = useState(initial.role || rc.role || null);
  const [screen, setScreen] = useState(initial.screen || rc.screen || "home");
  const [code, setCode] = useState(initial.code || null);  // 세션코드 (URL의 /teacher/class/:code 또는 입장 후 설정)
  const [myGroupId, setMyGroupId] = useState(rc.myGroupId || null);
  const [myPin, setMyPin] = useState(rc.pin || null);      // 학생 모둠 PIN
  const [myName, setMyName] = useState(rc.name || null);   // 학생 대표참여자 이름
  const [session, setSession] = useState(null);
  const [mySessionsList, setMySessionsList] = useState(null); // 교사 내 학급 목록
  const skipPush = useRef(false);                          // popstate로 인한 변경이면 pushState 생략
  const [data, setData] = useState({
    responses: {}, opinions: {}, participants: {},
    escapeAnswers: {}, groupVariants: {},
  });
  const [conn, setConn] = useState(code ? "connecting" : "idle");

  const [authConfig, setAuthConfig] = useState({ loginRequired: false, clientId: "" });
  const [authToken, setAuthTokenState] = useState(() => {
    try { return localStorage.getItem(TEACHER_KEY) || null; } catch (e) { return null; }
  });
  const [authProfile, setAuthProfile] = useState(null);

  const applyToken = (token, profile) => {
    window.API.setToken(token);
    setAuthTokenState(token);
    setAuthProfile(profile || null);
    try {
      if (token) localStorage.setItem(TEACHER_KEY, token);
      else localStorage.removeItem(TEACHER_KEY);
    } catch (e) {}
  };

  // 부팅: 공개 설정 로드 + 저장된 교사 토큰 검증 + 학생 자동 재접속
  useEffect(() => {
    let alive = true;
    window.API.setToken(authToken);
    (async () => {
      try {
        const cfg = await window.API.getConfig();
        if (alive) setAuthConfig({ loginRequired: !!cfg.loginRequired, clientId: cfg.googleClientId || "" });
      } catch (e) {}

      if (authToken) {
        try {
          const me = await window.API.me();
          if (alive) setAuthProfile({ email: me.email, name: me.name, picture: me.picture });
        } catch (e) {
          if (alive) applyToken(null, null);
        }
      }

      // 저장된 학생 PIN+이름으로 자동 재접속
      if (alive && rc.pin && rc.name && !rc.code) {
        try {
          const r = await window.API.joinByPin(rc.pin, rc.name);
          if (alive) {
            setCode(r.sessionCode);
            setMyGroupId(r.groupId);
            setMyPin(rc.pin);
            setMyName(rc.name);
            setConn("ok");
            setScreen(rc.screen && rc.screen.startsWith("S") ? rc.screen : "S2");
          }
        } catch (e) {
          if (alive) { localStorage.removeItem(RECONNECT_KEY); setScreen("home"); }
        }
      }
    })();
    return () => { alive = false; };
  }, []);

  // 재접속 정보 저장 — code는 저장하지 않음 (PIN으로만 재접속)
  // code를 저장하면 !rc.code 조건이 false가 되어 자동재접속 분기가 우회됨
  useEffect(() => {
    try {
      localStorage.setItem(RECONNECT_KEY, JSON.stringify({
        role, screen, myGroupId, pin: myPin, name: myName,
      }));
    } catch (e) {}
  }, [role, screen, myGroupId, myPin, myName]);

  // URL 동기화: 화면/코드가 바뀌면 주소창에 반영 (popstate로 인한 변경은 push 생략)
  useEffect(() => {
    const target = stateToPath(screen, code);
    if (window.location.pathname !== target) {
      try {
        if (skipPush.current) window.history.replaceState({}, "", target);
        else window.history.pushState({}, "", target);
      } catch (e) {}
    }
    skipPush.current = false;
  }, [screen, code]);

  // 브라우저 뒤로/앞으로: 주소 → 화면 복원
  useEffect(() => {
    const onPop = () => {
      const st = pathToState(window.location.pathname);
      skipPush.current = true;          // 이 변경은 URL을 다시 push하지 않음
      setRoleState(st.role);
      setScreen(st.screen);
      if (st.code) setCode(st.code);    // 학급 코드가 경로에 있으면 그 학급 로드(폴링이 처리)
    };
    window.addEventListener("popstate", onPop);
    return () => window.removeEventListener("popstate", onPop);
  }, []);

  // 폴링: code가 있으면 2초마다 서버 상태 동기화
  useEffect(() => {
    if (!code) { setSession(null); setData({ responses: {}, opinions: {}, participants: {}, escapeAnswers: {}, groupVariants: {} }); return; }
    let alive = true;
    const tick = async () => {
      try {
        const st = await window.API.getSession(code);
        if (!alive) return;
        setSession(st.session);
        setData({
          responses: st.responses || {},
          opinions: st.opinions || {},
          participants: st.participants || {},
          escapeAnswers: st.escapeAnswers || {},
          groupVariants: st.groupVariants || {},
        });
        setConn("ok");
      } catch (e) {
        if (!alive) return;
        if (e.status === 404) setConn("expired");
        else setConn("error");
      }
    };
    tick();
    const id = setInterval(tick, POLL_MS);
    return () => { alive = false; clearInterval(id); };
  }, [code]);

  // ---- 파생 ----
  const groups = session ? window.makeGroups(session.groupCount) : [];
  const getResponse = (gid, pinId) => (data.responses || {})[`${gid}:${pinId}`] || null;
  const getOpinion = (gid) => (data.opinions || {})[gid] || null;

  // 이 모둠의 탈출 미션 변형(A/B/C) 반환
  const getVariant = (gid) => (data.groupVariants || {})[gid] || "A";

  // 특정 핀의 탈출 완료 여부
  const getEscapeStatus = (gid, pinId) =>
    !!((data.escapeAnswers || {})[`${gid}:${pinId}`]);

  // 탈출까지 완료된 핀 수 (진행 게이지 기준: Q1+Q2 응답 + 탈출 둘 다 완료)
  const getGroupDone = (gid) =>
    window.PINS.reduce((n, p) => {
      const responded = !!getResponse(gid, p.id);
      const escaped = getEscapeStatus(gid, p.id);
      return n + (responded && escaped ? 1 : 0);
    }, 0);

  const getParticipants = (gid) => (data.participants || {})[gid] || [];

  // 의견 작성을 열기 위한 필수 완료 미션 수 (교사 설정, 기본 5)
  const requiredCount = (session && session.requiredCount) || 5;
  // 의견 작성 가능 여부 — 완료 미션 수가 필수 수 이상
  const canWriteOpinion = (gid) => getGroupDone(gid) >= requiredCount;

  // ---- 액션 ----
  const ctx = {
    session, groups, role, myGroupId, myName, screen, conn, requiredCount,
    getResponse, getOpinion, getGroupDone, getParticipants, canWriteOpinion,
    getVariant, getEscapeStatus,

    auth: {
      loginRequired: authConfig.loginRequired,
      clientId: authConfig.clientId,
      token: authToken,
      profile: authProfile,
      isAuthed: !authConfig.loginRequired || !!authToken,
    },

    loginTeacher: async (credential) => {
      const r = await window.API.loginGoogle(credential);
      applyToken(r.token, { email: r.email, name: r.name, picture: r.picture });
      setScreen("tlist"); // 로그인 후 내 학급 목록으로
      return r;
    },
    logoutTeacher: async () => {
      try { await window.API.logout(); } catch (e) {}
      applyToken(null, null);
      setMySessionsList(null);
    },

    go: (s) => setScreen(s),
    setRole: (r) => setRoleState(r),

    // 교사: 내 학급 목록 불러오기
    mySessionsList,
    loadMySessions: async () => {
      try {
        const r = await window.API.mySessions();
        setMySessionsList(r.sessions || []);
        return r.sessions;
      } catch (e) {
        if (e.status === 401) applyToken(null, null);
        setMySessionsList([]);
        throw e;
      }
    },
    // 교사: 기존 학급 열기 (목록에서 클릭) → 대시보드로
    openClass: (classCode) => {
      setCode(classCode);
      setConn("connecting");
      setScreen("T2");
    },

    createSession: async (className, n, requiredCount) => {
      try {
        const st = await window.API.createSession(className, n, requiredCount);
        setSession(st.session);
        setData({
          responses: st.responses || {},
          opinions: st.opinions || {},
          participants: st.participants || {},
          escapeAnswers: st.escapeAnswers || {},
          groupVariants: st.groupVariants || {},
        });
        setCode(st.session.code);
        setConn("ok");
        return st.session;
      } catch (e) {
        if (e.status === 401) { applyToken(null, null); }
        throw e;
      }
    },

    // 학생: PIN + 이름으로 입장 (신규 or 재입장)
    joinByPin: async (pin, name) => {
      const r = await window.API.joinByPin(pin, name);
      const st = await window.API.getSession(r.sessionCode);
      setSession(st.session);
      setData({
        responses: st.responses || {},
        opinions: st.opinions || {},
        participants: st.participants || {},
        escapeAnswers: st.escapeAnswers || {},
        groupVariants: st.groupVariants || {},
      });
      setCode(r.sessionCode);
      setMyGroupId(r.groupId);
      setMyPin(pin);
      setMyName(name);
      setRoleState("student");
      setConn("ok");
      setScreen("S2");
    },

    saveResponse: (gid, pinId, val) => {
      setData((d) => ({ ...d, responses: { ...d.responses, [`${gid}:${pinId}`]: val } }));
      if (code) window.API.saveResponse(code, gid, pinId, val, myPin).catch(() => setConn("error"));
    },

    saveOpinion: (gid, val) => {
      setData((d) => ({ ...d, opinions: { ...d.opinions, [gid]: val } }));
      if (code) window.API.saveOpinion(code, gid, val, myPin).catch(() => setConn("error"));
    },

    submitEscape: async (gid, pinId, answer) => {
      if (!code) return { correct: false };
      try {
        const r = await window.API.submitEscape(code, gid, pinId, answer, myPin);
        if (r && r.correct) {
          // 서버가 정답 확인 → escapeAnswers 반영 (폴링 전 즉시 UI 갱신)
          setData((d) => ({
            ...d,
            escapeAnswers: { ...d.escapeAnswers, [`${gid}:${pinId}`]: true },
          }));
        }
        return r || { correct: false };
      } catch (e) {
        setConn("error");
        return { correct: false, error: true };
      }
    },

    setStatus: (status) => {
      setSession((s) => (s ? { ...s, status } : s));
      if (code) window.API.setStatus(code, status).catch(() => setConn("error"));
    },

    leave: () => {
      setCode(null); setMyGroupId(null); setMyPin(null); setMyName(null);
      setSession(null); setData({ responses: {}, opinions: {}, participants: {}, escapeAnswers: {}, groupVariants: {} });
      setConn("idle"); setScreen("home");
    },

    resetAll: () => {
      try { localStorage.removeItem(RECONNECT_KEY); } catch (e) {}
      setRoleState(null); setScreen("home"); setCode(null);
      setMyGroupId(null); setMyPin(null); setMyName(null);
      setSession(null); setData({ responses: {}, opinions: {}, participants: {}, escapeAnswers: {}, groupVariants: {} });
      setConn("idle");
    },
  };

  // ---- 라우팅 ----
  const isTeacher = ["tlist", "intro", "T1", "T2", "T3"].includes(screen);
  // code가 있지만 session이 아직 null이면 폴링 대기 중 (첫 tick 전)
  const awaitingSession = code && !session && conn !== "expired" && conn !== "idle";
  let view, stageClass;

  if (screen === "home") {
    view = <Landing ctx={ctx} />;
    stageClass = "stage-student";
  } else if (isTeacher) {
    if (!ctx.auth.isAuthed) view = <TeacherLogin ctx={ctx} />;
    else if (screen === "tlist") view = <TList ctx={ctx} />;
    else if (screen === "intro") view = <TIntro ctx={ctx} />;
    else if (screen === "T1") view = <T1Create ctx={ctx} />;
    // 새로고침 직후 session이 null이면 로딩 표시 (T1 화면 순간 노출 → 실수로 세션 재생성 방지)
    else if (screen === "T2") view = awaitingSession ? <TeacherLoading /> : session ? <T2Dashboard ctx={ctx} /> : <TList ctx={ctx} />;
    else view = awaitingSession ? <TeacherLoading /> : session ? <T3Present ctx={ctx} /> : <TList ctx={ctx} />;
    stageClass = "stage-teacher";
  } else {
    stageClass = "stage-student";
    if (screen === "S1" || !myGroupId) view = <S1Enter ctx={ctx} />;
    else if (screen === "S2") view = <S2Explore ctx={ctx} />;
    else if (screen === "S4") view = canWriteOpinion(myGroupId) ? <S4Opinion ctx={ctx} /> : <S2Explore ctx={ctx} />;
    else if (screen === "S5") view = <S5Done ctx={ctx} />;
    else view = <S1Enter ctx={ctx} />;
  }

  return (
    <div className={stageClass}>
      {view}
      <ConnBanner ctx={ctx} />
      <DemoNav ctx={ctx} />
    </div>
  );
}

/* ---- 연결 상태 배너 ---- */
function ConnBanner({ ctx }) {
  if (ctx.conn === "expired") {
    return (
      <div className="conn-banner expired">
        ⚠ 세션을 찾을 수 없어요 (마감·만료). <button className="link" onClick={() => ctx.resetAll()}>처음으로</button>
      </div>
    );
  }
  if (ctx.conn === "error") {
    return <div className="conn-banner error">📡 재연결 중…</div>;
  }
  return null;
}

/* ===================== 0-1 진입 / 역할 분기 (16:9) ===================== */
function Landing({ ctx }) {
  return (
    <div className="landing">
      {/* 왼쪽: 타이틀 + 역할 선택 */}
      <div className="landing-hero">
        <div className="landing-badge">통합사회 · 마을 탐구 미션</div>
        <h1 className="landing-title">우리 마을,<br />얼마나<br />알고 있나요?</h1>
        <p className="landing-sub">낡은 공장이 예술촌으로, 쪽방촌이 다문화 거리로 —<br />우리 동네 다섯 곳의 변신 비밀을 단서로 풀어 봐요</p>
        <div className="landing-roles">
          <button className="role-btn teacher" onClick={() => { ctx.setRole("teacher"); ctx.go("tlist"); }}>
            <span className="role-emoji">🧑‍🏫</span>
            <span className="role-text"><b>교사로 시작</b><span>학급 목록 · 세션 만들기 · 발표</span></span>
            <span className="role-arrow">→</span>
          </button>
          <button className="role-btn student" onClick={() => { ctx.setRole("student"); ctx.go("S1"); }}>
            <span className="role-emoji">🧭</span>
            <span className="role-text"><b>학생으로 참여</b><span>PIN 입력 · 임무 수락 · 탐험</span></span>
            <span className="role-arrow">→</span>
          </button>
        </div>
      </div>
      {/* 오른쪽: 인터랙티브 지도 */}
      <div className="landing-map-col">
        <div className="map-wrap" style={{ position: "absolute", inset: 0 }}>
          <BaseMap />
          {window.PINS.map((p) => <PinMarker key={p.id} pin={p} done={false} onClick={() => {}} />)}
          <SchoolMarker />
        </div>
      </div>
    </div>
  );
}

/* ===================== 교사 로그인 (구글) ===================== */
function TeacherLogin({ ctx }) {
  const btnRef = useRef(null);
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);

  useEffect(() => {
    const clientId = ctx.auth.clientId;
    if (!clientId) return;
    let tries = 0, cancelled = false;
    const init = () => {
      if (cancelled) return;
      if (!(window.google && window.google.accounts && window.google.accounts.id)) {
        if (tries++ < 50) return setTimeout(init, 100);
        setErr("구글 로그인 모듈을 불러오지 못했어요. 새로고침 해 주세요.");
        return;
      }
      window.google.accounts.id.initialize({
        client_id: clientId,
        callback: async (resp) => {
          setBusy(true); setErr("");
          try {
            await ctx.loginTeacher(resp.credential);
          } catch (e) {
            if (e.status === 403) setErr("classmate.minssam.com에 가입된 계정이 아니에요.");
            else setErr("로그인에 실패했어요. 잠시 후 다시 시도해 주세요.");
            setBusy(false);
          }
        },
      });
      if (btnRef.current) {
        // 기존 버튼 제거 후 재렌더
        while (btnRef.current.firstChild) btnRef.current.removeChild(btnRef.current.firstChild);
        window.google.accounts.id.renderButton(btnRef.current, {
          theme: "filled_blue", size: "large", shape: "pill", text: "signin_with", locale: "ko",
        });
      }
    };
    init();
    return () => { cancelled = true; };
  }, [ctx.auth.clientId]);

  return (
    <div className="landing tlogin">
      {/* 왼쪽: 지도 배경 */}
      <div className="landing-map-col">
        <div className="map-wrap" style={{ position: "absolute", inset: 0, opacity: .5 }}>
          <BaseMap />
          {window.PINS.map((p) => <PinMarker key={p.id} pin={p} done={false} onClick={() => {}} />)}
          <SchoolMarker />
        </div>
      </div>
      {/* 오른쪽: 로그인 카드 */}
      <div className="split-form" style={{ borderLeft: "1.5px solid var(--line)" }}>
        <div className="split-form-card">
          <div className="landing-badge" style={{ marginBottom: 20 }}>교사 전용 · 구글 로그인</div>
          <h1 style={{ fontSize: 36, fontWeight: 800, letterSpacing: "-.03em", marginBottom: 10 }}>교사 로그인</h1>
          <p style={{ fontSize: 15, color: "var(--slate)", lineHeight: 1.7, marginBottom: 28 }}>
            classmate.minssam.com에 가입된 계정으로<br />로그인하면 수업 세션을 만들고 진행할 수 있어요.
          </p>
          <div className="tlogin-box" style={{ margin: 0, padding: "22px 20px" }}>
            {busy ? <p className="tlogin-status">로그인 중…</p> : null}
            <div ref={btnRef} className="gbtn-host" />
            {err ? <p className="inline-err">⚠ {err}</p> : null}
            <p className="tlogin-note">학생은 로그인이 필요 없어요. 선생님께 받은 PIN만 입력하면 됩니다.</p>
          </div>

          {/* classmate 미가입 교사 안내 — 가입 페이지로 이동 */}
          <div className="signup-cta">
            <span className="signup-cta-text">아직 classmate 회원이 아니신가요?</span>
            <a className="signup-cta-btn" href="https://classmate.minssam.com" target="_blank" rel="noopener noreferrer">
              classmate.minssam.com에서 가입하기 →
            </a>
          </div>

          <button className="role-btn back" style={{ marginTop: 20, width: "100%" }}
            onClick={() => { ctx.setRole(null); ctx.go("home"); }}>
            <span className="role-arrow">←</span>
            <span className="role-text"><b>처음으로</b><span>학생 참여 / 역할 다시 선택</span></span>
          </button>
        </div>
      </div>
    </div>
  );
}

/* ===================== 데모 내비게이션 (시연용) ===================== */
function DemoNav({ ctx }) {
  const [open, setOpen] = useState(false);
  return (
    <div className={`demonav ${open ? "open" : ""}`}>
      {open ? (
        <div className="demonav-panel fade-up">
          <div className="dn-title">빠른 이동 <span>내 역할 화면으로</span></div>
          <div className="dn-group">
            <span className="dn-label">학생</span>
            <button onClick={() => ctx.go("S1")}>S1 입장</button>
            <button onClick={() => ctx.myGroupId ? ctx.go("S2") : ctx.go("S1")}>S2 미션</button>
            <button onClick={() => ctx.go("S4")}>S4 의견</button>
            <button onClick={() => ctx.go("S5")}>S5 완료</button>
          </div>
          <div className="dn-group">
            <span className="dn-label">교사</span>
            <button onClick={() => ctx.go("intro")}>도입</button>
            <button onClick={() => ctx.go("T1")}>T1 생성</button>
            <button onClick={() => ctx.session ? ctx.go("T2") : ctx.go("T1")}>T2 대시보드</button>
            <button onClick={() => ctx.session ? ctx.go("T3") : ctx.go("T1")}>T3 발표</button>
          </div>
          {ctx.auth.profile ? (
            <div className="dn-account">
              <span className="dn-who">👤 {ctx.auth.profile.name || ctx.auth.profile.email}</span>
              <button className="dn-logout" onClick={async () => { if (confirm("교사 로그아웃 할까요?")) { await ctx.logoutTeacher(); ctx.go("home"); } }}>로그아웃</button>
            </div>
          ) : null}
          <div className="dn-bottom">
            <button className="dn-home" onClick={() => ctx.go("home")}>처음 화면</button>
            <button className="dn-reset" onClick={() => { if (confirm("이 기기의 접속을 초기화할까요?")) ctx.resetAll(); }}>접속 초기화</button>
          </div>
        </div>
      ) : null}
      <button className="demonav-fab" onClick={() => setOpen(!open)} title="빠른 이동">
        {open ? "✕" : "≡"}
      </button>
    </div>
  );
}

/* 교사 화면 폴링 대기 중 스피너 (새로고침 시 T1 화면 순간 노출 방지) */
function TeacherLoading() {
  return (
    <div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center",
      flexDirection: "column", gap: 16, color: "var(--slate)", fontSize: 15 }}>
      <div className="dotpulse" /><div className="dotpulse" /><div className="dotpulse" />
      <span style={{ marginTop: 8 }}>수업 세션 불러오는 중…</span>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
