import { useEffect, useState } from 'react'

const won = (n: number) => n.toLocaleString('ko-KR') + '원'
function fmtTs(ms?: unknown): string {
  if (typeof ms !== 'number' || !ms) return '-'
  const d = new Date(ms); const p = (n: number) => String(n).padStart(2, '0')
  return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`
}
const box: React.CSSProperties = { border: '1px solid #e2e2e2', borderRadius: 8, padding: 16, marginTop: 20 }
const cell: React.CSSProperties = { padding: '5px 9px', borderBottom: '1px solid #eee', fontSize: 13 }
const th: React.CSSProperties = { ...cell, borderBottom: '2px solid #ccc', textAlign: 'left', background: '#fafafa' }

type Row = Record<string, any>

/** 정식 pacs.008.001.08 전문 생성(테스트 실행용). */
function buildPacs008(bmi: string, from: string, to: string, amt: number): string {
  const now = new Date().toISOString().replace(/\.\d+Z$/, '')
  return `<?xml version="1.0" encoding="UTF-8"?><Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08">` +
    `<FIToFICstmrCdtTrf><GrpHdr><MsgId>${bmi}</MsgId><CreDtTm>${now}</CreDtTm><NbOfTxs>1</NbOfTxs>` +
    `<SttlmInf><SttlmMtd>CLRG</SttlmMtd></SttlmInf></GrpHdr><CdtTrfTxInf><PmtId><EndToEndId>${bmi}</EndToEndId></PmtId>` +
    `<IntrBkSttlmAmt Ccy="KRW">${amt}</IntrBkSttlmAmt><ChrgBr>SLEV</ChrgBr>` +
    `<Dbtr><Nm>BANK-${from}</Nm></Dbtr><DbtrAcct><Id><Othr><Id>ACC-${from}</Id></Othr></Id></DbtrAcct>` +
    `<DbtrAgt><FinInstnId><ClrSysMmbId><MmbId>${from}</MmbId></ClrSysMmbId></FinInstnId></DbtrAgt>` +
    `<CdtrAgt><FinInstnId><ClrSysMmbId><MmbId>${to}</MmbId></ClrSysMmbId></FinInstnId></CdtrAgt>` +
    `<Cdtr><Nm>BANK-${to}</Nm></Cdtr><CdtrAcct><Id><Othr><Id>ACC-${to}</Id></Othr></Id></CdtrAcct>` +
    `</CdtTrfTxInf></FIToFICstmrCdtTrf></Document>`
}

function MiniTable({ title, cols, rows, timeCols = [] }: { title: string; cols: [string, string][]; rows: Row[]; timeCols?: string[] }) {
  return (
    <div style={{ marginTop: 10 }}>
      <div style={{ fontWeight: 600, marginBottom: 4 }}>{title} <span style={{ color: '#888', fontSize: 12 }}>({rows.length})</span></div>
      <div style={{ overflowX: 'auto' }}>
        <table style={{ borderCollapse: 'collapse', width: '100%' }}>
          <thead><tr>{cols.map(([, ko]) => <th key={ko} style={th}>{ko}</th>)}</tr></thead>
          <tbody>
            {rows.map((r, i) => (
              <tr key={i}>{cols.map(([k]) => <td key={k} style={{ ...cell, fontFamily: /code|seq|bmi|balance|amount/.test(k) ? 'monospace' : undefined }}>
                {timeCols.includes(k) ? fmtTs(r[k]) : String(r[k] ?? '-')}
              </td>)}</tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  )
}

export default function AdminPanel() {
  const [summary, setSummary] = useState<any>(null)
  const [transfers, setTransfers] = useState<Row[]>([])
  const [journal, setJournal] = useState<Row[]>([])
  const [views, setViews] = useState<Row[]>([])
  const [insts, setInsts] = useState<Row[]>([])
  const [codes, setCodes] = useState<Row[]>([])
  const [users, setUsers] = useState<Row[]>([])
  const [health, setHealth] = useState<Row[]>([])
  const [msg, setMsg] = useState<string | null>(null)
  const [err, setErr] = useState<string | null>(null)

  // 인증(A1): Gucci 로그인으로 발급받은 ADMIN JWT
  const [token, setToken] = useState<string | null>(() => localStorage.getItem('rtgs_admin_token'))
  const [loginUser, setLoginUser] = useState<string>(() => localStorage.getItem('rtgs_admin_user') || '')
  const [loginForm, setLoginForm] = useState({ username: '', secret: '' })
  const [mustChange, setMustChange] = useState(false)
  const [newSecret, setNewSecret] = useState('')

  // 폼 상태
  const [inst, setInst] = useState({ code: '', name: '', balance: 1000000000 })
  const [user, setUser] = useState({ username: '', displayName: '', role: 'ORG_S', orgCode: '' })

  // 테스트 실행(B6)
  const [test, setTest] = useState({ bmiStart: '2026071099000000000001', count: 50 })
  const [testRun, setTestRun] = useState<{ running: boolean; done: number; sent: number; rcvd: number; rjct: number; err: number } | null>(null)

  const authHeaders = (json = false): Record<string, string> => {
    const h: Record<string, string> = {}
    if (json) h['Content-Type'] = 'application/json'
    if (token) h['Authorization'] = `Bearer ${token}`
    return h
  }
  const logout = () => { localStorage.removeItem('rtgs_admin_token'); localStorage.removeItem('rtgs_admin_user'); setToken(null); setMustChange(false) }
  const check401 = (r: Response) => { if (r.status === 401) { logout(); throw new Error('세션 만료 또는 권한 없음 — 다시 로그인하세요') } }

  const j = async (u: string) => { const r = await fetch(u, { headers: authHeaders() }); check401(r); return r.json() }
  const post = async (url: string, body?: any, method = 'POST') => {
    const r = await fetch(url, { method, headers: authHeaders(!!body), body: body ? JSON.stringify(body) : undefined })
    check401(r); return r.json()
  }

  const doLogin = async () => {
    setErr(null)
    try {
      const resp = await fetch('/gucci/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(loginForm) })
      if (!resp.ok) { setErr('로그인 실패 — 아이디/비밀번호를 확인하세요'); return }
      const r = await resp.json()
      if (r.role !== 'ADMIN') { setErr('관리자(ADMIN) 권한이 필요합니다'); return }
      localStorage.setItem('rtgs_admin_token', r.token); localStorage.setItem('rtgs_admin_user', loginForm.username)
      setLoginUser(loginForm.username); setToken(r.token); setMustChange(!!r.mustChangePassword)
    } catch (e: any) { setErr('로그인 실패: ' + e.message) }
  }
  const doChangePassword = async () => {
    setErr(null)
    if (newSecret.length < 1) { setErr('새 비밀번호를 입력하세요'); return }
    try {
      const resp = await fetch('/gucci/auth/change-password', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username: loginUser, oldSecret: loginForm.secret, newSecret }),
      })
      if (!resp.ok) { setErr('비밀번호 변경 실패'); return }
      setMsg('비밀번호가 변경되었습니다.'); setMustChange(false); setNewSecret('')
    } catch (e: any) { setErr('변경 실패: ' + e.message) }
  }

  const loadAll = async () => {
    setErr(null)
    try {
      const [s, t, jn, v, ins, cd, us, hl] = await Promise.all([
        j('/admin/summary'), j('/admin/ledger/transfers?limit=15'), j('/admin/ledger/journal?limit=15'),
        j('/admin/ledger/views?limit=15'), j('/admin/institutions'), j('/admin/status-codes'), j('/admin/users'), j('/admin/health'),
      ])
      setSummary(s); setTransfers(t); setJournal(jn); setViews(v); setInsts(ins); setCodes(cd); setUsers(us); setHealth(hl)
    } catch (e: any) { setErr('관리자 데이터 로드 실패: ' + e.message) }
  }
  useEffect(() => { if (token && !mustChange) loadAll() }, [token, mustChange])

  const doReset = async () => {
    if (!confirm('정말 초기화할까요?\n거래/저널/조회사본/원전문을 모두 비우고 계좌 잔액을 각 10억으로 되돌립니다.')) return
    setMsg(null); setErr(null)
    try { const r = await post('/admin/reset'); setMsg(`초기화 완료: 계좌 ${r.accountsReset}개 리셋, 원장 비움`); await loadAll() }
    catch (e: any) { setErr('초기화 실패: ' + e.message) }
  }
  const saveInst = async () => {
    if (!/^[0-9]{4}$/.test(inst.code)) { setErr('기관코드는 숫자 4자리'); return }
    try { await post('/admin/institutions', inst); setMsg(`기관 저장: ${inst.code}`); await loadAll() } catch (e: any) { setErr('' + e.message) }
  }
  const delInst = async (code: string) => { if (confirm(`기관 ${code} 삭제?`)) { await post('/admin/institutions/' + code, undefined, 'DELETE'); await loadAll() } }
  const saveUser = async () => {
    if (!user.username) { setErr('사용자ID 필수'); return }
    try { await post('/admin/users', { ...user, orgCode: user.orgCode || null }); setMsg(`사용자 저장: ${user.username}`); await loadAll() } catch (e: any) { setErr('' + e.message) }
  }
  const delUser = async (u: string) => { if (confirm(`사용자 ${u} 삭제?`)) { await post('/admin/users/' + u, undefined, 'DELETE'); await loadAll() } }

  // B6: 테스트 이체 대량 생성(송/수신 랜덤, 금액 1,000~1,000,000 랜덤, BMI 시작번호부터 순번 증가)
  const BANKS_FALLBACK = ['1001', '1002', '1003', '1004', '1005', '1006', '1007', '1008', '1009', '1010']
  const runTest = async () => {
    setErr(null); setMsg(null)
    const n = Number(test.count)
    if (!n || n < 1) { setErr('건수를 1 이상 입력하세요'); return }
    let base: bigint
    try { base = BigInt((test.bmiStart || '').trim()) } catch { setErr('BMI 시작번호는 숫자여야 합니다'); return }
    const banks = insts.length ? insts.map((a) => String(a.code)) : BANKS_FALLBACK
    const st = { running: true, done: 0, sent: n, rcvd: 0, rjct: 0, err: 0 }
    setTestRun({ ...st })
    const one = async (i: number) => {
      const from = banks[Math.floor(Math.random() * banks.length)]
      let to = from, guard = 0
      while (to === from && guard++ < 20) to = banks[Math.floor(Math.random() * banks.length)]
      const amt = 1000 + Math.floor(Math.random() * (1000000 - 1000 + 1))
      const bmi = (base + BigInt(i)).toString().padStart(22, '0')
      try {
        const r = await fetch('/pay/customer', { method: 'POST', headers: { 'Content-Type': 'application/xml' }, body: buildPacs008(bmi, from, to, amt) })
        const txt = await r.text()
        if (txt.includes('RCVD')) st.rcvd++; else st.rjct++
      } catch { st.err++ }
      st.done++
      if (st.done % 10 === 0 || st.done === n) setTestRun({ ...st })
    }
    const CONC = 20 // 브라우저 동시요청 배치
    for (let i = 0; i < n; i += CONC) {
      await Promise.all(Array.from({ length: Math.min(CONC, n - i) }, (_, k) => one(i + k)))
    }
    st.running = false; setTestRun({ ...st })
    setMsg(`테스트 완료: 접수 ${st.rcvd} · 반려 ${st.rjct} · 오류 ${st.err} (완결은 대시보드 새로고침으로 확인)`)
    await loadAll()
  }

  const totalOk = summary && summary.totalBalance === (summary.accounts * 1000000000)

  // --- A1: 로그인 게이트 ---
  if (!token) {
    return (
      <section style={{ ...box, maxWidth: 380, margin: '40px auto', background: '#f7fbff' }}>
        <h2 style={{ marginTop: 0 }}>🔐 관리자 로그인</h2>
        <p style={{ color: '#666', fontSize: 13 }}>시스템관리(LouisVuitton) 콘솔은 <b>ADMIN</b> 로그인이 필요합니다.<br />테스트 계정: <code>a / 1</code></p>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <input placeholder="아이디" value={loginForm.username} onChange={(e) => setLoginForm({ ...loginForm, username: e.target.value })} style={{ padding: 8 }} />
          <input placeholder="비밀번호" type="password" value={loginForm.secret}
            onChange={(e) => setLoginForm({ ...loginForm, secret: e.target.value })}
            onKeyDown={(e) => { if (e.key === 'Enter') doLogin() }} style={{ padding: 8 }} />
          <button onClick={doLogin} style={{ padding: '10px', background: '#2d6cdf', color: '#fff', border: 0, borderRadius: 6, cursor: 'pointer' }}>로그인</button>
        </div>
        {err && <p style={{ color: '#c00', marginTop: 12 }}>{err}</p>}
      </section>
    )
  }
  if (mustChange) {
    return (
      <section style={{ ...box, maxWidth: 380, margin: '40px auto', background: '#fff8f0' }}>
        <h2 style={{ marginTop: 0 }}>🔑 초기 비밀번호 변경</h2>
        <p style={{ color: '#666', fontSize: 13 }}>본격 사용 전 초기 비밀번호를 변경해야 합니다.</p>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <input placeholder="새 비밀번호" type="password" value={newSecret}
            onChange={(e) => setNewSecret(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter') doChangePassword() }} style={{ padding: 8 }} />
          <button onClick={doChangePassword} style={{ padding: '10px', background: '#c0392b', color: '#fff', border: 0, borderRadius: 6, cursor: 'pointer' }}>변경</button>
          <button onClick={logout} style={{ fontSize: 12 }}>로그아웃</button>
        </div>
        {err && <p style={{ color: '#c00', marginTop: 12 }}>{err}</p>}
      </section>
    )
  }

  return (
    <div>
      <div style={{ textAlign: 'right', color: '#666', fontSize: 13, marginTop: 8 }}>
        👤 <b>{loginUser}</b> (ADMIN) <button onClick={logout} style={{ fontSize: 12, marginLeft: 8 }}>로그아웃</button>
      </div>
      {/* 서비스 상태(B5 하트비트) */}
      <section style={{ ...box, background: '#f7fff7' }}>
        <h2 style={{ marginTop: 0 }}>🩺 서비스 상태 <span style={{ fontSize: 12, color: '#888' }}>(하트비트 기반 · 헤드리스 포함)</span></h2>
        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
          {health.length === 0 && <span style={{ color: '#888' }}>하트비트 수집 대기…</span>}
          {health.map((h) => (
            <span key={String(h.service) + h.center} style={{ padding: '4px 10px', borderRadius: 6, fontSize: 13, background: h.status === 'UP' ? '#e6f4ea' : '#fde8e8', border: '1px solid ' + (h.status === 'UP' ? '#8bd3a0' : '#f0a0a0') }}>
              {h.status === 'UP' ? '🟢' : '🔴'} <b>{String(h.service)}</b> <span style={{ color: '#666' }}>({String(h.center)}·pid {String(h.pid)}·{Math.round(Number(h.ageMs) / 1000)}s)</span>
            </span>
          ))}
        </div>
      </section>

      {/* 대사 대시보드 */}
      <section style={{ ...box, background: '#f7fbff' }}>
        <h2 style={{ marginTop: 0 }}>📊 대사(정합성) 대시보드 <button onClick={loadAll} style={{ fontSize: 12 }}>새로고침</button></h2>
        {summary && (
          <div style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
            <div>총 거래: <b>{summary.transfers}</b></div>
            <div>최대 전역순번: <b>{summary.maxSeq}</b></div>
            <div>계좌수: <b>{summary.accounts}</b></div>
            <div>총액: <b style={{ fontFamily: 'monospace' }}>{won(summary.totalBalance)}</b> {totalOk ? <span style={{ color: '#1a7f37' }}>✓ 보존</span> : <span style={{ color: '#c00' }}>⚠ 확인</span>}</div>
            <div>상태분포: {summary.byStatus?.map((b: any) => <span key={b.status} style={{ marginRight: 8 }}><code>{b.status}</code> {b.cnt}</span>)}</div>
          </div>
        )}
        <MiniTable title="원장(transfer) — Dior 접수→Hermes 결제→Prada 완결"
          cols={[['global_seq', '순번'], ['bmi', 'BMI'], ['status', '상태'], ['sender_code', '송신'], ['receiver_code', '수신'], ['amount', '금액'], ['updated_at', '최종수정']]}
          rows={transfers} timeCols={['updated_at']} />
        <MiniTable title="선저널(journal_log) — Hermes write-ahead"
          cols={[['global_seq', '순번'], ['bmi', 'BMI'], ['applied', '적용']]} rows={journal} />
        <MiniTable title="조회사본(settlement_view) — Prada"
          cols={[['global_seq', '순번'], ['bmi', 'BMI'], ['final_status', '최종상태'], ['debtor_balance_after', '송신잔액'], ['creditor_balance_after', '수신잔액'], ['finalized_at', '완결시각']]}
          rows={views} timeCols={['finalized_at']} />
      </section>

      {/* DB 초기화 */}
      <section style={box}>
        <h2 style={{ marginTop: 0 }}>♻️ 테스트용 DB 초기화</h2>
        <p style={{ color: '#555' }}>거래/저널/조회사본/원전문을 모두 비우고 계좌 잔액을 각 10억으로 되돌립니다. (Kafka 저널 이력은 보존)</p>
        <button onClick={doReset} style={{ padding: '10px 20px', background: '#c0392b', color: '#fff', border: 0, borderRadius: 6, cursor: 'pointer' }}>DB 초기화 실행</button>
      </section>

      {/* 기관/코드 관리 */}
      <section style={box}>
        <h2 style={{ marginTop: 0 }}>🏛 코드 관리 — 참가기관 마스터</h2>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'flex-end', marginBottom: 8 }}>
          <label>기관코드(4자리)<br /><input value={inst.code} onChange={(e) => setInst({ ...inst, code: e.target.value })} style={{ padding: 6, width: 100, fontFamily: 'monospace' }} /></label>
          <label>기관명<br /><input value={inst.name} onChange={(e) => setInst({ ...inst, name: e.target.value })} style={{ padding: 6, width: 160 }} /></label>
          <label>초기잔액<br /><input type="number" value={inst.balance} onChange={(e) => setInst({ ...inst, balance: Number(e.target.value) })} style={{ padding: 6, width: 140 }} /></label>
          <button onClick={saveInst} style={{ padding: '8px 16px' }}>추가/수정</button>
        </div>
        <table style={{ borderCollapse: 'collapse', width: '100%' }}>
          <thead><tr><th style={th}>코드</th><th style={th}>기관명</th><th style={{ ...th, textAlign: 'right' }}>잔액</th><th style={th}></th></tr></thead>
          <tbody>{insts.map((a) => (
            <tr key={a.code}>
              <td style={{ ...cell, fontFamily: 'monospace' }}>{a.code}</td><td style={cell}>{a.name}</td>
              <td style={{ ...cell, textAlign: 'right', fontFamily: 'monospace' }}>{won(a.balance)}</td>
              <td style={cell}><button onClick={() => setInst({ code: a.code, name: a.name, balance: a.balance })} style={{ fontSize: 11 }}>편집</button> <button onClick={() => delInst(a.code)} style={{ fontSize: 11, color: '#c00' }}>삭제</button></td>
            </tr>))}</tbody>
        </table>
        <MiniTable title="처리상태 코드(데이터 사전)" cols={[['code', '코드'], ['name', '한글명'], ['desc', '설명']]} rows={codes} />
      </section>

      {/* 사용자 권한 관리 */}
      <section style={box}>
        <h2 style={{ marginTop: 0 }}>👤 사용자 권한 관리 <span style={{ fontSize: 12, color: '#888' }}>(관리 마스터 · ADMIN 로그인 적용됨)</span></h2>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'flex-end', marginBottom: 8 }}>
          <label>사용자ID<br /><input value={user.username} onChange={(e) => setUser({ ...user, username: e.target.value })} style={{ padding: 6, width: 120 }} /></label>
          <label>사용자명<br /><input value={user.displayName} onChange={(e) => setUser({ ...user, displayName: e.target.value })} style={{ padding: 6, width: 140 }} /></label>
          <label>역할<br /><select value={user.role} onChange={(e) => setUser({ ...user, role: e.target.value })} style={{ padding: 6 }}>
            <option value="ADMIN">ADMIN(관리자)</option><option value="ORG_S">ORG_S(송신기관)</option><option value="ORG_R">ORG_R(수신기관)</option>
          </select></label>
          <label>매핑기관<br /><input value={user.orgCode} onChange={(e) => setUser({ ...user, orgCode: e.target.value })} placeholder="1001" style={{ padding: 6, width: 90, fontFamily: 'monospace' }} /></label>
          <button onClick={saveUser} style={{ padding: '8px 16px' }}>추가/수정</button>
        </div>
        <table style={{ borderCollapse: 'collapse', width: '100%' }}>
          <thead><tr><th style={th}>사용자ID</th><th style={th}>사용자명</th><th style={th}>역할</th><th style={th}>매핑기관</th><th style={th}></th></tr></thead>
          <tbody>{users.map((u) => (
            <tr key={u.username}>
              <td style={{ ...cell, fontFamily: 'monospace' }}>{u.username}</td><td style={cell}>{u.displayName}</td>
              <td style={cell}><b>{u.role}</b></td><td style={{ ...cell, fontFamily: 'monospace' }}>{u.orgCode ?? '-'}</td>
              <td style={cell}><button onClick={() => delUser(u.username)} style={{ fontSize: 11, color: '#c00' }}>삭제</button></td>
            </tr>))}</tbody>
        </table>
      </section>

      {/* 테스트 실행 (B6) */}
      <section style={box}>
        <h2 style={{ marginTop: 0 }}>🧪 테스트 실행 — 이체 대량 생성</h2>
        <p style={{ color: '#555' }}>송신/수신기관 <b>랜덤</b>, 금액 <b>1,000~1,000,000원 랜덤</b>, BMI는 시작번호부터 순번 증가.
          (Chanel 직접 호출 — 관문/인증 우회. k6 대체용 간이 도구)</p>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'flex-end', marginBottom: 8 }}>
          <label>BMI 시작번호<br /><input value={test.bmiStart} onChange={(e) => setTest({ ...test, bmiStart: e.target.value })} style={{ padding: 6, width: 220, fontFamily: 'monospace' }} /></label>
          <label>건수<br /><input type="number" value={test.count} onChange={(e) => setTest({ ...test, count: Number(e.target.value) })} style={{ padding: 6, width: 100 }} /></label>
          <button onClick={runTest} disabled={testRun?.running} style={{ padding: '8px 16px', background: '#2d6cdf', color: '#fff', border: 0, borderRadius: 6, cursor: 'pointer' }}>{testRun?.running ? '실행 중…' : '실행'}</button>
        </div>
        {testRun && (
          <div style={{ fontFamily: 'monospace', fontSize: 13 }}>
            진행 <b>{testRun.done}/{testRun.sent}</b> · 접수(RCVD) {testRun.rcvd} · 반려(RJCT) {testRun.rjct} · 오류 {testRun.err}
          </div>
        )}
      </section>

      {msg && <p style={{ color: '#1a7f37', marginTop: 12 }}>{msg}</p>}
      {err && <p style={{ color: '#c00', marginTop: 12 }}>{err}</p>}
    </div>
  )
}
