/** @jsxRuntime classic */
// 팀 조회 화면 (PRD 5.2). 주소 / 의 기본 화면이다(# 뒤가 관리 주소가 아니면 이 화면).
// 팀을 고르고 4자리 비밀번호를 넣으면 서버가 그 팀만 볼 수 있는 쿠키를 준다(/api/team/login).
// 데이터는 /api/team/view 하나로 받는다. 서버가 calc.js 로 계산한 올해 1월~이번 달 결과와 내역이 오고, 화면은 보여 주기만 한다.

const longDate = d => (d ? `${Number(d.slice(5, 7))}월 ${Number(d.slice(8, 10))}일` : null);

function TeamApp() {
  const [st, setSt] = useState({ state: 'checking' });
  const [teams, setTeams] = useState([]);
  // 팀 목록과 이 브라우저가 이미 로그인한 팀을 확인한다. notice 는 로그인 화면에 띄울 안내
  const check = useCallback(async notice => {
    setSt({ state: 'checking' });
    try {
      const d = await api('GET', '/api/team/teams');
      setTeams(d.teams);
      setSt(d.current ? { state: 'in' } : { state: 'login', notice });
    } catch (err) {
      setSt({ state: 'error', message: err.message });
    }
  }, []);
  useEffect(() => { check(); }, []);

  async function logout() {
    try { await api('POST', '/api/team/logout', {}); } catch {}
    check();
  }

  if (st.state === 'in') return <TeamView onLogout={logout} onExpired={() => check('비밀번호가 바뀌었거나 조회 시간이 끝났습니다. 비밀번호를 다시 넣어 주세요')} />;
  return (
    <>
      <TopBar mode="team" />
      <Shell>
        {st.state === 'checking' && <div className="page-msg">불러오는 중…</div>}
        {st.state === 'error' && <div className="page-msg"><p>{st.message}</p><button className="btn" onClick={() => check()}>다시 시도</button></div>}
        {st.state === 'login' && <TeamLogin teams={teams} notice={st.notice} onDone={() => setSt({ state: 'in' })} />}
      </Shell>
    </>
  );
}

function TeamLogin({ teams, notice, onDone }) {
  // 마지막에 고른 팀은 이 브라우저에 기억해 두고 다음에 미리 골라 둔다
  const [teamId, setTeamId] = useStored('team.selected', '');
  const [passcode, setPasscode] = useState('');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState(notice || '');
  const passRef = useRef(null);
  const team = teams.find(t => t.id === teamId) || null;
  useEffect(() => { if (team && passRef.current) passRef.current.focus(); }, [teamId]);

  async function submit(e) {
    e.preventDefault();
    if (!team || passcode.length !== 4) return;
    setBusy(true);
    setError('');
    try {
      await api('POST', '/api/team/login', { teamId: team.id, passcode });
      onDone();
    } catch (err) {
      setError(err.message);
      setPasscode('');
      setBusy(false);
      setTimeout(() => passRef.current && passRef.current.focus(), 0);
    }
  }
  return (
    <div className="login">
      <form className="card" onSubmit={submit}>
        <div className="brand-mark"><Icon.Wallet /></div>
        <h1>팀 조회</h1>
        <p className="muted small" style={{ margin: '0 0 20px' }}>팀을 고르고 조회 비밀번호(숫자 4자리)를 넣어 주세요.</p>
        <div className="stack" style={{ gap: 12 }}>
          <div className="field"><span id="team-picker-label">팀</span>
            <TeamPicker teams={teams} value={team ? team.id : ''} labelId="team-picker-label"
              onChange={id => { setTeamId(id); setPasscode(''); setError(''); }} />
          </div>
          {team && !team.has_passcode ? (
            <p className="err-text" style={{ margin: 0 }}>이 팀은 조회 비밀번호가 아직 정해지지 않았습니다. 관리자에게 문의해 주세요.</p>
          ) : (
            <label className="field"><span>비밀번호</span>
              <input ref={passRef} className="input" type="password" inputMode="numeric" autoComplete="off" maxLength={4} disabled={!team}
                value={passcode} onChange={e => setPasscode(e.target.value.replace(/\D/g, '').slice(0, 4))} />
            </label>
          )}
          {error && <div className="err-text" role="alert">{error}</div>}
          <button className="btn primary" disabled={busy || !team || !team.has_passcode || passcode.length !== 4}>{busy ? '확인 중…' : '조회'}</button>
        </div>
      </form>
    </div>
  );
}

// 팀 고르기. 기본 select 는 펼친 목록을 꾸밀 수 없어 직접 만든다.
// 이름으로 찾을 수 있고(띄어쓰기 무시), 찾기 칸에서 ↑↓ 로 옮기고 Enter 로 고르고 Esc 로 닫는다
const DIVISIONS = ['인', '물'];
const searchKey = s => s.replace(/\s/g, '').toLowerCase();

function TeamPicker({ teams, value, onChange, labelId }) {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState('');
  const [active, setActive] = useState(0);
  const boxRef = useRef(null);
  const btnRef = useRef(null);
  const searchRef = useRef(null);
  const listRef = useRef(null);
  const selected = teams.find(t => t.id === value) || null;
  const shown = teams.filter(t => searchKey(t.name).includes(searchKey(q)));
  const groups = DIVISIONS.map(d => ({ d, list: shown.filter(t => t.division === d) })).filter(g => g.list.length);
  const flat = groups.flatMap(g => g.list);   // 화면에 그려지는 순서

  useEffect(() => {
    if (!open) return;
    if (searchRef.current) searchRef.current.focus();
    const onDown = e => { if (boxRef.current && !boxRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);
  // 키보드로 옮긴 항목이 목록 안에서 보이게
  useEffect(() => {
    const el = open && listRef.current && listRef.current.querySelector('[data-active="true"]');
    if (el) el.scrollIntoView({ block: 'nearest' });
  }, [open, active, q]);

  function show() {
    const all = DIVISIONS.flatMap(d => teams.filter(t => t.division === d));
    setQ('');
    setActive(Math.max(0, all.findIndex(t => t.id === value)));
    setOpen(true);
  }
  function close() {
    setOpen(false);
    if (btnRef.current) btnRef.current.focus();
  }
  function pick(t) {
    setOpen(false);
    // 다른 팀을 고르면 비밀번호 칸으로 간다(TeamLogin). 같은 팀이면 버튼으로 돌아온다
    if (t.id === value) { if (btnRef.current) btnRef.current.focus(); } else onChange(t.id);
  }
  function onKey(e) {
    if (e.key === 'ArrowDown') { e.preventDefault(); setActive(a => Math.min(a + 1, flat.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(a => Math.max(a - 1, 0)); }
    else if (e.key === 'Enter') { e.preventDefault(); if (flat[active]) pick(flat[active]); }
    else if (e.key === 'Escape') { e.preventDefault(); close(); }
    else if (e.key === 'Tab') setOpen(false);
  }

  return (
    <div className="picker" ref={boxRef}>
      <button type="button" ref={btnRef} className="input picker-btn" aria-haspopup="listbox" aria-expanded={open} aria-labelledby={`${labelId} team-picker-value`}
        onClick={() => (open ? setOpen(false) : show())}
        onKeyDown={e => { if (!open && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) { e.preventDefault(); show(); } }}>
        <span id="team-picker-value" className={cx('name', !selected && 'muted')}>{selected ? selected.name : '팀을 고르세요'}</span>
        {selected && <span className="badge">({selected.division})서면</span>}
        <Icon.Down />
      </button>
      {open && (
        <div className="picker-pop">
          <div className="picker-search">
            <Icon.Search />
            <input ref={searchRef} value={q} placeholder="팀 이름으로 찾기" autoComplete="off" role="combobox" aria-label="팀 이름으로 찾기"
              aria-expanded="true" aria-controls="team-picker-list" aria-activedescendant={flat[active] ? `tp-${flat[active].id}` : undefined}
              onChange={e => { setQ(e.target.value); setActive(0); }} onKeyDown={onKey} />
          </div>
          <ul id="team-picker-list" className="picker-list" role="listbox" aria-label="팀" ref={listRef}>
            {groups.map(g => (
              <li key={g.d} role="presentation">
                <div className="picker-group" aria-hidden="true">({g.d})서면 <span className="meta">{g.list.length}</span></div>
                <ul role="group" aria-label={`(${g.d})서면`}>
                  {g.list.map(t => {
                    const i = flat.indexOf(t);
                    return (
                      <li key={t.id} id={`tp-${t.id}`} role="option" aria-selected={t.id === value} data-active={i === active}
                        onMouseMove={() => { if (i !== active) setActive(i); }} onMouseDown={e => e.preventDefault()} onClick={() => pick(t)}>
                        <span className="name">{t.name}</span>
                        {!t.has_passcode && <span className="badge">비밀번호 없음</span>}
                        {t.id === value && <Icon.Check />}
                      </li>
                    );
                  })}
                </ul>
              </li>
            ))}
            {!flat.length && <li className="picker-empty" role="presentation">'{q}'(이)가 들어간 팀이 없습니다</li>}
          </ul>
        </div>
      )}
    </div>
  );
}

function TeamView({ onLogout, onExpired }) {
  const load = useLoad(() => api('GET', '/api/team/view').catch(err => {
    if (err.data.needTeamLogin) onExpired();
    throw err;
  }), []);
  const [picked, setPicked] = useState(null);
  const data = load.data;
  const top = (
    <TopBar mode="team" actions={
      <button type="button" className="btn ghost sm" onClick={onLogout} title="이 팀 조회를 끝내고 팀 고르기로 돌아갑니다"><Icon.Logout />나가기</button>
    } />
  );
  if (!data) {
    return (
      <>
        {top}
        <Shell>{load.error ? <LoadError load={load} /> : <div className="page-msg">불러오는 중…</div>}</Shell>
      </>
    );
  }

  const { team, year } = data;
  // 고른 달이 없거나 새로 읽은 데이터 범위 밖이면 이번 달
  const month = picked && picked <= data.month ? picked : data.month;
  const c = data.months[month - 1];
  const side = (
    <>
      <div className="side-label">({team.division})서면</div>
      <div className="side-title">{team.name}</div>
      <div className="side-sep" />
      <div className="side-cap"><Icon.Calendar />{year}년 1월 ~ {data.month}월</div>
      <MonthNav months={data.months.map(m => ({ month: m.month }))} value={month} onPick={setPicked} />
    </>
  );
  return (
    <>
      {top}
      <Shell side={side} rail={<TeamYearTotals team={team} year={year} months={data.months} />}>
        <PageHead title={`${year}년 ${month}월`} load={load} crumbs={[{ label: team.name }, { label: `${month}월` }]}
          sub={<p className="page-desc">{month === data.month ? '이번 달' : `${month}월`} 회식비{team.has_interview ? '·면담비' : ''} 한도와 사용 내역입니다.</p>} />
        <LoadError load={load} />
        <div className="stack">
          <MonthSummary team={team} c={c} year={year} month={month} />
          <TeamExpenses team={team} month={month} expenses={data.expenses.filter(e => e.month === month)} />
          <TeamYearFlow team={team} year={year} months={data.months} month={month} onPick={setPicked} />
        </div>
      </Shell>
    </>
  );
}

function TeamExpenses({ team, month, expenses }) {
  const sum = kind => expenses.filter(e => e.kind === kind).reduce((s, e) => s + e.amount, 0);
  return (
    <section className="card card-pad">
      <div className="spread"><h2>{month}월 사용 내역</h2>
        <span className="small muted num">회식비 {won(sum('dining'))}{team.has_interview ? ` · 면담비 ${won(sum('interview'))}` : ''}</span>
      </div>
      {expenses.length === 0 ? <div className="muted small" style={{ padding: '8px 4px' }}>이 달의 사용 내역이 없습니다.</div> : (
        <div>
          {expenses.map(x => (
            <div className="exp ro" key={x.id}>
              <span className="num">{x.date ? longDate(x.date) : <span className="muted small" title="엑셀에서 옮겨 온 면담비라 날짜가 없습니다">날짜 없음</span>}</span>
              <span><span className={cx('badge', x.kind === 'interview' && 'accent')}>{KIND[x.kind]}</span></span>
              <span className={cx('amt', x.amount < 0 && 'neg')}>{won(x.amount)}</span>
            </div>
          ))}
        </div>
      )}
    </section>
  );
}

// 올해 누계 (PRD 5.2 의 5). 오른쪽 정보 칸에 둔다
function TeamYearTotals({ team, year, months }) {
  const total = key => months.reduce((s, m) => s + m[key], 0);
  const last = months[months.length - 1];
  return (
    <section>
      <h2><Icon.Chart />{year}년 누계 ({last.month}월까지)</h2>
      <div className="kv"><span className="k">받은 회식비 <span className="hint">(한도+추가)</span></span><span className="v">{won(total('diningLimit') + total('extraAmount'))}</span></div>
      <div className="kv"><span className="k">쓴 회식비</span><span className="v">{won(total('diningUsed'))}</span></div>
      {!!team.has_interview && <div className="kv"><span className="k">쓴 면담비</span><span className="v">{won(total('interviewUsed'))}</span></div>}
      <div className="kv"><span className="k">1월 이월</span><span className={cx('v', months[0].carryOver < 0 && 'neg')}>{won(months[0].carryOver)}</span></div>
      <div className="kv hero"><span className="k">지금 잔액</span><span className={cx('v', last.balance < 0 && 'neg')}>{won(last.balance)}</span></div>
    </section>
  );
}

// 월별 잔액 흐름. 줄을 누르면 그 달로 간다
function TeamYearFlow({ team, year, months, month, onPick }) {
  return (
    <section className="card">
      <div className="card-pad" style={{ paddingBottom: 14 }}><h2 style={{ margin: 0 }}>{year}년 월별 흐름</h2></div>
      <div className="table-wrap">
        <table className="tbl num">
          <thead><tr>
            <th>월</th><th className="r">한도+추가</th><th className="r">사용</th><th className="r">잔액</th>
            {!!team.has_interview && <th className="r hide-sm">면담비 사용 / 한도</th>}
          </tr></thead>
          <tbody>
            {months.map(m => (
              <tr key={m.month} className={cx('linkrow', m.month === month && 'current')} onClick={() => onPick(m.month)}>
                <td>{m.month}월</td>
                <td className="r">{fmt(m.diningLimit + m.extraAmount)}</td>
                <td className="r">{fmt(m.diningUsed)}</td>
                <td className={cx('r', m.balance < 0 && 'neg')}><strong>{fmt(m.balance)}</strong></td>
                {!!team.has_interview && <td className="r hide-sm">{fmt(m.interviewUsed)} / {fmt(m.interviewLimit)}</td>}
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </section>
  );
}
