/**
 * NK CAPTCHA — Embeddable Web Component
 *
 * Usage:
 *   <script src="nk-captcha.js"></script>
 *   <nk-captcha></nk-captcha>
 *
 * Attributes:
 *   locale     — "ko", "en", or "both" (default: "both")
 *   timeout    — seconds (default: 60)
 *   theme      — "dark" or "light" (default: "dark")
 *   on-verify  — callback function name on window (receives {pass, challengeId, similarity, transcript})
 *
 * Events:
 *   nk-captcha-verified — CustomEvent with detail {pass, challengeId, code, similarity}
 *
 * Example:
 *   <nk-captcha locale="both" timeout="60" on-verify="onNkResult"></nk-captcha>
 *   <script>
 *     function onNkResult(result) { console.log(result.pass ? 'PASS' : 'FAIL'); }
 *   </script>
 */
(function () {
  const CHALLENGES = [
    {id:"dictator-1",ko:"김정은은 독재자이다",en:"Kim Jong-un is a dictator"},
    {id:"dictator-2",ko:"김정은은 미치광이 독재자이다",en:"Kim Jong-un is a deranged dictator"},
    {id:"human-rights-1",ko:"북한 정권은 인권을 탄압한다",en:"The North Korean regime suppresses human rights"},
    {id:"freedom-1",ko:"북한에는 자유가 없다",en:"There is no freedom in North Korea"},
    {id:"failed-state-1",ko:"김정은 정권은 실패한 체제이다",en:"The Kim Jong-un regime is a failed system"},
    {id:"juche-1",ko:"주체사상은 거짓 이념이다",en:"Juche ideology is a false ideology"},
    {id:"liberation-1",ko:"북한 주민들은 해방되어야 한다",en:"The North Korean people must be liberated"},
    {id:"dynasty-1",ko:"김씨 왕조는 끝나야 한다",en:"The Kim dynasty must end"},
    {id:"propaganda-1",ko:"북한의 선전은 모두 거짓말이다",en:"North Korean propaganda is all lies"},
    {id:"gulags-1",ko:"정치범 수용소를 폐쇄하라",en:"Shut down the political prison camps"},
    {id:"pink-pig-1",ko:"김정은은 뚱뚱한 분홍 돼지다",en:"Kim Jong-un is a fat pink pig"},
    {id:"clown-1",ko:"김정은은 핵무기를 가진 광대다",en:"Kim Jong-un is a clown with nuclear weapons"},
    {id:"coward-1",ko:"김정은은 국민이 두려운 겁쟁이다",en:"Kim Jong-un is a coward afraid of his own people"},
    {id:"media-pathetic-1",ko:"김정은은 한심한 독재자다",en:"Kim Jong-un is a pathetic dictator"},
    {id:"media-joke-1",ko:"김정은은 세계의 웃음거리다",en:"Kim Jong-un is a laughingstock of the world"},
    {id:"media-slaves-1",ko:"북한은 국민을 노예로 만든다",en:"North Korea enslaves its own people"},
    {id:"media-collapse-1",ko:"북한 정권은 반드시 무너진다",en:"The North Korean regime will surely collapse"},
    {id:"media-fat-1",ko:"김정은은 국민이 굶주리는데 혼자만 살찐 돼지다",en:"Kim Jong-un is a fat pig while his people starve"},
  ];

  function lev(a, b) {
    const m = Array.from({length: b.length + 1}, (_, i) => [i]);
    for (let j = 0; j <= a.length; j++) m[0][j] = j;
    for (let i = 1; i <= b.length; i++)
      for (let j = 1; j <= a.length; j++)
        m[i][j] = Math.min(m[i-1][j]+1, m[i][j-1]+1, m[i-1][j-1]+(b[i-1]===a[j-1]?0:1));
    return m[b.length][a.length];
  }

  function sim(a, b) {
    if (!a || !b) return 0;
    const la = a.toLowerCase(), lb = b.toLowerCase();
    if (la === lb) return 1;
    const d = lev(la, lb);
    return (Math.max(la.length, lb.length) - d) / Math.max(la.length, lb.length);
  }

  function esc(c) { return c.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }

  class NkCaptcha extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
      this._challenge = null;
      this._timer = null;
      this._timeLeft = 60;
    }

    connectedCallback() {
      this._timeout = parseInt(this.getAttribute('timeout') || '60');
      this._locale = this.getAttribute('locale') || 'both';
      this._theme = this.getAttribute('theme') || 'dark';
      this._onVerify = this.getAttribute('on-verify');
      this.render();
    }

    disconnectedCallback() {
      clearInterval(this._timer);
    }

    pick() {
      return CHALLENGES[Math.floor(Math.random() * CHALLENGES.length)];
    }

    emitResult(result) {
      this.dispatchEvent(new CustomEvent('nk-captcha-verified', { detail: result, bubbles: true }));
      if (this._onVerify && typeof window[this._onVerify] === 'function') {
        window[this._onVerify](result);
      }
    }

    render() {
      const isDark = this._theme === 'dark';
      const bg = isDark ? '#080808' : '#fafafa';
      const surface = isDark ? '#111' : '#fff';
      const border = isDark ? '#2a2a2a' : '#e0e0e0';
      const text = isDark ? '#e5e5e5' : '#1a1a1a';
      const textDim = isDark ? '#737373' : '#888';
      const red = '#dc2626';
      const green = '#22c55e';
      const gold = '#f59e0b';

      this.shadowRoot.innerHTML = `
<style>
  :host { display: block; font-family: -apple-system, 'Noto Sans KR', sans-serif; }
  * { margin: 0; padding: 0; box-sizing: border-box; }

  .wrap {
    background: ${bg};
    border: 1px solid ${border};
    border-radius: 8px;
    max-width: 480px;
    overflow: hidden;
    position: relative;
  }

  .header {
    background: ${red};
    color: #fff;
    padding: 12px 20px;
    font-family: 'Courier New', monospace;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.15em;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .header svg { width: 20px; height: 20px; }

  .body { padding: 24px 20px; }

  .screen { display: none; flex-direction: column; gap: 16px; }
  .screen.active { display: flex; }

  .desc {
    font-size: 13px;
    color: ${textDim};
    text-align: center;
    line-height: 1.6;
  }

  .phrase-box {
    background: ${surface};
    border: 1px solid ${border};
    padding: 16px;
    text-align: center;
    border-radius: 4px;
  }
  .phrase-ko {
    font-size: 1.3rem;
    font-weight: 900;
    color: ${text};
    line-height: 1.4;
  }
  .phrase-en {
    font-size: 0.8rem;
    color: ${textDim};
    margin-top: 6px;
    font-style: italic;
  }

  input {
    width: 100%;
    background: ${isDark ? '#0a0a0a' : '#f5f5f5'};
    border: 1px solid ${border};
    color: ${text};
    font-family: 'Courier New', monospace;
    font-size: 1rem;
    padding: 12px 14px;
    border-radius: 4px;
    outline: none;
    transition: border-color 0.2s;
  }
  input:focus { border-color: ${red}; }
  input::placeholder { color: ${textDim}; opacity: 0.5; }

  .feedback {
    font-family: 'Courier New', monospace;
    font-size: 0.8rem;
    min-height: 20px;
    word-break: break-all;
    line-height: 1.5;
  }
  .c-ok { color: ${green}; }
  .c-err { color: ${red}; text-decoration: underline; }
  .c-miss { color: ${textDim}; opacity: 0.25; }

  .meter {
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .meter-label {
    font-family: 'Courier New', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    color: ${textDim};
  }
  .meter-bar {
    flex: 1;
    height: 4px;
    background: ${isDark ? '#1a1a1a' : '#e5e5e5'};
    border-radius: 2px;
    overflow: hidden;
  }
  .meter-fill {
    height: 100%;
    width: 0%;
    background: ${red};
    transition: width 0.2s, background 0.3s;
    border-radius: 2px;
  }
  .meter-fill.mid { background: ${gold}; }
  .meter-fill.high { background: ${green}; }
  .meter-pct {
    font-family: 'Courier New', monospace;
    font-size: 0.75rem;
    font-weight: 600;
    color: ${textDim};
    min-width: 32px;
    text-align: right;
  }

  .timer-bar {
    height: 2px;
    background: ${isDark ? '#1a1a1a' : '#e5e5e5'};
    overflow: hidden;
  }
  .timer-fill {
    height: 100%;
    background: ${red};
    transition: width 1s linear;
  }

  button {
    background: transparent;
    border: 1.5px solid ${red};
    color: ${red};
    font-family: 'Courier New', monospace;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    padding: 11px 32px;
    cursor: pointer;
    transition: all 0.2s;
    border-radius: 3px;
    align-self: center;
  }
  button:hover { background: ${red}11; box-shadow: 0 0 20px ${red}33; }
  button.disabled { opacity: 0.25; pointer-events: none; }

  .btn-sm {
    border-color: ${border};
    color: ${textDim};
    padding: 8px 20px;
    font-size: 0.7rem;
  }
  .btn-sm:hover { border-color: ${textDim}; color: ${text}; }

  .result-icon { text-align: center; font-size: 56px; }
  .result-title {
    font-family: 'Courier New', monospace;
    font-size: 1.6rem;
    font-weight: 800;
    letter-spacing: 0.15em;
    text-align: center;
  }
  .pass-title { color: ${green}; }
  .fail-title { color: ${red}; }
  .result-sub {
    font-size: 0.8rem;
    color: ${textDim};
    text-align: center;
  }
  .badge {
    border: 1px solid ${green};
    padding: 6px 18px;
    text-align: center;
    font-family: 'Courier New', monospace;
    font-size: 0.65rem;
    letter-spacing: 0.15em;
    font-weight: 600;
    color: ${green};
    border-radius: 2px;
    align-self: center;
  }
  .badge-fail { border-color: ${red}; color: ${red}; }
  .code {
    font-family: 'Courier New', monospace;
    font-size: 0.7rem;
    color: ${textDim};
    text-align: center;
    padding: 6px 12px;
    background: ${surface};
    border: 1px solid ${border};
    border-radius: 2px;
    align-self: center;
  }
</style>

<div class="wrap">
  <div class="header">
    <span>NK CAPTCHA</span>
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
      <circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/>
    </svg>
  </div>
  <div class="timer-bar"><div class="timer-fill" id="tf" style="width:100%"></div></div>
  <div class="body">
    <!-- INTRO -->
    <div class="screen active" id="s-intro">
      <div class="desc">
        Verify you are <strong>not</strong> a DPRK operative.<br>
        Type the anti-regime phrase to pass.
      </div>
      <button id="btn-start">BEGIN VERIFICATION</button>
    </div>
    <!-- CHALLENGE -->
    <div class="screen" id="s-challenge">
      <div class="phrase-box">
        <div class="phrase-ko" id="p-ko"></div>
        <div class="phrase-en" id="p-en"></div>
      </div>
      <input id="inp" autocomplete="off" spellcheck="false" placeholder="Type the phrase here..." />
      <div class="feedback" id="fb"></div>
      <div class="meter">
        <span class="meter-label">MATCH</span>
        <div class="meter-bar"><div class="meter-fill" id="mf"></div></div>
        <span class="meter-pct" id="mp">0%</span>
      </div>
      <button id="btn-verify" class="disabled">VERIFY</button>
    </div>
    <!-- PASS -->
    <div class="screen" id="s-pass">
      <div class="result-icon">✅</div>
      <div class="result-title pass-title">VERIFIED</div>
      <div class="result-sub">Identity Confirmed — Not a DPRK Operative</div>
      <div class="badge">NOT A DPRK OPERATIVE</div>
      <div class="code" id="v-code"></div>
    </div>
    <!-- FAIL -->
    <div class="screen" id="s-fail">
      <div class="result-icon">🚫</div>
      <div class="result-title fail-title">FAILED</div>
      <div class="result-sub">Could not verify identity</div>
      <div class="badge badge-fail">POTENTIAL DPRK OPERATIVE</div>
      <button class="btn-sm" id="btn-retry">RETRY</button>
    </div>
  </div>
</div>`;

      const $ = (id) => this.shadowRoot.getElementById(id);
      const show = (id) => {
        this.shadowRoot.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
        $(id).classList.add('active');
      };

      $('btn-start').onclick = () => {
        this._challenge = this.pick();
        $('p-ko').textContent = this._challenge.ko;
        $('p-en').textContent = this._challenge.en;
        $('inp').value = '';
        $('fb').innerHTML = '';
        $('mf').style.width = '0%';
        $('mp').textContent = '0%';
        $('btn-verify').classList.add('disabled');
        show('s-challenge');
        $('inp').focus();

        this._timeLeft = this._timeout;
        $('tf').style.width = '100%';
        this._timer = setInterval(() => {
          this._timeLeft--;
          $('tf').style.width = ((this._timeLeft / this._timeout) * 100) + '%';
          if (this._timeLeft <= 0) {
            clearInterval(this._timer);
            this.emitResult({ pass: false, challengeId: this._challenge.id, reason: 'timeout' });
            show('s-fail');
          }
        }, 1000);
      };

      $('inp').oninput = () => {
        const v = $('inp').value;
        const c = this._challenge;
        const sKo = sim(v, c.ko), sEn = sim(v, c.en);
        const best = Math.max(sKo, sEn);
        const target = sKo >= sEn ? c.ko : c.en;
        const pct = Math.round(best * 100);

        $('mf').style.width = pct + '%';
        $('mf').className = 'meter-fill' + (pct >= 90 ? ' high' : pct >= 50 ? ' mid' : '');
        $('mp').textContent = pct + '%';

        let html = '';
        for (let i = 0; i < Math.max(v.length, target.length); i++) {
          if (i < v.length && i < target.length) {
            html += v[i] === target[i]
              ? '<span class="c-ok">' + esc(v[i]) + '</span>'
              : '<span class="c-err">' + esc(v[i]) + '</span>';
          } else if (i >= v.length) {
            html += '<span class="c-miss">' + esc(target[i]) + '</span>';
          }
        }
        $('fb').innerHTML = html;

        if (pct >= 80) $('btn-verify').classList.remove('disabled');
        else $('btn-verify').classList.add('disabled');
      };

      $('inp').onkeydown = (e) => { if (e.key === 'Enter') $('btn-verify').click(); };

      $('btn-verify').onclick = () => {
        if ($('btn-verify').classList.contains('disabled')) return;
        clearInterval(this._timer);
        const v = $('inp').value.trim();
        const c = this._challenge;
        const best = Math.max(sim(v, c.ko), sim(v, c.en));
        const pass = best >= 0.9;
        const code = 'NKCAP-' + Date.now().toString(36).toUpperCase() + '-' + Math.random().toString(36).substring(2,8).toUpperCase();

        if (pass) {
          $('v-code').textContent = code;
          show('s-pass');
        } else {
          show('s-fail');
        }
        this.emitResult({ pass, challengeId: c.id, similarity: best, code: pass ? code : null });
      };

      $('btn-retry').onclick = () => {
        clearInterval(this._timer);
        show('s-intro');
      };
    }
  }

  customElements.define('nk-captcha', NkCaptcha);
})();
