// Keys section — Screens 6 (slots), 7 (empty), 9 (checkout), 10 (migration).
// Per spec: URL не показывается в карточке слота. Только token_prefix + статусы из контракта.

// Features ниже отражают единый продукт «Fortune VPN Premium»: 3 seats (1 = 1 active device или slot),
// до 2 mobile devices + до 2 external slots (V2Ray/Incy). Источник: PREMIUM_ENTITLEMENT_MODEL_V2.md.
const PREMIUM_FEATURES = [
  "До 3 устройств одновременно",
  "V2Ray / Incy сейчас · Android и iOS после релиза",
  "Без ограничений по трафику и скорости",
  "Серверы обновляются автоматически",
];
// Бизнес-правило (2026-05-12): подписка ровно на 1 месяц, без долгосрочных тарифов.
// Рынок быстро меняется — не делаем коммитов длиннее 30 дней.
const PREMIUM_PLAN = {
  code: "premium_1m",
  period: "1 месяц",
  price: "99 ₽",
  priceNumeric: 99,
  per: "один платёж",
  summary: "30 дней · до 3 подключений",
  features: PREMIUM_FEATURES,
};
const TEST_PLAN = {
  code: "premium_test_7d",
  period: "7 дней",
  price: "30 ₽",
  priceNumeric: 30,
  per: "один платёж",
  summary: "7 дней · 1 слот · без реф-бонусов",
  features: [
    "1 подключение для проверки сервиса",
    "V2Ray / Incy и web-кабинет",
    "Серверы обновляются автоматически",
    "Реферальный бонус не начисляется",
  ],
};
const KEY_PLANS = [PREMIUM_PLAN, TEST_PLAN];

// Маленький статистик-блок «X из Y» — для seats / mobile / external.
function SeatStat({ label, used, max }) {
  const pct = max > 0 ? Math.min(100, Math.round((used / max) * 100)) : 0;
  const isFull = used >= max && max > 0;
  return (
    <div>
      <div style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: 0.6, marginBottom: 8 }}>{label}</div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginBottom: 8 }}>
        <span style={{ fontSize: 28, fontWeight: 800, letterSpacing: "-0.03em", color: isFull ? "var(--warning)" : "#FFF" }}>{used}</span>
        <span style={{ fontSize: 14, color: "var(--muted)" }}>из {max}</span>
      </div>
      <div style={{ height: 4, borderRadius: 999, background: "var(--panel)", overflow: "hidden" }}>
        <div style={{
          height: "100%", width: `${pct}%`,
          background: isFull ? "var(--warning)" : "var(--grad)",
          transition: "width .2s",
        }} />
      </div>
    </div>
  );
}

// статус-копи из глоссария брифа
const STATUS_COPY = {
  active:        { label: "Активна",     kind: "active" },
  pending_apply: { label: "Готовится",   kind: "pending" },
  expired:       { label: "Истекла",     kind: "danger" },
  revoked:       { label: "Отозвана",    kind: "pending" },
  abuse_hold:    { label: "Заблокирована",kind: "danger" },
};

function KeysPage({ state = "active" }) {
  if (state === "empty")     return <Keys_Empty />;
  if (state === "checkout")  return <Keys_Checkout />;
  if (state === "migration") return <Keys_Migration />;
  return <Keys_Active />;
}

function Keys_Active() {
  const s = useAppState();
  const ent = s.entitlement;
  const expires = window.fmtDate(ent.expires_at);
  const activeSlots = s.slots.filter((x) => x.status !== "revoked");
  const revokedSlots = s.slots.filter((x) => x.status === "revoked");
  const externalDisabled = window.hasCapabilityHold?.("external_subscription_url") || !s.premium?.capabilities?.external_subscription_url;

  const lim = s.limits;
  const gate = window.canBuySubscription();
  const renewBtn = gate.canBuy
    ? <button className="btn btn-primary" onClick={() => window.navigate("keys-checkout")}>Продлить</button>
    : null;
  return (
    <Shell active="keys" screenLabel="06 Subscription">
      <SectionHeader
        eyebrow="ПОДПИСКА"
        title="Ваша подписка"
        badge={<Badge kind="active">{`Активна · до ${expires}`}</Badge>}
        action={renewBtn}
      />
      {!gate.canBuy && (
        <div style={{ marginBottom: 16 }}>
          <RenewalLockedNote msLeft={gate.msLeft} />
        </div>
      )}

      {/* Сводка: всего подключений + лимиты по типу */}
      <div className="ps" style={{ padding: 20, marginBottom: 16 }}>
        <div data-grid="cards" style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 16 }}>
          <SeatStat label="Подключений всего" used={lim.seats_used} max={lim.max_seats} />
          <SeatStat label="Мобильные устройства" used={lim.mobile_devices_used} max={lim.max_mobile_devices_total} />
          <SeatStat label="Ссылки V2Ray / Incy" used={lim.external_slots_used} max={lim.max_external_slots} />
        </div>
      </div>

      {/* Мобильные устройства — placeholder "скоро" */}
      <div style={{ marginBottom: 18 }}>
        <div className="eyebrow" style={{ fontSize: 11, marginBottom: 10 }}>МОБИЛЬНЫЕ УСТРОЙСТВА</div>
        <div data-grid="cards" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <MobileSoonCard platform="Android" />
          <MobileSoonCard platform="iOS" />
        </div>
        <div style={{ marginTop: 8, fontSize: 12.5, color: "var(--dim)" }}>
          Приложения для Android и iOS — в разработке. Каждое подключённое устройство займёт одно из {lim.max_seats} подключений.
        </div>
      </div>

      {/* Ссылки V2Ray / Incy */}
      <div className="eyebrow" style={{ fontSize: 11, marginBottom: 10 }}>ССЫЛКИ ДЛЯ V2RAY / INCY</div>
      {externalDisabled && <CapabilityHoldBanner capability="external_subscription_url" />}
      {activeSlots.map((slot, i) => (
        <React.Fragment key={slot.id}>
          {i > 0 && <div style={{ height: 14 }} />}
          <KeySlot slot={slot} />
        </React.Fragment>
      ))}

      {ent.slots_available > 0 && activeSlots.length > 0 && !externalDisabled && (
        <><div style={{ height: 14 }} /><AddSlotCTA /></>
      )}

      {activeSlots.length === 0 && !externalDisabled && (
        <PsCard style={{ textAlign: "center", padding: "32px 24px" }}>
          <div style={{ fontSize: 15, color: "var(--muted)", marginBottom: 14 }}>
            Все ссылки отозваны. Создайте новую, чтобы подключиться.
          </div>
          <button className="btn btn-primary" onClick={() => window.actions.openCreateSlot()}>Создать ссылку</button>
        </PsCard>
      )}

      {revokedSlots.length > 0 && (
        <details style={{ marginTop: 14 }}>
          <summary style={{ cursor: "pointer", color: "var(--muted)", fontSize: 13 }}>
            Отозванные ссылки ({revokedSlots.length})
          </summary>
          <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 10 }}>
            {revokedSlots.map((slot) => <KeySlot key={slot.id} slot={slot} />)}
          </div>
        </details>
      )}

      <PsCard style={{ marginTop: 18 }}>
        <h3 style={{ margin: "0 0 12px", fontSize: 18, fontWeight: 700 }}>Как подключить</h3>
        <div data-grid="cards" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
          <ClientHowTo name="V2Ray / v2rayNG / v2rayN" steps={["Установите клиент из официального магазина или GitHub", "Скопируйте subscription URL из модального окна", "Откройте раздел подписок / groups / subscription URL", "Добавьте ссылку как подписку и нажмите обновить"]} />
          <ClientHowTo name="Incy"  steps={["Установите Incy", "Скопируйте subscription URL из модального окна", "Откройте Subscribe URL / подписки", "Вставьте ссылку и обновите подписку"]} />
        </div>
        <div style={{ marginTop: 12, fontSize: 13, color: "var(--dim)" }}>
          Серверы обновляются автоматически — клиент сам подтянет изменения.
        </div>
      </PsCard>
    </Shell>
  );
}

function CapabilityHoldBanner({ capability }) {
  const isAccountHold = window.appState?.premium?.capability_holds?.account_access;
  const copy = isAccountHold
    ? "Доступ к подписке временно ограничен проверкой аккаунта."
    : "Создание и обновление ссылок V2Ray / Incy временно ограничено проверкой.";
  return (
    <div style={{
      display: "flex", gap: 10, alignItems: "flex-start", marginBottom: 14,
      padding: "12px 14px", borderRadius: 12,
      background: "rgba(245,166,35,.08)", border: "1px solid rgba(245,166,35,.25)", fontSize: 13.5,
    }}>
      <span style={{ color: "var(--warning)", flexShrink: 0, marginTop: 1 }}><Icons.alert size={16} /></span>
      <span>
        {copy} Напишите в поддержку:{" "}
        <a href="https://t.me/FortuneVPNsupportbot" target="_blank" rel="noopener" style={{ color: "var(--accent-soft)", textDecoration: "none" }}>@FortuneVPNsupportbot</a>
        {" "}или{" "}
        <a href="mailto:support@fortunetavern.com" style={{ color: "var(--accent-soft)", textDecoration: "none" }}>support@fortunetavern.com</a>.
      </span>
    </div>
  );
}

function KeySlot({ slot }) {
  const { id, slot_number, client_label, token_prefix, expires_at, last_used_at, status, can_regenerate, can_revoke } = slot;
  const stat = STATUS_COPY[status] || STATUS_COPY.active;
  const isPending = status === "pending_apply";
  const isAbuse   = status === "abuse_hold";
  const isRevoked = status === "revoked";
  const externalDisabled = window.hasCapabilityHold?.("external_subscription_url") || !window.appState?.premium?.capabilities?.external_subscription_url;
  const expires = window.fmtDate(expires_at);
  const lastUsed = last_used_at ? window.fmtDate(last_used_at) : "Ещё не использовался";

  return (
    <div className="ps" style={{
      borderColor: isAbuse ? "rgba(229,72,77,.4)" : isPending ? "rgba(245,166,35,.35)" : "var(--border)",
      opacity: isRevoked ? 0.55 : 1,
    }}>
      <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12, marginBottom: 14 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <div className="ps-icon" style={{ marginBottom: 0, width: 40, height: 40 }}>
            <Icon size={18}><circle cx="8" cy="15" r="4" /><path d="M11 12l9-9" /><path d="M17 6l3 3" /></Icon>
          </div>
          <div>
            <div style={{ fontSize: 16, fontWeight: 600 }}>{client_label}</div>
            <div style={{ fontSize: 12.5, color: "var(--muted)" }}>Ссылка {slot_number} из {window.appState?.limits?.max_external_slots || 2}</div>
          </div>
        </div>
        <Badge kind={stat.kind}>{stat.label}</Badge>
      </div>

      {isPending && (
        <div style={{
          display: "flex", gap: 10, alignItems: "flex-start", marginBottom: 14,
          padding: "10px 12px", borderRadius: 10,
          background: "rgba(245,166,35,.08)", border: "1px solid rgba(245,166,35,.25)", fontSize: 13.5,
        }}>
          <span style={{ color: "var(--warning)", flexShrink: 0, marginTop: 1 }}><Icons.info size={16} /></span>
          <span>Ссылка создана, но сервер ещё применяет конфигурацию. До активации V2Ray/Incy могут показывать ошибку при импорте. Обычно занимает несколько минут.</span>
        </div>
      )}
      {isAbuse && (
        <div style={{
          display: "flex", gap: 10, alignItems: "flex-start", marginBottom: 14,
          padding: "10px 12px", borderRadius: 10,
          background: "rgba(229,72,77,.10)", border: "1px solid rgba(229,72,77,.30)", fontSize: 13.5,
        }}>
          <span style={{ color: "#FF8488", flexShrink: 0, marginTop: 1 }}><Icons.alert size={16} /></span>
          <span>Ссылка временно заблокирована после проверки. Напишите в поддержку: <a href="https://t.me/FortuneVPNsupportbot" target="_blank" rel="noopener" style={{ color: "var(--accent-soft)", textDecoration: "none" }}>@FortuneVPNsupportbot</a> или <a href="mailto:support@fortunetavern.com" style={{ color: "var(--accent-soft)", textDecoration: "none" }}>support@fortunetavern.com</a></span>
        </div>
      )}

      <div style={{
        display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 16,
        padding: "12px 14px", borderRadius: 10,
        background: "var(--panel)", border: "1px solid var(--border)", marginBottom: isRevoked ? 0 : 14,
      }}>
        <Field k="ID" v={<span className="mono">{token_prefix}…</span>} />
        <Field k="До" v={expires} />
        <Field k="Использован" v={lastUsed} />
      </div>

      {!isRevoked && !externalDisabled && (can_regenerate || can_revoke) && (
        <div style={{ display: "flex", flexWrap: "wrap", gap: 10 }}>
          {can_regenerate && (
            <button className="btn btn-ghost" onClick={() => window.actions.askRegenerate(id)}>
              <Icons.refresh size={14} /> Перегенерировать ссылку
            </button>
          )}
          {can_revoke && (
            <button className="btn btn-ghost" style={{ borderColor: "rgba(229,72,77,.3)", color: "#FF8488" }} onClick={() => window.actions.askRevoke(id)}>
              Отозвать ссылку
            </button>
          )}
        </div>
      )}
    </div>
  );
}

const Field = ({ k, v }) => (
  <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
    <span style={{ fontSize: 11.5, color: "var(--dim)", textTransform: "uppercase", letterSpacing: "0.08em" }}>{k}</span>
    <span style={{ fontSize: 13.5, color: "var(--ink)" }}>{v}</span>
  </div>
);

function AddSlotCTA() {
  const onClick = () => window.actions.openCreateSlot();
  return (
    <div onClick={onClick} style={{
      border: "1.5px dashed var(--border)", borderRadius: 20,
      padding: 24, display: "flex", alignItems: "center", gap: 16, cursor: "pointer",
    }}>
      <div style={{
        width: 44, height: 44, borderRadius: 10,
        border: "1.5px dashed var(--border)", display: "grid", placeItems: "center",
        color: "var(--muted)", fontSize: 22, fontWeight: 300,
      }}>+</div>
      <div style={{ flex: 1 }}>
        <div style={{ fontWeight: 600, fontSize: 16 }}>Добавить ещё одну ссылку</div>
        <div style={{ fontSize: 13.5, color: "var(--muted)" }}>Покажем ссылку один раз — сразу сохраните её на устройстве.</div>
      </div>
      <button className="btn btn-primary" onClick={(e) => { e.stopPropagation(); onClick(); }}>Создать ссылку</button>
    </div>
  );
}

// Маленькая placeholder-карточка «Android · скоро» / «iOS · скоро».
// Визуально отражает будущий блок mobile-устройств в seat-математике.
function MobileSoonCard({ platform }) {
  return (
    <div style={{
      padding: 16, borderRadius: 14,
      background: "rgba(255,255,255,.02)",
      border: "1px dashed var(--border)",
      display: "flex", alignItems: "center", gap: 12, opacity: 0.78,
    }}>
      <div style={{
        width: 36, height: 36, borderRadius: 10,
        background: "rgba(255,255,255,.04)", border: "1px solid var(--border)",
        display: "grid", placeItems: "center", color: "var(--muted)",
      }}>
        {platform === "iOS" ? <Icons.apple /> : <Icons.android />}
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14.5, fontWeight: 600, color: "var(--ink)" }}>{platform}</div>
        <div style={{ fontSize: 12.5, color: "var(--dim)" }}>Приложение в разработке</div>
      </div>
      <span style={{
        fontSize: 10.5, padding: "3px 8px", borderRadius: 999, letterSpacing: "0.03em",
        background: "rgba(255,255,255,.05)", border: "1px solid var(--border)", color: "var(--muted)",
      }}>скоро</span>
    </div>
  );
}

function ClientHowTo({ name, steps }) {
  return (
    <div style={{ background: "var(--panel)", border: "1px solid var(--border)", borderRadius: 12, padding: 16 }}>
      <div style={{ fontWeight: 600, marginBottom: 12 }}>{name}</div>
      <ol style={{ margin: 0, padding: "0 0 0 18px", display: "flex", flexDirection: "column", gap: 6, fontSize: 13.5, color: "var(--muted)" }}>
        {steps.map((s, i) => <li key={i}>{s}</li>)}
      </ol>
    </div>
  );
}

function TestPlanCard({ selected = false, onClick = null, disabled = false }) {
  return (
    <div
      role={onClick && !disabled ? "button" : undefined}
      tabIndex={onClick && !disabled ? 0 : undefined}
      onClick={!disabled ? (onClick || undefined) : undefined}
      onKeyDown={onClick && !disabled ? (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onClick(e); } } : undefined}
      style={{
        position: "relative",
        minHeight: 265,
        background: "linear-gradient(180deg, rgba(255,255,255,0.035), rgba(255,255,255,0.01))",
        border: selected ? "1.5px solid rgba(46,168,255,.65)" : "1.5px solid var(--border)",
        borderRadius: 18,
        padding: "22px 22px 24px",
        cursor: onClick && !disabled ? "pointer" : "default",
        opacity: disabled ? 0.58 : 1,
        boxShadow: selected ? "0 0 0 1px rgba(46,168,255,.18), 0 18px 45px rgba(46,168,255,.10)" : "none",
      }}
    >
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }}>
        <div style={{
          width: 34, height: 34, borderRadius: 10,
          background: "rgba(162,172,184,.10)", border: "1px solid var(--border)",
          color: "var(--muted)", display: "grid", placeItems: "center",
        }}>
          <Icons.shield size={17} />
        </div>
        <div style={{ fontSize: 12.5, fontWeight: 600, letterSpacing: "0.12em", color: "#C9C9DC", textTransform: "uppercase" }}>
          Пробный доступ
        </div>
        <span style={{ marginLeft: "auto", fontSize: 11, fontWeight: 700, letterSpacing: "0.08em", color: "var(--muted)", padding: "2px 8px", borderRadius: 99, background: "var(--panel)", border: "1px solid var(--border)" }}>
          7 дней
        </span>
      </div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginBottom: 10 }}>
        <span style={{ fontSize: 36, fontWeight: 800, letterSpacing: "-0.03em", color: "#FFF" }}>{TEST_PLAN.price}</span>
        <span style={{ fontSize: 13, color: "var(--muted)" }}>{TEST_PLAN.per}</span>
      </div>
      <div style={{ fontSize: 13.5, color: "var(--muted)", marginBottom: 16 }}>{TEST_PLAN.summary}</div>
      {disabled && (
        <div style={{
          marginBottom: 14,
          padding: "9px 10px",
          borderRadius: 10,
          background: "rgba(245,166,35,.08)",
          border: "1px solid rgba(245,166,35,.24)",
          color: "var(--warning)",
          fontSize: 12.5,
          fontWeight: 700,
        }}>
          Пробный доступ скоро будет доступен
        </div>
      )}
      <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 8 }}>
        {TEST_PLAN.features.map((f, j) => (
          <li key={j} style={{ display: "flex", alignItems: "flex-start", gap: 8, fontSize: 14, color: "var(--ink)" }}>
            <span style={{ color: "var(--green)", flexShrink: 0, marginTop: 3 }}><Icons.check size={14} /></span>
            {f}
          </li>
        ))}
      </ul>
    </div>
  );
}

function PlanGrid({ selectedCode = PREMIUM_PLAN.code, onSelect = null, action = null }) {
  const s = useAppState();
  const availability = s.checkoutPlanAvailability || {};
  const testDisabled = availability[TEST_PLAN.code] === false;
  React.useEffect(() => {
    if (window.actions?.loadCommerceConfig) window.actions.loadCommerceConfig();
  }, []);
  return (
    <div data-grid="plan-options" style={{ display: "grid", gridTemplateColumns: "repeat(2, minmax(0, 1fr))", gap: 14, alignItems: "stretch" }}>
      <div>
        <SinglePlanCard
          plan={PREMIUM_PLAN}
          selected={selectedCode === PREMIUM_PLAN.code}
          onClick={onSelect ? () => onSelect(PREMIUM_PLAN.code) : null}
        />
        {action === "buy" && (
          <button className="btn btn-primary btn-lg btn-block" style={{ marginTop: 12 }} onClick={() => window.actions.buyPremium(PREMIUM_PLAN.code)}>
            Оформить за {PREMIUM_PLAN.price}
          </button>
        )}
      </div>
      <div>
        <TestPlanCard
          selected={!testDisabled && selectedCode === TEST_PLAN.code}
          disabled={testDisabled}
          onClick={onSelect && !testDisabled ? () => onSelect(TEST_PLAN.code) : null}
        />
        {action === "buy" && (
          <button className="btn btn-ghost btn-lg btn-block" disabled={testDisabled} style={{ marginTop: 12, opacity: testDisabled ? 0.55 : 1, cursor: testDisabled ? "not-allowed" : "pointer" }} onClick={() => !testDisabled && window.actions.buyPremium(TEST_PLAN.code)}>
            {testDisabled ? "Пробный доступ скоро будет доступен" : <>Попробовать за {TEST_PLAN.price}</>}
          </button>
        )}
      </div>
    </div>
  );
}

function selectedPlanByCode(code) {
  const normalized = String(code || "").trim();
  return normalized === TEST_PLAN.code || normalized === "account_premium_test_7d" ? TEST_PLAN : PREMIUM_PLAN;
}

function selectedPlanCodeFromLocation() {
  try {
    const params = new URLSearchParams(window.location.search || "");
    return selectedPlanByCode(params.get("plan") || params.get("plan_code")).code;
  } catch (_) {
    return PREMIUM_PLAN.code;
  }
}

function SelectedPlanCopy({ plan }) {
  if (plan.code === TEST_PLAN.code) {
    return (
      <>
        <div style={{ fontSize: 18, fontWeight: 700 }}>Итого: {plan.price}</div>
        <div style={{ fontSize: 13.5, color: "var(--muted)" }}>Доступ на 7 дней · 1 слот · без автопродления</div>
      </>
    );
  }
  return (
    <>
      <div style={{ fontSize: 18, fontWeight: 700 }}>Итого: {plan.price}</div>
      <div style={{ fontSize: 13.5, color: "var(--muted)" }}>Доступ на 1 месяц · без автопродления</div>
    </>
  );
}

function PlanSelectionHint({ plan }) {
  if (plan.code === TEST_PLAN.code) {
    return (
      <div style={{ marginTop: 12, fontSize: 12.5, color: "var(--dim)" }}>
        Пробный доступ на 7 дней: 1 слот, без автопродления и без реферальных бонусов.
      </div>
    );
  }
  return (
    <div style={{ marginTop: 12, fontSize: 12.5, color: "var(--dim)" }}>
      Оплата картой Visa / Mastercard / МИР или через СБП. После оплаты Premium обновится в личном кабинете автоматически.
    </div>
  );
}

function PlanLayoutStyles() {
  return (
    <style>{`
      @media (max-width: 760px) {
        [data-grid="plan-options"] {
          grid-template-columns: 1fr !important;
        }
      }
    `}</style>
  );
}

function Keys_Empty() {
  return (
    <Shell active="keys" screenLabel="07 Subscription · empty">
      <SectionHeader eyebrow="ПОДПИСКА" title="Ваша подписка" />

      <PsCard style={{ textAlign: "center", padding: "44px 28px" }}>
        <div style={{
          width: 72, height: 72, borderRadius: 18, margin: "0 auto 18px",
          background: "rgba(46,168,255,.10)", border: "1px solid rgba(46,168,255,.22)",
          color: "#7DCBFF", display: "grid", placeItems: "center",
        }}>
          <Icons.shield />
        </div>
        <h3 style={{ margin: "0 0 8px", fontSize: 22, fontWeight: 700, letterSpacing: "-0.02em" }}>Подписка не оформлена</h3>
        <p style={{ margin: "0 auto 20px", fontSize: 15, color: "var(--muted)", maxWidth: 460 }}>
          Одна подписка — до 3 подключений: V2Ray, Incy и (скоро) приложения для Android и iOS.
        </p>
        <div style={{ display: "inline-flex", gap: 10 }}>
          <button className="btn btn-primary btn-lg" onClick={() => window.navigate("keys-checkout")}>Выбрать тариф</button>
          <button className="btn btn-ghost btn-lg" onClick={() => window.navigate("redeem")}>У меня ваучер</button>
        </div>
      </PsCard>

      <div style={{ marginTop: 28, marginBottom: 16 }}>
        <h3 style={{ margin: "0 0 4px", fontSize: 22, fontWeight: 700, letterSpacing: "-0.02em" }}>Тариф</h3>
        <p style={{ margin: 0, fontSize: 14, color: "var(--muted)" }}>Выберите 1 месяц или тест на 7 дней. Оба тарифа без автопродления.</p>
      </div>
      <div style={{ maxWidth: 980 }}>
        <PlanLayoutStyles />
        <PlanGrid action="buy" />
      </div>
    </Shell>
  );
}

function Keys_Checkout() {
  const s = useAppState(); // подписка на ререндер для countdown
  const [selectedPlanCode, setSelectedPlanCode] = React.useState(() => selectedPlanByCode(window.appState.checkoutPlanCode || selectedPlanCodeFromLocation()).code);
  const gate = window.canBuySubscription();
  const checkoutBusy = s.api?.pendingAction === "checkout";
  const checkoutSyncBusy = s.api?.pendingAction === "checkout_sync";
  const checkoutReturn = s.checkoutReturn || {};
  const isRenewal = window.appState.premium?.status === "active";
  const selectedPlan = selectedPlanByCode(selectedPlanCode);
  const invite = s.referralInvite || {};
  const showInvite = !!(invite.loaded && invite.valid && invite.code);
  React.useEffect(() => {
    if (invite.code && !invite.loaded && !invite.loading && window.actions?.loadReferralInvite) {
      window.actions.loadReferralInvite();
    }
  }, [invite.code, invite.loaded, invite.loading]);
  const selectPlan = (code) => {
    const normalized = selectedPlanByCode(code).code;
    setSelectedPlanCode(normalized);
    if (window.actions?.setCheckoutPlan) window.actions.setCheckoutPlan(normalized);
  };
  const title = isRenewal ? "Продление подписки" : "Оформление подписки";
  const eyebrow = isRenewal ? "ПРОДЛЕНИЕ" : "ОФОРМЛЕНИЕ";
  return (
    <Shell active="keys" screenLabel="09 Keys · checkout">
      <SectionHeader eyebrow={eyebrow} title={title} />

      {/* Бизнес-модель: 1 месяц как основной тариф + короткий тест на 7 дней. Никаких 3/6/12 — рынок меняется быстро. */}
      <div style={{ maxWidth: 980, marginInline: "auto" }}>
        <PlanLayoutStyles />
        <PlanGrid selectedCode={selectedPlanCode} onSelect={selectPlan} />

        {showInvite && (
          <PsCard style={{ marginTop: 16, borderColor: "rgba(46,168,255,.35)", background: "rgba(46,168,255,.06)" }}>
            <div style={{ display: "flex", gap: 12, alignItems: "flex-start" }}>
              <span style={{ color: "var(--accent-soft)", flexShrink: 0, marginTop: 2 }}>
                <Icons.users size={16} />
              </span>
              <div>
                <div style={{ fontSize: 15, fontWeight: 800, color: "var(--ink)", marginBottom: 4 }}>
                  {invite.inviter?.email_masked ? `Вас пригласил ${invite.inviter.email_masked}` : "Вы пришли по приглашению Fortune VPN"}
                </div>
                <div style={{ fontSize: 13.5, color: "var(--muted)", lineHeight: 1.55 }}>
                  После подтверждения первой реальной оплаты вы оба получите +{Number(invite.reward_days || 10)} дней.
                </div>
              </div>
            </div>
          </PsCard>
        )}

        {checkoutReturn.sessionId && checkoutReturn.status !== "idle" && (
          <PsCard style={{ marginTop: 16, borderColor: checkoutReturn.status === "error" ? "rgba(229,72,77,.35)" : "rgba(46,168,255,.35)" }}>
            <div style={{ display: "flex", gap: 12, alignItems: "flex-start" }}>
              <span style={{ color: checkoutReturn.status === "error" ? "#FF8488" : "var(--accent-soft)", flexShrink: 0, marginTop: 2 }}>
                <Icons.info size={16} />
              </span>
              <div>
                <div style={{ fontSize: 15, fontWeight: 700, color: "var(--ink)", marginBottom: 4 }}>
                  {checkoutSyncBusy || checkoutReturn.status === "syncing" ? "Проверяем оплату" : "Статус оплаты"}
                </div>
                <div style={{ fontSize: 13.5, color: "var(--muted)", lineHeight: 1.55 }}>
                  {checkoutReturn.message || "Если оплата уже прошла, подписка появится здесь через 1–2 минуты. Обновите страницу или напишите в поддержку, если статус не изменился."}
                </div>
                {checkoutReturn.status === "error" && (
                  <button className="btn btn-ghost" style={{ marginTop: 10, padding: "7px 12px" }} onClick={() => window.actions.syncCheckoutReturn()}>
                    Проверить снова
                  </button>
                )}
              </div>
            </div>
          </PsCard>
        )}

        <PsCard style={{ marginTop: 16 }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 12, marginBottom: 8 }}>
            <div>
              <SelectedPlanCopy plan={selectedPlan} />
            </div>
            {gate.canBuy ? (
              <button className="btn btn-primary btn-lg" disabled={checkoutBusy} style={checkoutBusy ? { opacity: 0.65, cursor: "wait" } : null} onClick={() => window.actions.buyPremium(selectedPlan.code)}>
                {checkoutBusy ? "Открываем оплату..." : (isRenewal ? `Продлить за ${selectedPlan.price}` : `Оплатить ${selectedPlan.price}`)}
              </button>
            ) : (
              <button className="btn btn-primary btn-lg" disabled style={{ opacity: 0.45, cursor: "not-allowed" }}>
                Оплатить {selectedPlan.price}
              </button>
            )}
          </div>
          {!gate.canBuy && <RenewalLockedNote msLeft={gate.msLeft} />}
          <PlanSelectionHint plan={selectedPlan} />
        </PsCard>
      </div>
    </Shell>
  );
}

// Карточка одного тарифа — без grid, без выбора, без бейджа «Популярно».
function SinglePlanCard({ plan, selected = false, onClick = null }) {
  return (
    <div
      role={onClick ? "button" : undefined}
      tabIndex={onClick ? 0 : undefined}
      onClick={onClick || undefined}
      onKeyDown={onClick ? (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onClick(e); } } : undefined}
      style={{
      position: "relative",
      minHeight: 265,
      background: "linear-gradient(180deg, rgba(46,168,255,.08), rgba(255,255,255,0.01))",
      border: selected ? "1.5px solid rgba(46,168,255,.75)" : "1.5px solid rgba(46,168,255,.35)",
      borderRadius: 18, padding: "22px 22px 24px",
      cursor: onClick ? "pointer" : "default",
      boxShadow: selected ? "0 0 0 1px rgba(46,168,255,.20), 0 18px 45px rgba(46,168,255,.12)" : "none",
    }}>
      <div style={{ fontSize: 12.5, fontWeight: 600, letterSpacing: "0.12em", color: "#C9C9DC", textTransform: "uppercase", marginBottom: 8 }}>
        Подписка · {plan.period}
      </div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginBottom: 16 }}>
        <span style={{ fontSize: 36, fontWeight: 800, letterSpacing: "-0.03em", color: "#FFF" }}>{plan.price}</span>
        <span style={{ fontSize: 13, color: "var(--muted)" }}>{plan.per}</span>
      </div>
      <ul style={{ margin: 0, padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 8 }}>
        {plan.features.map((f, j) => (
          <li key={j} style={{ display: "flex", alignItems: "flex-start", gap: 8, fontSize: 14, color: "var(--ink)" }}>
            <span style={{ color: "var(--green)", flexShrink: 0, marginTop: 3 }}><Icons.check size={14} /></span>
            {f}
          </li>
        ))}
      </ul>
    </div>
  );
}

// Объяснение, почему продление сейчас недоступно. Используется на /keys-checkout, /keys и /home.
function RenewalLockedNote({ msLeft, compact }) {
  const renewableIn = Math.max(0, msLeft - 24 * 60 * 60 * 1000);
  const left = window.fmtTimeLeft ? window.fmtTimeLeft(renewableIn) : "позже";
  return (
    <div style={{
      display: "flex", gap: 10, alignItems: "flex-start",
      padding: compact ? "8px 10px" : "10px 12px", borderRadius: 10, marginTop: 10,
      background: "rgba(46,168,255,.06)", border: "1px solid rgba(46,168,255,.22)",
      fontSize: compact ? 12.5 : 13, color: "var(--ink)", lineHeight: 1.5,
    }}>
      <span style={{ color: "var(--accent-soft)", flexShrink: 0, marginTop: 1 }}><Icons.info size={14} /></span>
      <span>
        Продление откроется через <b style={{ color: "var(--accent-soft)" }}>{left}</b> — за 24 часа до окончания подписки. Так вы платите ровно за нужный месяц, без переплаты за неиспользованные дни.
      </span>
    </div>
  );
}

function Keys_Migration() {
  return (
    <Shell active="keys" screenLabel="Перенос старой покупки">
      <SectionHeader eyebrow="ПОДПИСКА" title="Ваша подписка" badge={<Badge kind="active">Действует до 12.06.2026</Badge>} />

      <div style={{
        display: "flex", gap: 14, alignItems: "flex-start",
        background: "rgba(46,168,255,.08)", border: "1px solid rgba(46,168,255,.22)",
        borderLeft: "4px solid var(--accent)", borderRadius: 14,
        padding: "18px 20px", marginBottom: 20,
      }}>
        <span style={{ color: "var(--accent-soft)", flexShrink: 0, marginTop: 2 }}><Icons.info /></span>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 6 }}>У вас есть VLESS-ключ от 12.03.2026, оплачен до 12.06.2026</div>
          <div style={{ fontSize: 14, color: "var(--ink)", marginBottom: 12 }}>
            Мы автоматически создали для вас новую подписку с тем же сроком — рекомендуем перейти на неё, чтобы серверы обновлялись автоматически.
          </div>
          <div style={{ display: "flex", gap: 10 }}>
            <button className="btn btn-primary btn-sm">Перейти на новую</button>
            <button className="btn btn-ghost btn-sm">Подробнее</button>
          </div>
        </div>
      </div>

      <KeySlot idx={1} label="Новая подписка" tokenPrefix="m9c4btr88p" expires="12.06.2026" lastUsed="Ещё не использовался" status="active" canRegenerate canRevoke />

      <PsCard style={{ marginTop: 14, opacity: 0.7 }}>
        <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 10 }}>
          <div>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}>
              <span style={{ fontSize: 16, fontWeight: 600 }}>Старый VLESS-ключ</span>
              <Badge kind="pending">Работает до 12.06.2026</Badge>
            </div>
            <div style={{ fontSize: 12.5, color: "var(--muted)" }}>Создан 12.03.2026 · read-only</div>
          </div>
          <Icons.shield />
        </div>
        <div style={{ fontSize: 13, color: "var(--dim)", marginTop: 6 }}>
          Перегенерация и отзыв недоступны. Продолжит работать до своей даты — серверы не обновляются автоматически.
        </div>
      </PsCard>
    </Shell>
  );
}

Object.assign(window, { KeysPage, Keys_Active, Keys_Empty, Keys_Checkout, Keys_Migration, KeySlot, KEY_PLANS, PREMIUM_PLAN, PlanGrid, TestPlanCard, MobileSoonCard, SinglePlanCard, RenewalLockedNote });
