/* Interactive tech stack — click an item, see code example + plain-language outcome */

function getPublicContact() {
  return window.__PUBLIC_DATA__?.contact || {};
}

const contact = getPublicContact();

const STACK = [
  {
    id: 'php',
    abbr: 'PHP',
    color: '#8993be',
    name: 'PHP / MySQL',
    tag: 'формы, админки, старые сайты',
    badge: 'починю что есть',
    headline: 'Чиню формы, оптимизирую старые сайты',
    sub: 'Самописные PHP-сайты на десять лет — это норма. Прихожу в существующий код, нахожу причину, чиню без переписывания всего.',
    lang: 'php',
    snippet: `<?php
// форма роняла письма в спам — добавил SPF-заголовки и нормальный mailer
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host       = getenv('SMTP_HOST');
$mail->SMTPAuth   = true;
$mail->Username   = getenv('SMTP_USER');
$mail->Password   = getenv('SMTP_PASS');
$mail->setFrom('noreply@your-site.ru', 'Заявки');
$mail->addAddress($manager);
$mail->Subject    = 'Новая заявка';
$mail->Body       = htmlspecialchars($message);

if (!$mail->send()) {
    error_log("mail: " . $mail->ErrorInfo);
    return ['ok' => false, 'reason' => 'mailer'];
}`,
    outcomes: [
      'Заявки снова доходят до менеджера',
      'Можно понять, на каком шаге форма ломается',
      'Логи говорят, что пошло не так',
      'Спам-фильтры перестают резать письма',
    ],
  },
  {
    id: 'python',
    abbr: 'PY',
    color: '#79b8ff',
    name: 'Python · FastAPI',
    tag: 'backend, скрипты, автоматизация',
    badge: 'автоматизирую рутину',
    headline: 'Backend-сервисы и автоматизация',
    sub: 'FastAPI для небольших backend-сервисов, скрипты-парсеры, выгрузки прайсов, обработка заявок. Всё, что можно делать руками — пусть делает скрипт.',
    lang: 'python',
    snippet: `# FastAPI-эндпоинт принимает заявки с сайта и шлёт в Telegram + CRM
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel, EmailStr

app = FastAPI()

class Lead(BaseModel):
    name: str
    contact: str
    message: str

@app.post("/lead")
async def lead(l: Lead, bg: BackgroundTasks):
    bg.add_task(send_telegram, l)
    bg.add_task(push_to_crm, l)
    return {"ok": True}`,
    outcomes: [
      'Заявки мгновенно прилетают в Telegram',
      'Прайс обновляется сам каждую ночь',
      'Менеджер не копирует данные руками',
      'Понятный API под мобильное приложение',
    ],
  },
  {
    id: 'docker',
    abbr: 'DK',
    color: '#2496ed',
    name: 'Docker · Compose',
    tag: 'один файл — весь проект',
    badge: 'упакую проект',
    headline: 'Один docker-compose — весь проект',
    sub: 'Сайт, база, кеш, фоновые задачи — описаны в одном файле. Перенос на другой сервер занимает 10 минут, а не неделю.',
    lang: 'yaml',
    snippet: `# docker-compose.yml — сайт + база + nginx, всё в одной команде
services:
  app:
    build: .
    restart: unless-stopped
    env_file: .env
    depends_on: [db]
  db:
    image: postgres:16-alpine
    volumes: ['./pgdata:/var/lib/postgresql/data']
    environment:
      POSTGRES_PASSWORD: \${DB_PASS}
  nginx:
    image: nginx:alpine
    ports: ['80:80', '443:443']
    volumes: ['./nginx.conf:/etc/nginx/nginx.conf:ro']`,
    outcomes: [
      'Переезд на другой сервер — за один вечер',
      'Локальная копия для тестов — одной командой',
      'Никаких "у меня работало"',
      'Бэкапы и обновления — предсказуемые',
    ],
  },
  {
    id: 'nginx',
    abbr: 'NX',
    color: '#009639',
    name: 'Nginx · SSL',
    tag: 'reverse proxy, HTTPS',
    badge: 'настрою сервер',
    headline: 'Reverse proxy, HTTPS и редиректы',
    sub: 'Сайт по HTTPS, www → без www, старые URL → новые, кеш статики, защита админки по IP. Всё в одном понятном конфиге.',
    lang: 'nginx',
    snippet: `# nginx — HTTPS + редиректы + защита админки
server {
  listen 443 ssl http2;
  server_name your-site.ru;

  ssl_certificate     /etc/letsencrypt/live/your-site.ru/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/your-site.ru/privkey.pem;

  location /admin/ {
    allow 203.0.113.0/24;   # офис
    deny  all;
    proxy_pass http://app:8000;
  }

  location / {
    proxy_pass http://app:8000;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}`,
    outcomes: [
      'Сайт открывается по HTTPS, замочек в браузере',
      'Старые ссылки в Яндексе не теряются',
      'Админка закрыта для посторонних',
      'Картинки и CSS грузятся быстрее',
    ],
  },
  {
    id: 'seo',
    abbr: 'SE',
    color: '#f7df1e',
    name: 'schema.org · SEO',
    tag: 'микроразметка, индексация',
    badge: 'покажу поисковику',
    headline: 'Микроразметка и техническое SEO',
    sub: 'JSON-LD для статей, услуг, организации и хлебных крошек. robots.txt, sitemap, canonical и редиректы — чтобы поисковик увидел все нужные страницы.',
    lang: 'json',
    snippet: `{
  "@context": "https://schema.org",
  "@type": "Service",
  "serviceType": "Технический SEO-аудит",
  "provider": {
    "@type": "Person",
    "name": "${contact.specialist_name || ''}",
    "url": "https://app.winetlab.site/"
  },
  "areaServed": "RU",
  "offers": {
    "@type": "Offer",
    "price": "2000",
    "priceCurrency": "RUB"
  }
}`,
    outcomes: [
      'Яндекс и Google понимают, что у вас за бизнес',
      'В выдаче появляются хлебные крошки, цены, рейтинг',
      'Лишние страницы перестают попадать в индекс',
      'Понятно, что чинить в первую очередь',
    ],
  },
  {
    id: 'api',
    abbr: 'API',
    color: '#c590e0',
    name: 'API-интеграции',
    tag: 'CRM, мессенджеры, выгрузки',
    badge: 'свяжу системы',
    headline: 'Связываю системы между собой',
    sub: 'Заявки с сайта → в CRM. Прайсы поставщика → в каталог. Уведомления → в Telegram. Без танцев с экспортом-импортом руками.',
    lang: 'python',
    snippet: `# заявки с сайта летят в Telegram и amoCRM одновременно
async def push_to_crm(lead: Lead):
    async with httpx.AsyncClient() as c:
        r = await c.post(
            f"{AMO_URL}/api/v4/leads",
            headers={"Authorization": f"Bearer {AMO_TOKEN}"},
            json=[{
                "name": lead.name,
                "price": 0,
                "custom_fields_values": [
                    {"field_code": "PHONE", "values": [{"value": lead.contact}]},
                    {"field_code": "DESC",  "values": [{"value": lead.message}]},
                ],
            }],
        )
        r.raise_for_status()`,
    outcomes: [
      'Менеджер видит заявки в CRM, а не в почте',
      'Прайс обновляется без копи-пасты',
      'Уведомления — там, где их прочитают',
      'Возвраты, статусы, чеки — автоматом',
    ],
  },
];

function Stack() {
  const [active, setActive] = React.useState(STACK[0].id);
  const item = STACK.find(s => s.id === active);

  return (
    <section id="stack">
      <div className="container">
        <Reveal as="div" className="section-label">// интерактивный стек</Reveal>
        <Reveal as="h2" className="section-title" delay={60}>
          Кликните <em>по технологии</em> — покажу пример
        </Reveal>
        <Reveal as="p" className="section-lead" delay={120}>
          С чем работаю каждый день. Слева — стек, справа — конкретный кусок кода из реальной задачи и понятным языком что это даёт бизнесу.
        </Reveal>

        <Reveal className="stack-wrap" delay={180}>
          <div className="stack-list" role="tablist">
            {STACK.map(s => (
              <button
                key={s.id}
                role="tab"
                aria-selected={active === s.id}
                className={`stack-item ${active === s.id ? 'active' : ''}`}
                onClick={() => setActive(s.id)}
              >
                <div className="stack-icon" style={{ color: s.color }}>{s.abbr}</div>
                <div className="stack-item-text">
                  <div className="name">{s.name}</div>
                  <div className="tag">{s.tag}</div>
                </div>
              </button>
            ))}
          </div>

          <div className="stack-panel" key={item.id}>
            <div className="stack-panel-head">
              <span className="badge">{item.badge}</span>
              <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>~ /examples/{item.id}.{item.lang === 'python' ? 'py' : item.lang === 'php' ? 'php' : item.lang === 'yaml' ? 'yml' : item.lang === 'nginx' ? 'conf' : 'json'}</span>
            </div>
            <h3>{item.headline}</h3>
            <p className="sub">{item.sub}</p>

            <div className="stack-snippet" data-lang={item.lang}>
              <pre>
                <CodeHighlight code={item.snippet} lang={item.lang} />
              </pre>
            </div>

            <div className="stack-outcomes">
              {item.outcomes.map((o, i) => (
                <div key={i} className="row">
                  <span className="ico">→</span>
                  <span>{o}</span>
                </div>
              ))}
            </div>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

/* Lightweight syntax highlighter
   Uses sentinel chars (\x01 \x02 \x03) as markers so subsequent regex passes
   never re-scan injected markup. Final pass converts sentinels to real spans. */
function CodeHighlight({ code, lang }) {
  const KW = {
    php:    /\b(use|new|return|if|else|getenv|true|false|null|public|function|foreach|as|require|require_once|namespace)\b/g,
    python: /\b(from|import|async|def|return|if|else|elif|None|True|False|with|as|await|for|in|raise|try|except)\b/g,
    nginx:  /\b(server|listen|server_name|ssl_certificate|ssl_certificate_key|location|allow|deny|proxy_pass|proxy_set_header|root|index)\b/g,
    yaml:   /^(\s*)([a-z_-]+):/gmi,
    json:   /"([^"]+)":/g,
  };

  const html = React.useMemo(() => {
    // 1) escape HTML first
    let s = code
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;');

    // 2) wrap matches with sentinels — \x01<cls>\x02<body>\x03
    const wrap = (cls) => (m) => `\x01${cls}\x02${m}\x03`;

    // Comments first (so strings inside comments aren't double-wrapped)
    if (lang === 'python' || lang === 'yaml' || lang === 'nginx') {
      s = s.replace(/(^|\n)(\s*)(#[^\n]*)/g, (_, a, b, c) => a + b + `\x01term-comment\x02${c}\x03`);
    }
    if (lang === 'php') {
      s = s.replace(/(\/\/[^\n]*)/g, wrap('term-comment'));
    }
    // Strings
    s = s.replace(/("(?:[^"\\]|\\.)*")/g, wrap('term-str'));
    s = s.replace(/('(?:[^'\\]|\\.)*')/g, wrap('term-str'));
    // Numbers (word boundary so sentinels — which are control chars — won't match)
    s = s.replace(/\b(\d+(?:\.\d+)?)\b/g, wrap('term-num'));
    // Keywords
    const kw = KW[lang];
    if (kw) s = s.replace(kw, wrap('term-key'));

    // 3) convert sentinels to real spans. Open and close are independent —
    //    nesting is preserved naturally because wraps always emit balanced pairs.
    s = s.replace(/\x01([a-z-]+)\x02/g, '<span class="$1">');
    s = s.replace(/\x03/g, '</span>');

    return s;
  }, [code, lang]);

  return <code dangerouslySetInnerHTML={{ __html: html }} />;
}

window.Stack = Stack;
