// ─── Color-소개팅 · 메인 앱 셸 ────────────────────────────────
// URL 라우팅:
//   /match/main         → 사용자 홈
//   /match/submit       → 사용자 접수 폼
//   /match/submit/done  → 접수 완료 화면
//   /access/admin       → 관리자 콘솔 (내부에서 sub-screen 전환)
//   그 외               → /match/main 으로 redirect

const { useState: useS, useMemo: useM, useEffect: useE, useCallback: useCB, useRef: useR } = React;

const ADMIN_SEEN_NEW_KEY = 'cs_admin_seen_new_v1';

function loadAdminSeenNewIds() {
  try {
    const raw = localStorage.getItem(ADMIN_SEEN_NEW_KEY);
    const arr = JSON.parse(raw || '[]');
    return Array.isArray(arr) ? arr.filter((x) => typeof x === 'string') : [];
  } catch {
    return [];
  }
}

// ── 경로 → 화면 식별자 ─────────────────────────────────────────
const USER_PATHS = {
  '/match/main': 'home',
  '/match/submit': 'apply',
  '/match/submit/done': 'apply-done',
};
const ADMIN_PATH = '/access/admin';

const isAdminPath = (p) => p === ADMIN_PATH || p.startsWith(ADMIN_PATH + '/');

const resolveScreen = (pathname) => {
  if (isAdminPath(pathname)) return 'admin';
  return USER_PATHS[pathname] || null;
};

// screen 식별자 → URL
const screenToPath = (screen) => {
  if (screen === 'home') return '/match/main';
  if (screen === 'apply') return '/match/submit';
  if (screen === 'apply-done') return '/match/submit/done';
  if (typeof screen === 'string' && (screen === 'admin' || screen.startsWith('admin-')))
    return ADMIN_PATH;
  return '/match/main';
};

function App() {
  // 초기 경로 정규화
  useE(() => {
    const p = window.location.pathname;
    if (!USER_PATHS[p] && !isAdminPath(p)) {
      window.history.replaceState({}, '', '/match/main');
    }
  }, []);

  // 화면 상태 (관리자 sub-screen 포함)
  const initialScreen = (() => {
    const p = window.location.pathname;
    if (isAdminPath(p)) {
      // 토큰이 있으면 대시보드, 없으면 로그인
      return window.CSApi.getToken() ? 'admin-dashboard' : 'admin-login';
    }
    return USER_PATHS[p] || 'home';
  })();

  const [screen, setScreen] = useS(initialScreen);
  const [members, setMembers] = useS([]);
  const [dashboard, setDashboard] = useS(null);
  const [activeMemberId, setActiveMemberId] = useS(null);
  const [matches, setMatches] = useS([]);
  const [proposalGroups, setProposalGroups] = useS([]);
  const [toast, setToast] = useS(null);
  const [applyResult, setApplyResult] = useS(null);
  /** 최근 5일 이내 신규 중, 상세·매칭 화면에서 연 회원 코드 (배지·NEW 표시에서 제외) */
  const [seenNewMemberIds, setSeenNewMemberIds] = useS(loadAdminSeenNewIds);
  const membersRef = useR(members);
  membersRef.current = members;

  const notify = useCB((title, msg, ms = 2800) => {
    setToast({ title, msg });
    setTimeout(() => setToast(null), ms);
  }, []);

  useE(() => {
    window.__adminNotify = (title, msg, ms) => notify(title, msg, ms);
    return () => { delete window.__adminNotify; };
  }, [notify]);

  const isAdmin = screen === 'admin' || (typeof screen === 'string' && screen.startsWith('admin-'));
  const isUser = !isAdmin;

  // ── 네비게이션 (URL + state 동기화) ────────────────────────
  const go = useCB((target) => {
    const isAdminTarget =
      target === 'admin' || (typeof target === 'string' && target.startsWith('admin-'));
    const targetPath = screenToPath(target);

    if (window.location.pathname !== targetPath) {
      window.history.pushState({}, '', targetPath);
    }
    if (isAdminTarget) {
      setScreen(target === 'admin' ? (window.CSApi.getToken() ? 'admin-dashboard' : 'admin-login') : target);
    } else {
      setScreen(target);
    }
  }, []);

  // 브라우저 뒤로/앞으로
  useE(() => {
    const onPop = () => {
      const p = window.location.pathname;
      if (isAdminPath(p)) {
        setScreen(window.CSApi.getToken() ? 'admin-dashboard' : 'admin-login');
      } else if (USER_PATHS[p]) {
        setScreen(USER_PATHS[p]);
      } else {
        window.history.replaceState({}, '', '/match/main');
        setScreen('home');
      }
    };
    window.addEventListener('popstate', onPop);
    return () => window.removeEventListener('popstate', onPop);
  }, []);

  /**
   * 401(토큰 만료/위조) — api-client.js 가 토큰을 비운 뒤 보내는 이벤트.
   * 관리자 화면을 보던 중이면 즉시 로그인 화면으로 이동 + 토스트.
   * 사용자 화면(/match/*)이면 무시.
   */
  useE(() => {
    const onUnauthorized = () => {
      // 관리자 경로가 아니면 화면 이동 없이 무시
      if (!isAdminPath(window.location.pathname)) return;
      // 이미 로그인 화면이면 토스트만
      setScreen((cur) => (cur === 'admin-login' ? cur : 'admin-login'));
      // 화면 상태 초기화 — 다음 로그인 후 깨끗이 시작
      setMembers([]);
      setMatches([]);
      setProposalGroups([]);
      setDashboard(null);
      setActiveMemberId(null);
      // URL 정규화
      if (window.location.pathname !== '/access/admin') {
        try { window.history.replaceState({}, '', '/access/admin'); } catch (_) {}
      }
      notify('세션 만료', '로그인이 만료되어 다시 로그인해 주세요.', 3200);
    };
    window.addEventListener('cs-unauthorized', onUnauthorized);
    window.__onUnauthorized = onUnauthorized;
    return () => {
      window.removeEventListener('cs-unauthorized', onUnauthorized);
      delete window.__onUnauthorized;
    };
  }, [notify]);

  // ── 관리자: 회원/매칭 fetch ────────────────────────────────
  const fetchMembers = useCB(async () => {
    try {
      const r = await window.CSApi.listMembers();
      const list = (r?.members || []).map(window.normalizeMember).filter(Boolean);
      setMembers(list);
    } catch (err) {
      console.error('회원 목록 로딩 실패:', err);
    }
  }, []);

  /** 목록에 없거나 5일 지난 신규는 저장 목록에서 정리 */
  useE(() => {
    if (!members.length) return;
    setSeenNewMemberIds((arr) => {
      const next = arr.filter((id) => {
        const m = members.find((mm) => mm.id === id);
        return m && m.daysAgo <= 5;
      });
      if (next.length === arr.length) return arr;
      try {
        localStorage.setItem(ADMIN_SEEN_NEW_KEY, JSON.stringify(next));
      } catch (_) {}
      return next;
    });
  }, [members]);

  const markNewMemberSeen = useCB((id) => {
    if (!id) return;
    setSeenNewMemberIds((arr) => {
      if (arr.includes(id)) return arr;
      const m = membersRef.current.find((mm) => mm.id === id);
      if (!m || m.daysAgo > 5) return arr;
      const next = [...arr, id];
      try {
        localStorage.setItem(ADMIN_SEEN_NEW_KEY, JSON.stringify(next));
      } catch (_) {}
      return next;
    });
  }, []);

  /** 회원 목록 등에서 NEW 배지 일괄 숨김 — 최근 5일 이내 신규 전원 읽음 처리 */
  const markAllNewMembersSeen = useCB(() => {
    const recentIds = membersRef.current
      .filter((m) => m && m.daysAgo <= 5 && m.id)
      .map((m) => m.id);
    if (!recentIds.length) {
      notify('전체 읽음', '5일 이내 신규 회원이 없습니다.', 2200);
      return;
    }
    setSeenNewMemberIds((arr) => {
      const next = Array.from(new Set([...arr, ...recentIds]));
      try {
        localStorage.setItem(ADMIN_SEEN_NEW_KEY, JSON.stringify(next));
      } catch (_) {}
      return next;
    });
    notify('전체 읽음', 'NEW 표시를 모두 숨겼습니다.', 2400);
  }, [notify]);

  const fetchMatches = useCB(async () => {
    try {
      const r = await window.CSApi.listMatches();
      const list = (r?.matches || []).map((mt) => ({
        a: mt.a?.memberCode || String(mt.a?._id || mt.a),
        b: mt.b?.memberCode || String(mt.b?._id || mt.b),
        ts: new Date(mt.createdAt).getTime(),
        status: mt.status,
        _id: String(mt._id),
      }));
      setMatches(list);
    } catch (err) {
      console.error('매칭 목록 로딩 실패:', err);
    }
  }, []);

  const fetchProposals = useCB(async () => {
    try {
      const r = await window.CSApi.listProposals();
      setProposalGroups(Array.isArray(r?.groups) ? r.groups : []);
    } catch (err) {
      console.error('매칭 제안 그룹 로딩 실패:', err);
      setProposalGroups([]);
    }
  }, []);

  const fetchDashboard = useCB(async () => {
    try {
      const r = await window.CSApi.dashboard();
      setDashboard(r);
    } catch (err) {
      console.error('대시보드 통계 로딩 실패:', err);
      setDashboard(null);
    }
  }, []);

  useE(() => {
    if (isAdmin && screen !== 'admin-login' && window.CSApi.getToken()) {
      fetchMembers();
      fetchMatches();
      fetchProposals();
      fetchDashboard();
    }
  }, [isAdmin, screen]);

  const openDetail = useCB(
    (id) => {
      markNewMemberSeen(id);
      setActiveMemberId(id);
      setScreen('admin-detail');
    },
    [markNewMemberSeen],
  );

  const openActivity = (id) => {
    setActiveMemberId(id);
    setScreen('admin-log-detail');
  };

  const startMatch = useCB(
    (id) => {
      markNewMemberSeen(id);
      setActiveMemberId(id);
      setScreen('admin-match');
    },
    [markNewMemberSeen],
  );

  const setMemberTier = async (id, tier) => {
    const member = members.find((m) => m.id === id);
    if (!member || !member._id) return;
    try {
      const r = await window.CSApi.updateTier(member._id, tier);
      const updated = window.normalizeMember(r.member);
      setMembers((ms) => ms.map((m) => (m.id === id ? { ...m, ...updated } : m)));
      fetchDashboard();
      notify('등급 변경 ✓', `${member.name} → ${tier.toUpperCase()}`);
    } catch (err) {
      notify('등급 변경 실패', err.message || '오류가 발생했습니다.', 3200);
    }
  };

  const persistMemberProfile = useCB(async (_id, payload) => {
    if (!_id) {
      notify('저장 실패', '회원 내부 ID가 없습니다.', 2800);
      throw new Error('회원 내부 ID가 없습니다.');
    }
    try {
      const r = await window.CSApi.updateMemberProfile(_id, payload);
      const updated = window.normalizeMember(r.member);
      setMembers((ms) =>
        ms.map((m) => (String(m._id) === String(_id) ? { ...m, ...updated } : m)),
      );
      notify('기본 프로필 저장 ✓', `${updated.name}님 정보가 반영되었습니다.`);
    } catch (err) {
      notify('저장 실패', err.message || '오류가 발생했습니다.', 3600);
      throw err;
    }
  }, [notify]);

  /** 한마디 / 자기소개 / 이상형 / 선호 조건 부분 저장 (인라인 편집) */
  const persistMemberTextPrefs = useCB(async (_id, payload, sectionLabel) => {
    if (!_id) {
      notify('저장 실패', '회원 내부 ID가 없습니다.', 2800);
      throw new Error('회원 내부 ID가 없습니다.');
    }
    try {
      const r = await window.CSApi.updateMemberTextPrefs(_id, payload);
      const updated = window.normalizeMember(r.member);
      setMembers((ms) =>
        ms.map((m) => (String(m._id) === String(_id) ? { ...m, ...updated } : m)),
      );
      notify(
        `${sectionLabel || '항목'} 저장 ✓`,
        `${updated.name}님 정보가 반영되었습니다.`,
      );
      return updated;
    } catch (err) {
      notify('저장 실패', err.message || '오류가 발생했습니다.', 3600);
      throw err;
    }
  }, [notify]);

  const purgeMemberById = useCB(async (displayId) => {
    const member = members.find((m) => m.id === displayId);
    if (!member || !member._id) {
      notify('파기 실패', '회원 정보를 찾을 수 없습니다.', 3200);
      return;
    }
    const ok = window.confirm(
      `「${member.name}」(${member.id}) 회원을 DB에서 완전히 삭제할까요?\n\n`
      + '관련 매칭 기록도 함께 삭제됩니다. 이 작업은 되돌릴 수 없습니다.',
    );
    if (!ok) return;
    try {
      await window.CSApi.purgeMember(member._id);
      setMembers((ms) => ms.filter((m) => m.id !== member.id));
      setSeenNewMemberIds((arr) => {
        const next = arr.filter((x) => x !== member.id);
        try { localStorage.setItem(ADMIN_SEEN_NEW_KEY, JSON.stringify(next)); } catch (_) {}
        return next;
      });
      setActiveMemberId((cur) => (cur === member.id ? null : cur));
      await Promise.all([fetchMatches(), fetchDashboard()]);
      notify('파기 완료', '회원 및 관련 매칭을 삭제했습니다.');
      go('admin-list');
    } catch (err) {
      notify('파기 실패', err.message || '오류가 발생했습니다.', 3600);
    }
  }, [members, fetchMatches, fetchDashboard, go, notify]);

  /**
   * 매칭 제안 등록 (1:N).
   *  - sourceId(소스 표시 id) + candidateDisplayIds(후보 표시 id 배열)을 받아
   *    내부적으로 _id 로 변환 후 백엔드 호출.
   *  - 성공 시 회원/매칭/대시보드 새로고침 + 토스트.
   *  - 반환값: { ok, groupId } / { ok: false, error }
   */
  const proposeMatches = async (sourceDisplayId, candidateDisplayIds) => {
    const src = members.find((m) => m.id === sourceDisplayId);
    if (!src?._id) {
      notify('제안 실패', '소스 회원 정보를 찾을 수 없습니다.', 3200);
      return { ok: false };
    }
    const cands = (candidateDisplayIds || [])
      .map((id) => members.find((m) => m.id === id))
      .filter((m) => m && m._id);
    if (cands.length === 0) {
      notify('제안 실패', '후보가 비어있습니다.', 3200);
      return { ok: false };
    }
    try {
      const res = await window.CSApi.createProposal(
        src._id,
        cands.map((c) => c._id),
      );
      await Promise.all([fetchMembers(), fetchMatches(), fetchProposals(), fetchDashboard()]);
      notify(
        '매칭 제안 등록 ✓',
        `${src.name} 님께 후보 ${cands.length}명 제안 (그룹 ${res.groupId.slice(0, 6)}…)`,
        3200,
      );
      return { ok: true, groupId: res.groupId };
    } catch (err) {
      notify('제안 실패', err.message || '오류가 발생했습니다.', 3500);
      return { ok: false, error: err };
    }
  };

  /** 후보 한 명을 그룹의 최종 매칭으로 승격 */
  const selectFinalMatch = async (matchId) => {
    try {
      await window.CSApi.selectFinal(matchId);
      await Promise.all([fetchMembers(), fetchMatches(), fetchProposals(), fetchDashboard()]);
      notify('최종 매칭 확정 ✓', '해당 후보만 ○ 처리됐어요. 다른 행은 그대로예요.', 3000);
      return { ok: true };
    } catch (err) {
      notify('최종 매칭 실패', err.message || '오류가 발생했습니다.', 3500);
      return { ok: false };
    }
  };

  /** 후보 한 명을 '선택 안함(rejected)' 처리 — 빨간 ✕로 표시됨 */
  const rejectCandidateMatch = async (matchId) => {
    try {
      await window.CSApi.updateMatch(matchId, { status: 'rejected' });
      await Promise.all([fetchMembers(), fetchMatches(), fetchProposals(), fetchDashboard()]);
      notify('선택 안함 처리 ✓', '빨간 ✕로 표시됩니다', 2400);
      return { ok: true };
    } catch (err) {
      notify('처리 실패', err.message || '오류가 발생했습니다.', 3200);
      return { ok: false };
    }
  };

  /** (구) 1:1 즉시 매칭 — 호환 유지. 새 UI는 proposeMatches 사용. */
  const confirmMatch = async (a, b) => proposeMatches(a, [b]);

  const onLoggedIn = async () => {
    await Promise.all([fetchMembers(), fetchMatches(), fetchProposals(), fetchDashboard()]);
  };

  const onApplied = (result) => {
    setApplyResult(result);
  };

  const activeMember = useM(
    () => members.find((m) => m.id === activeMemberId),
    [members, activeMemberId],
  );

  // ────────────────────────────────────────────────
  // ADMIN view
  // ────────────────────────────────────────────────
  if (isAdmin) {
    return (
      <div style={{ minHeight: '100vh', background: '#161412' }}
           data-screen-label={`Color-소개팅 admin · ${screen}`}>
        {screen === 'admin-login' && <AdminLogin go={go} onLoggedIn={onLoggedIn} />}
        {screen === 'admin-dashboard' && (
          <AdminDashboard
            go={go}
            members={members}
            dashboard={dashboard}
            seenNewMemberIds={seenNewMemberIds}
            openDetail={openDetail}
          />
        )}
        {screen === 'admin-list' && (
          <AdminList
            go={go}
            members={members}
            openDetail={openDetail}
            startMatch={startMatch}
            seenNewMemberIds={seenNewMemberIds}
            onMarkAllNewSeen={markAllNewMembersSeen}
          />
        )}
        {screen === 'admin-detail' && activeMember && (
          <AdminDetail
            go={go}
            member={activeMember}
            setTier={setMemberTier}
            members={members}
            startMatch={startMatch}
            seenNewMemberIds={seenNewMemberIds}
            onPurge={purgeMemberById}
            persistMemberProfile={persistMemberProfile}
            persistMemberTextPrefs={persistMemberTextPrefs}
          />
        )}
        {screen === 'admin-match' && activeMember && (
          <AdminMatch
            go={go}
            source={activeMember}
            members={members}
            matches={matches}
            proposalGroups={proposalGroups}
            onConfirm={confirmMatch}
            onPropose={proposeMatches}
            onSelectFinal={selectFinalMatch}
            onRejectCandidate={rejectCandidateMatch}
            seenNewMemberIds={seenNewMemberIds}
            openDetail={openDetail}
          />
        )}
        {screen === 'admin-match-pick' && (
          <AdminMatchPick
            go={go}
            members={members}
            startMatch={startMatch}
            matches={matches}
            seenNewMemberIds={seenNewMemberIds}
            openDetail={openDetail}
          />
        )}
        {screen === 'admin-settings' && (
          <AdminSettings go={go} members={members} seenNewMemberIds={seenNewMemberIds} />
        )}
        {screen === 'admin-messages' && (
          <AdminMessageCenter go={go} members={members} seenNewMemberIds={seenNewMemberIds} openDetail={openDetail} />
        )}
        {screen === 'admin-match-history' && (
          <AdminMatchHistory
            go={go}
            members={members}
            proposalGroups={proposalGroups}
            onSelectFinal={selectFinalMatch}
            onRejectCandidate={rejectCandidateMatch}
            seenNewMemberIds={seenNewMemberIds}
            openDetail={openDetail}
          />
        )}
        {screen === 'admin-instagram-story' && (
          <AdminMemberStoryWorkspace go={go} members={members} seenNewMemberIds={seenNewMemberIds} openDetail={openDetail} />
        )}
        {screen === 'admin-log' && (
          <AdminActivityLog go={go} members={members} openActivity={openActivity} openDetail={openDetail} seenNewMemberIds={seenNewMemberIds} />
        )}
        {screen === 'admin-log-detail' && activeMember && (
          <AdminActivityDetail
            go={go}
            member={activeMember}
            members={members}
            seenNewMemberIds={seenNewMemberIds}
            openDetail={openDetail}
          />
        )}

        {toast && <Toast toast={toast} />}
      </div>
    );
  }

  // ────────────────────────────────────────────────
  // USER view — 실제 모바일 반응형 (iOS 프레임/네비게이션 없음)
  // ────────────────────────────────────────────────
  return (
    <div className="user-shell" data-screen-label={`Color-소개팅 user · ${screen}`}>
      {screen === 'home' && <HomeScreen go={go} />}
      {screen === 'apply' && <ApplyScreen go={go} onApplied={onApplied} />}
      {screen === 'apply-done' && <ApplyDoneScreen go={go} applyResult={applyResult} />}
    </div>
  );
}

function Toast({ toast }) {
  return (
    <div
      style={{
        position: 'fixed', bottom: 24, left: '50%', transform: 'translateX(-50%)',
        background: '#161412', color: '#fff', padding: '14px 22px',
        borderRadius: 14, boxShadow: '0 12px 30px rgba(0,0,0,0.3)',
        zIndex: 300, fontFamily: 'var(--f-sans)',
        animation: 'toastIn 240ms ease',
        display: 'flex', alignItems: 'center', gap: 12,
      }}
    >
      <div
        style={{
          width: 32, height: 32, borderRadius: 999,
          background: 'linear-gradient(135deg, #e23a2e, #f5c419)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 16,
        }}
      >💌</div>
      <div>
        <div style={{ fontSize: 13, fontWeight: 700 }}>{toast.title}</div>
        <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.7)' }}>{toast.msg}</div>
      </div>
      <style>{`@keyframes toastIn { from { transform: translate(-50%, 20px); opacity: 0; } }`}</style>
    </div>
  );
}

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