/* global React */
const { useState, useEffect, useRef, useMemo } = React;

const KPOS_LANG = window.KPOS_LANG || 'ru';
const KK = {
  'payments.sh — live': 'payments.sh — live',
  'QR': 'QR',
  'IDLE': 'IDLE',
  'Статус': 'Күй',
  'Подготовка запроса…': 'Сұраныс дайындалуда…',
  'QR создан · ожидаем оплату': 'QR жасалды · төлемді күтудеміз',
  'Ожидаем оплату клиента…': 'Клиенттің төлемін күтудеміз…',
  '✓ Оплата получена · webhook отправлен': '✓ Төлем қабылданды · webhook жіберілді',
  'назад': 'бұрын',
  'Алматы · кофейня «Тары»': 'Алматы · «Тары» кофеханасы',
  'Астана · магазин цветов': 'Астана · гүл дүкені',
  'Шымкент · пекарня «Нан»': 'Шымкент · «Нан» наубайханасы',
  'Караганда · доставка через Telegram': 'Қарағанды · Telegram арқылы жеткізу',
  'Алматы · барбершоп': 'Алматы · барбершоп',
  'Павлодар · онлайн-курс английского': 'Павлодар · ағылшын тілі онлайн-курсы',
  'Атырау · автомойка': 'Атырау · автожуу',
  'Тараз · магазин автозапчастей': 'Тараз · автобөлшектер дүкені',
  'Алматы · прокат самокатов': 'Алматы · самокат жалдау',
  'Усть-Каменогорск · доставка суши': 'Өскемен · суши жеткізу',
  'Оборот в месяц': 'Айлық айналым',
  'Ваша комиссия': 'Сіздің комиссияңыз',
  'Экономия vs банк*': 'Банкпен салыстырғандағы үнем*',
  'в месяц': 'айына',
  '/ мес': '/ ай',
  'от': 'бастап',
  'до': 'дейін',
  '* Условный банковский тариф: 4% + абонентская 30 000 ₸/мес.': '* Шартты банк тарифі: 4% + 30 000 ₸/ай абоненттік төлем.',
  'Ставка снижается автоматически по накопительному обороту.': 'Мөлшерлеме жинақталған айналымға қарай автоматты түрде төмендейді.',
  'Нет успешных платежей — счёт = 0 ₸.': 'Сәтті төлемдер жоқ — шот = 0 ₸.',
  'Часто спрашивают': 'Жиі сұралады',
  'Зачем делиться доступом к Kaspi?': 'Kaspi-ге қол жеткізуді не үшін бөлісу керек?',
  'Вы не передаёте пароль или PIN — никогда. В приложении Kaspi создаётся отдельная «роль кассира» с предельно узкими правами: принимать оплату и видеть свою историю операций. Сервису вы сообщаете только номер телефона этой роли и одноразовый SMS-код для активации.':
    'Сіз ешқашан құпиясөзді немесе PIN-кодты бермейсіз. Kaspi қосымшасында өте шектеулі құқықтары бар жеке «кассир рөлі» жасалады: төлем қабылдау және өз операцияларының тарихын көру. Сервиске тек осы рөлдің телефон нөмірін және белсендіруге арналған бір реттік SMS-кодты хабарлайсыз.',
  'Куда приходят деньги?': 'Ақша қайда түседі?',
  'Напрямую на вашу торговую точку внутри инфраструктуры Kaspi. Средства покупателей никогда не поступают на счета сервиса — мы их физически не касаемся, не храним и не перенаправляем. Только метаданные о факте платежа.':
    'Тікелей Kaspi инфрақұрылымындағы сіздің сауда нүктеңізге. Сатып алушылардың қаражаты ешқашан сервистің шотына түспейді — біз оған физикалық тұрғыда тиіспейміз, сақтамаймыз және қайта бағыттамаймыз. Тек төлем фактісі туралы метадеректер.',
  'Это легально?': 'Бұл заңды ма?',
  'Да. Вы используете собственный Kaspi-аккаунт через штатный механизм роли кассира — точно так же, как если бы вы наняли сотрудника. Сервис автоматизирует приём платежей, но не является банком, платёжной организацией или официальным партнёром Kaspi.':
    'Иә. Сіз өз Kaspi-аккаунтыңызды кассир рөлінің стандартты механизмі арқылы пайдаланасыз — қызметкер жалдағандағыдай. Сервис төлем қабылдауды автоматтандырады, бірақ банк, төлем ұйымы немесе Kaspi-дің ресми серіктесі болып табылмайды.',
  'Почему не интегрироваться с Kaspi напрямую?': 'Неге Kaspi-мен тікелей интеграцияланбасқа?',
  'У Kaspi нет публичного платёжного API для большинства бизнесов — оно доступно только крупным мерчантам с эквайринговым договором. Сервис берёт на себя протокол, подписи запросов, обновление сессий и доставку вебхуков — вам остаётся простой REST API.':
    'Kaspi-де бизнестердің көпшілігі үшін жария төлем API жоқ — ол тек эквайринг шарты бар ірі мерчанттарға қолжетімді. Сервис протоколды, сұраныс қолтаңбаларын, сессияларды жаңартуды және webhook жеткізуді өзіне алады — сізге қарапайым REST API қалады.',
  'Какая комиссия и есть ли скрытые сборы?': 'Комиссия қандай және жасырын төлемдер бар ма?',
  'Комиссия снимается только с успешных платежей — от 4% (старт) до 1% (от 5 млн ₸ оборота). Никакой абонентской платы, платы за подключение, минимальных оборотов или сборов за «активацию ключа». Нет платежей — 0 ₸.':
    'Комиссия тек сәтті төлемдерден алынады — 4%-дан (бастау) 1%-ға дейін (5 млн ₸ айналымнан). Абоненттік төлем, қосылу ақысы, ең төменгі айналым немесе «кілтті белсендіру» алымы жоқ. Төлемдер жоқ — 0 ₸.',
  'Что если Kaspi сменит протокол?': 'Егер Kaspi протоколды өзгертсе ше?',
  'Это произошло уже трижды за два года — и каждый раз ваш код продолжал работать без правок. В этом и смысл сервиса: вы интегрируетесь в наш REST, мы держим контракт стабильным и адаптируемся под изменения Kaspi внутри.':
    'Бұл екі жыл ішінде үш рет болды — әр жолы сіздің кодыңыз өзгеріссіз жұмыс істей берді. Сервистің мәні де осында: сіз біздің REST-ке интеграцияланасыз, біз келісімшартты тұрақты ұстаймыз және Kaspi өзгерістеріне ішкі деңгейде бейімделеміз.',
  'Как протестировать интеграцию?': 'Интеграцияны қалай тексеруге болады?',
  'Отдельного sandbox-окружения пока нет. Чтобы проверить интеграцию от и до, создайте QR на минимальную сумму — это настоящий платёж, но он проходит весь путь целиком: оплата, смена статуса и webhook. Возврат тоже доступен через API.':
    'Жеке sandbox-ортасы әзірге жоқ. Интеграцияны толық тексеру үшін ең аз сомаға QR жасаңыз — бұл нақты төлем, бірақ ол бүкіл жолдан өтеді: төлем, күй ауысуы және webhook. Қайтару да API арқылы қолжетімді.',
  'Главные': 'Басты',
  'сомнения.': 'күмәндар.',
};
const t = (s) => (KPOS_LANG === 'kk' ? (KK[s] || s) : s);

/* ─────────────────────────────────────────────────────────────
   QR code — pseudo-pattern, decorative
   ───────────────────────────────────────────────────────────── */
function QRArt({ seed = 1 }) {
  const grid = useMemo(() => {
    const size = 21;
    const cells = [];
    let s = seed * 9301 + 49297;
    const rnd = () => {
      s = (s * 9301 + 49297) % 233280;
      return s / 233280;
    };
    for (let y = 0; y < size; y++) {
      for (let x = 0; x < size; x++) {
        cells.push(rnd() > 0.55 ? 1 : 0);
      }
    }
    // finder squares (corners)
    const drawFinder = (cx, cy) => {
      for (let y = 0; y < 7; y++)
        for (let x = 0; x < 7; x++) {
          const i = (cy + y) * size + (cx + x);
          const ring = x === 0 || x === 6 || y === 0 || y === 6;
          const inner = x >= 2 && x <= 4 && y >= 2 && y <= 4;
          cells[i] = ring || inner ? 1 : 0;
        }
    };
    drawFinder(0, 0);
    drawFinder(size - 7, 0);
    drawFinder(0, size - 7);
    return { size, cells };
  }, [seed]);
  return (
    <svg viewBox={`0 0 ${grid.size} ${grid.size}`} shapeRendering="crispEdges">
      {grid.cells.map((c, i) =>
        c ? (
          <rect
            key={i}
            x={i % grid.size}
            y={Math.floor(i / grid.size)}
            width="1"
            height="1"
            fill="#0f0f10"
          />
        ) : null
      )}
    </svg>
  );
}

/* ─────────────────────────────────────────────────────────────
   Hero live demo: types curl → shows JSON → renders QR → "paid"
   Loops infinitely.
   ───────────────────────────────────────────────────────────── */
const DEMO_SCRIPT = [
  { t: 'cmd', s: '$ ', d: 100 },
  { t: 'type', s: 'curl -X POST https://api.kaspipos.kz/qr/create \\', d: 24 },
  { t: 'newline', d: 80 },
  { t: 'type', s: '  -H "X-Api-Key: kpos_live_7f2a..." \\', d: 18 },
  { t: 'newline', d: 80 },
  { t: 'type', s: '  -d \'{"amount": 5000, "ref": "ORDER-9821"}\'', d: 18 },
  { t: 'newline', d: 500 },
  { t: 'response', d: 100 },
];

function HeroDemo() {
  const [lines, setLines] = useState([]);
  const [phase, setPhase] = useState('typing'); // typing | json | qr | paid
  const [tick, setTick] = useState(0);
  const idx = useRef(0);
  const charIdx = useRef(0);
  const timer = useRef(null);

  useEffect(() => {
    let alive = true;

    function reset() {
      idx.current = 0;
      charIdx.current = 0;
      setLines([]);
      setPhase('typing');
      step();
    }

    function step() {
      if (!alive) return;
      if (idx.current >= DEMO_SCRIPT.length) {
        // sequence: show JSON, then QR, then "paid", then loop
        setPhase('json');
        timer.current = setTimeout(() => {
          if (!alive) return;
          setPhase('qr');
          timer.current = setTimeout(() => {
            if (!alive) return;
            setPhase('paid');
            timer.current = setTimeout(() => {
              if (!alive) return;
              reset();
            }, 2800);
          }, 2200);
        }, 1400);
        return;
      }
      const cur = DEMO_SCRIPT[idx.current];
      if (cur.t === 'cmd') {
        setLines((ls) => [...ls, { t: 'cmd', s: cur.s }]);
        idx.current++;
        timer.current = setTimeout(step, cur.d);
      } else if (cur.t === 'newline') {
        setLines((ls) => [...ls, { t: 'blank', s: '' }]);
        idx.current++;
        charIdx.current = 0;
        timer.current = setTimeout(step, cur.d);
      } else if (cur.t === 'type') {
        if (charIdx.current === 0) {
          setLines((ls) => [...ls, { t: 'type', s: '' }]);
        }
        charIdx.current++;
        setLines((ls) => {
          const out = ls.slice();
          out[out.length - 1] = { t: 'type', s: cur.s.slice(0, charIdx.current) };
          return out;
        });
        if (charIdx.current >= cur.s.length) {
          idx.current++;
          charIdx.current = 0;
          timer.current = setTimeout(step, 220);
        } else {
          timer.current = setTimeout(step, cur.d);
        }
      } else if (cur.t === 'response') {
        idx.current++;
        timer.current = setTimeout(step, 60);
      }
    }

    reset();
    return () => {
      alive = false;
      clearTimeout(timer.current);
    };
  }, [tick]);

  // jiggle qr seed
  const [seed, setSeed] = useState(7);
  useEffect(() => {
    if (phase === 'qr') setSeed(Math.floor(Math.random() * 999) + 1);
  }, [phase]);

  return (
    <div className="demo">
      <div className="demo-bar">
        <span className="light r" />
        <span className="light y" />
        <span className="light g" />
        <span className="f">{t('payments.sh — live')}</span>
      </div>
      <div className="demo-body">
        {lines.map((l, i) => {
          if (l.t === 'cmd')
            return (
              <div className="ln" key={i}>
                <span className="c-com">{l.s}</span>
              </div>
            );
          if (l.t === 'blank') return <div className="ln" key={i}>&nbsp;</div>;
          // type: highlight strings + nums quickly
          return (
            <div className="ln" key={i}>
              {renderLine(l.s)}
              {i === lines.length - 1 && phase === 'typing' && <span className="caret" />}
            </div>
          );
        })}
        {(phase === 'json' || phase === 'qr' || phase === 'paid') && (
          <>
            <div className="ln">&nbsp;</div>
            <div className="ln"><span className="c-com"># Response 201 Created · 84ms</span></div>
            <div className="ln">{'{'}</div>
            <div className="ln">{'  '}<span className="c-key">"QrOperationId"</span>: <span className="c-num">78921034</span>,</div>
            <div className="ln">{'  '}<span className="c-key">"QrToken"</span>: <span className="c-str">"https://pay.kaspi.kz/pay/A1F3..."</span>,</div>
            <div className="ln">{'  '}<span className="c-key">"Amount"</span>: <span className="c-num">5000</span>,</div>
            <div className="ln">{'  '}<span className="c-key">"ExpiresIn"</span>: <span className="c-num">300</span></div>
            <div className="ln">{'}'}</div>
          </>
        )}
      </div>
      <div className="demo-bottom">
        <div className="demo-qr">
          {phase === 'qr' || phase === 'paid' ? (
            <QRArt seed={seed} />
          ) : (
            <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10, color: '#6b6a64', letterSpacing: '0.04em' }}>
              {t('QR')}
              <br />
              {t('IDLE')}
            </div>
          )}
        </div>
        <div className="demo-state">
          <div className="label">{t('Статус')}</div>
          <div className={'v ' + (phase === 'paid' ? 'ok' : '')}>
            {phase === 'typing' && t('Подготовка запроса…')}
            {phase === 'json' && t('QR создан · ожидаем оплату')}
            {phase === 'qr' && t('Ожидаем оплату клиента…')}
            {phase === 'paid' && t('✓ Оплата получена · webhook отправлен')}
          </div>
        </div>
        <div className="demo-amt">
          <div className="amount">5 000 ₸</div>
          <div className="sub">ORDER-9821</div>
        </div>
      </div>
    </div>
  );
}

function renderLine(s) {
  // colorize strings, numbers
  const parts = [];
  let i = 0;
  let key = 0;
  while (i < s.length) {
    if (s[i] === '"') {
      const end = s.indexOf('"', i + 1);
      if (end === -1) {
        parts.push(<span key={key++}>{s.slice(i)}</span>);
        break;
      }
      parts.push(<span className="c-str" key={key++}>{s.slice(i, end + 1)}</span>);
      i = end + 1;
    } else if (s[i] === "'") {
      const end = s.indexOf("'", i + 1);
      if (end === -1) {
        parts.push(<span key={key++}>{s.slice(i)}</span>);
        break;
      }
      parts.push(<span className="c-str" key={key++}>{s.slice(i, end + 1)}</span>);
      i = end + 1;
    } else if (/\d/.test(s[i])) {
      let end = i;
      while (end < s.length && /[\d]/.test(s[end])) end++;
      parts.push(<span className="c-num" key={key++}>{s.slice(i, end)}</span>);
      i = end;
    } else {
      let end = i;
      while (end < s.length && s[end] !== '"' && s[end] !== "'" && !/\d/.test(s[end])) end++;
      parts.push(<span className="c-cmd" key={key++}>{s.slice(i, end)}</span>);
      i = end;
    }
  }
  return parts;
}

/* ─────────────────────────────────────────────────────────────
   Ticker
   ───────────────────────────────────────────────────────────── */
const TICKER_ITEMS = [
  { amt: '1 290 ₸', who: 'Алматы · кофейня «Тары»', t: '2с' },
  { amt: '6 500 ₸', who: 'Астана · магазин цветов', t: '5с' },
  { amt: '2 400 ₸', who: 'Шымкент · пекарня «Нан»', t: '8с' },
  { amt: '3 800 ₸', who: 'Караганда · доставка через Telegram', t: '11с' },
  { amt: '5 000 ₸', who: 'Алматы · барбершоп', t: '14с' },
  { amt: '24 900 ₸', who: 'Павлодар · онлайн-курс английского', t: '17с' },
  { amt: '3 000 ₸', who: 'Атырау · автомойка', t: '20с' },
  { amt: '47 500 ₸', who: 'Тараз · магазин автозапчастей', t: '23с' },
  { amt: '1 200 ₸', who: 'Алматы · прокат самокатов', t: '26с' },
  { amt: '12 700 ₸', who: 'Усть-Каменогорск · доставка суши', t: '29с' },
];

function Ticker() {
  const items = [...TICKER_ITEMS, ...TICKER_ITEMS];
  return (
    <div className="ticker" aria-hidden="true">
      <div className="track">
        {items.map((it, i) => (
          <div className="item" key={i}>
            <span className="amt">{it.amt}</span>
            <span>→</span>
            <span>{t(it.who)}</span>
            <span className="time">· {it.t} {t('назад')}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────────
   Code tabs (curl / js / python / php)
   ───────────────────────────────────────────────────────────── */
const SNIPPETS = {
  curl: `# Создать QR-платёж — один запрос
curl -X POST https://api.kaspipos.kz/api/qr/create \\
  -H "X-Api-Key: kpos_7f2a..." \\
  -H "Content-Type: application/json" \\
  -d '{ "amount": 5000 }'`,
  js: `// Node · обычный fetch, отдельный SDK не нужен
const res = await fetch('https://api.kaspipos.kz/api/qr/create', {
  method: 'POST',
  headers: {
    'X-Api-Key': process.env.KPOS_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ amount: 5000 }),
})

const payment = await res.json()
console.log(payment.Data.QrToken)  // → pay.kaspi.kz/pay/...`,
  python: `# Python · requests
import os, requests

res = requests.post(
    'https://api.kaspipos.kz/api/qr/create',
    headers={'X-Api-Key': os.environ['KPOS_KEY']},
    json={'amount': 5000},
)

print(res.json()['Data']['QrToken'])  # → pay.kaspi.kz/pay/...`,
  php: `<?php
// PHP · cURL
$ch = curl_init('https://api.kaspipos.kz/api/qr/create');
curl_setopt_array($ch, [
  CURLOPT_POST           => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => [
    'X-Api-Key: ' . getenv('KPOS_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS     => '{"amount": 5000}',
]);

$payment = json_decode(curl_exec($ch), true);
echo $payment['Data']['QrToken'];`,
};

function colorize(code, lang) {
  // crude but fine for display
  const tokens = [];
  let buf = '';
  let i = 0;
  const push = (cls) => {
    if (buf) {
      tokens.push({ cls, s: buf });
      buf = '';
    }
  };
  while (i < code.length) {
    const c = code[i];
    // comments
    if ((c === '#' && (lang === 'curl' || lang === 'python')) || (c === '/' && code[i + 1] === '/')) {
      push('cmd');
      let end = code.indexOf('\n', i);
      if (end === -1) end = code.length;
      tokens.push({ cls: 'c-com', s: code.slice(i, end) });
      i = end;
      continue;
    }
    // strings
    if (c === '"' || c === "'") {
      push('cmd');
      const q = c;
      let end = i + 1;
      while (end < code.length && code[end] !== q) {
        if (code[end] === '\\') end++;
        end++;
      }
      tokens.push({ cls: 'c-str', s: code.slice(i, end + 1) });
      i = end + 1;
      continue;
    }
    // numbers
    if (/[0-9]/.test(c) && !/[a-zA-Z_]/.test(code[i - 1] || '')) {
      push('cmd');
      let end = i;
      while (end < code.length && /[0-9]/.test(code[end])) end++;
      tokens.push({ cls: 'c-num', s: code.slice(i, end) });
      i = end;
      continue;
    }
    // keywords
    const kw = /^(const|import|from|await|new|console|log|print|echo|use|getenv|os|process|env)\b/.exec(
      code.slice(i)
    );
    if (kw && !/[a-zA-Z_0-9]/.test(code[i - 1] || '')) {
      push('cmd');
      tokens.push({ cls: 'c-key', s: kw[0] });
      i += kw[0].length;
      continue;
    }
    buf += c;
    i++;
  }
  push('cmd');
  return tokens.map((t, k) => (
    <span key={k} className={t.cls === 'cmd' ? '' : t.cls}>
      {t.s}
    </span>
  ));
}

function CodePlayground() {
  const [tab, setTab] = useState('curl');
  return (
    <div className="playground">
      <div className="lang-tabs">
        {['curl', 'js', 'python', 'php'].map((t) => (
          <div key={t} className={'lang-tab ' + (tab === t ? 'active' : '')} onClick={() => setTab(t)}>
            {t === 'js' ? 'NODE' : t.toUpperCase()}
          </div>
        ))}
      </div>
      <pre>{colorize(SNIPPETS[tab], tab)}</pre>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────────
   Webhook console — animated event list
   ───────────────────────────────────────────────────────────── */
const EVENT_POOL = [
  { evt: 'payment.success', amt: '5 000 ₸', ref: 'ORDER-9821', stat: 'ok' },
  { evt: 'payment.success', amt: '12 400 ₸', ref: 'SUB-RENEW-3401', stat: 'ok' },
  { evt: 'payment.success', amt: '49 900 ₸', ref: 'CART-7712', stat: 'ok' },
  { evt: 'payment.expired', amt: '3 200 ₸', ref: 'BOT-MSG-981', stat: 'exp' },
  { evt: 'payment.success', amt: '125 000 ₸', ref: 'INV-2026-0418', stat: 'ok' },
  { evt: 'payment.failed', amt: '8 750 ₸', ref: 'ORDER-9831', stat: 'fail' },
  { evt: 'payment.success', amt: '1 990 ₸', ref: 'SUB-PLAN-PRO', stat: 'ok' },
  { evt: 'payment.success', amt: '67 000 ₸', ref: 'REPAIR-451', stat: 'ok' },
  { evt: 'payment.success', amt: '2 500 ₸', ref: 'CART-1182', stat: 'ok' },
  { evt: 'payment.success', amt: '18 600 ₸', ref: 'CART-7798', stat: 'ok' },
];

function WebhookConsole() {
  const [events, setEvents] = useState([]);
  const counter = useRef(0);
  useEffect(() => {
    // seed with a few
    let n = 0;
    function nextTime() {
      const d = new Date();
      d.setSeconds(d.getSeconds() - n * 3);
      return d.toLocaleTimeString('ru-RU', { hour12: false });
    }
    setEvents(
      Array.from({ length: 4 }).map(() => {
        const base = EVENT_POOL[Math.floor(Math.random() * EVENT_POOL.length)];
        return { ...base, id: counter.current++, t: nextTime() };
      })
    );
    n = 0;
    const id = setInterval(() => {
      const base = EVENT_POOL[Math.floor(Math.random() * EVENT_POOL.length)];
      const newEvt = {
        ...base,
        id: counter.current++,
        t: new Date().toLocaleTimeString('ru-RU', { hour12: false }),
      };
      setEvents((evts) => [newEvt, ...evts].slice(0, 9));
    }, 2200);
    return () => clearInterval(id);
  }, []);
  return (
    <div className="console">
      <div className="console-head">
        <span>webhooks · prod</span>
        <span style={{ marginLeft: 12, color: 'rgba(244, 240, 230, 0.4)' }}>→ https://yourapp.kz/webhook</span>
        <span className="live">LIVE</span>
      </div>
      <div className="console-body">
        {events.map((e) => (
          <div className="event" key={e.id}>
            <span className="t">{e.t}</span>
            <span className="evt">
              <b>{e.evt}</b> <span className="acc">{e.amt}</span> · {e.ref}
            </span>
            <span className={'stat ' + e.stat}>
              {e.stat === 'ok' && '200 OK'}
              {e.stat === 'fail' && 'FAILED'}
              {e.stat === 'exp' && 'EXPIRED'}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────────
   Pricing calculator
   ───────────────────────────────────────────────────────────── */
const TIERS = [
  { upTo: 500_000, fee: 4.0 },
  { upTo: 1_000_000, fee: 3.0 },
  { upTo: 2_000_000, fee: 2.5 },
  { upTo: 3_000_000, fee: 2.0 },
  { upTo: 5_000_000, fee: 1.5 },
  { upTo: Infinity, fee: 1.0 },
];

function feeFor(volume) {
  for (const t of TIERS) if (volume <= t.upTo) return t.fee;
  return 1.0;
}

function fmt(n) {
  return n.toLocaleString('ru-RU').replaceAll(',', ' ');
}

function PricingCalc() {
  const [vol, setVol] = useState(800_000);
  const fee = feeFor(vol);
  const cost = Math.round((vol * fee) / 100);
  // competitor: 4% flat + 30 000 monthly
  const competitor = Math.round(vol * 0.04 + 30000);
  const saved = competitor - cost;
  const activeTierIdx = TIERS.findIndex((t) => vol <= t.upTo);
  return (
    <div className="price-wrap">
      <div className="calc">
        <div className="label">{t('Оборот в месяц')}</div>
        <div className="volume">
          {fmt(vol)} <span className="cur">₸</span>
        </div>
        <input
          className="slider"
          type="range"
          min={100000}
          max={6000000}
          step={50000}
          value={vol}
          onChange={(e) => setVol(parseInt(e.target.value, 10))}
        />
        <div className="range-marks">
          <span>100K</span>
          <span>1M</span>
          <span>3M</span>
          <span>6M+</span>
        </div>
        <div className="calc-result">
          <div className="cell">
            <div className="k">{t('Ваша комиссия')}</div>
            <div className="v acid">
              {fee.toFixed(fee % 1 === 0 ? 0 : 1)}<span className="it">%</span>
            </div>
            <div className="sub">{fmt(cost)} ₸ {t('/ мес')}</div>
          </div>
          <div className="cell">
            <div className="k">{t('Экономия vs банк*')}</div>
            <div className="v">
              {fmt(Math.max(0, saved))} <span className="it">₸</span>
            </div>
            <div className="sub">{t('в месяц')}</div>
          </div>
        </div>
      </div>
      <div>
        <div className="tiers">
          {TIERS.map((tier, i) => (
            <div className={'tier-row ' + (i === activeTierIdx ? 'active' : '')} key={i}>
              <div className="vol">
                {i === TIERS.length - 1
                  ? `${t('от')} ${fmt(TIERS[i - 1].upTo)} ₸`
                  : `${t('до')} ${fmt(tier.upTo)} ₸`}
              </div>
              <div className="fee">
                {tier.fee.toFixed(tier.fee % 1 === 0 ? 0 : 1)}
                <span className="it">%</span>
              </div>
            </div>
          ))}
        </div>
        <p
          style={{
            marginTop: 18,
            fontFamily: 'JetBrains Mono',
            fontSize: 11.5,
            color: 'var(--mute)',
            letterSpacing: '0.04em',
            textTransform: 'uppercase',
            lineHeight: 1.7,
          }}
        >
          {t('* Условный банковский тариф: 4% + абонентская 30 000 ₸/мес.')}<br />
          {t('Ставка снижается автоматически по накопительному обороту.')}<br />
          {t('Нет успешных платежей — счёт = 0 ₸.')}
        </p>
      </div>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────────
   FAQ
   ───────────────────────────────────────────────────────────── */
const FAQ_ITEMS = [
  {
    q: 'Зачем делиться доступом к Kaspi?',
    a: 'Вы не передаёте пароль или PIN — никогда. В приложении Kaspi создаётся отдельная «роль кассира» с предельно узкими правами: принимать оплату и видеть свою историю операций. Сервису вы сообщаете только номер телефона этой роли и одноразовый SMS-код для активации.',
  },
  {
    q: 'Куда приходят деньги?',
    a: 'Напрямую на вашу торговую точку внутри инфраструктуры Kaspi. Средства покупателей никогда не поступают на счета сервиса — мы их физически не касаемся, не храним и не перенаправляем. Только метаданные о факте платежа.',
  },
  {
    q: 'Это легально?',
    a: 'Да. Вы используете собственный Kaspi-аккаунт через штатный механизм роли кассира — точно так же, как если бы вы наняли сотрудника. Сервис автоматизирует приём платежей, но не является банком, платёжной организацией или официальным партнёром Kaspi.',
  },
  {
    q: 'Почему не интегрироваться с Kaspi напрямую?',
    a: 'У Kaspi нет публичного платёжного API для большинства бизнесов — оно доступно только крупным мерчантам с эквайринговым договором. Сервис берёт на себя протокол, подписи запросов, обновление сессий и доставку вебхуков — вам остаётся простой REST API.',
  },
  {
    q: 'Какая комиссия и есть ли скрытые сборы?',
    a: 'Комиссия снимается только с успешных платежей — от 4% (старт) до 1% (от 5 млн ₸ оборота). Никакой абонентской платы, платы за подключение, минимальных оборотов или сборов за «активацию ключа». Нет платежей — 0 ₸.',
  },
  {
    q: 'Что если Kaspi сменит протокол?',
    a: 'Это произошло уже трижды за два года — и каждый раз ваш код продолжал работать без правок. В этом и смысл сервиса: вы интегрируетесь в наш REST, мы держим контракт стабильным и адаптируемся под изменения Kaspi внутри.',
  },
  {
    q: 'Как протестировать интеграцию?',
    a: 'Отдельного sandbox-окружения пока нет. Чтобы проверить интеграцию от и до, создайте QR на минимальную сумму — это настоящий платёж, но он проходит весь путь целиком: оплата, смена статуса и webhook. Возврат тоже доступен через API.',
  },
];

function FAQ() {
  return (
    <div className="faq-grid">
      <div className="left">
        <span className="eyebrow-acid">{t('Часто спрашивают')}</span>
        <h2 className="h-title" style={{ marginTop: 18 }}>
          {t('Главные')} <span className="it">{t('сомнения.')}</span>
        </h2>
      </div>
      <div className="faq-list">
        {FAQ_ITEMS.map((it, i) => (
          <details className="faq-item" key={i} open={i === 0}>
            <summary>
              <span className="qn">Q.{String(i + 1).padStart(2, '0')}</span>
              <span>{t(it.q)}</span>
              <span className="plus">+</span>
            </summary>
            <div className="a">{t(it.a)}</div>
          </details>
        ))}
      </div>
    </div>
  );
}

/* expose for app.jsx */
Object.assign(window, {
  HeroDemo,
  Ticker,
  CodePlayground,
  WebhookConsole,
  PricingCalc,
  FAQ,
  QRArt,
});
