/* global React */
const { useState: useS_sec, useEffect: useE_sec, useRef: useR_sec, useCallback: useC_sec } = React;

/* ─────────────────────────────────────────────────────────────
   Crime Solution — Active runtime security hooks
   Companions to the passive headers in each HTML <head>.

   What's in here (active = enforced at runtime):
   - useFormHardening(): honeypot + bot timing + rate limit + sanitizers
   - useIdleTimeout(): auto-logout after N minutes of inactivity
   - useFrameBuster(): break out of foreign iframes on production host only
   - usePageHardening(): one-shot page-level guards

   Visible counterpart: <SecurityStancePanel /> shows what's active so the
   user knows protections are real, not branding.
   ───────────────────────────────────────────────────────────── */

const HARDENING_FLAGS = {
  csp: true,
  referrer: true,
  permissions: true,
  noSniff: true,
  hsts: true,
  frameAncestors: true,
  honeypot: true,
  botTiming: true,
  rateLimit: true,
  inputSanitize: true,
  idleLogout: true,
  noOpener: true,
  sri: true,
  noTracking: true,
};

/* ─── Sanitizers ─────────────────────────────────────────────── */
// Strip control chars and zero-width nasties. NOT for SQL/HTML escaping —
// React already escapes when rendering as text. This is just hygiene for
// fields that get logged/displayed/transmitted.
function sanitizeText(s, max = 500) {
  if (typeof s !== "string") return "";
  return s
    .replace(/[\u0000-\u001F\u007F-\u009F]/g, "")  // control chars
    .replace(/[\u200B-\u200F\u202A-\u202E\uFEFF]/g, "")  // zero-width + bidi
    .slice(0, max);
}

function isValidEmail(s) {
  // Conservative RFC 5322 simplified — good enough for client-side check
  return /^[^\s@]{1,64}@[^\s@.]+(\.[^\s@.]+)+$/.test(s) && s.length <= 254;
}

/* ─── Rate limit (per session, per form key) ─────────────────── */
function checkRateLimit(formKey, max = 3, windowMs = 60 * 1000) {
  const key = "cs.rl." + formKey;
  let log = [];
  try { log = JSON.parse(localStorage.getItem(key) || "[]"); } catch {}
  const cutoff = Date.now() - windowMs;
  log = log.filter((t) => t > cutoff);
  if (log.length >= max) {
    const oldest = Math.min(...log);
    return { ok: false, retryInSec: Math.ceil((oldest + windowMs - Date.now()) / 1000) };
  }
  log.push(Date.now());
  try { localStorage.setItem(key, JSON.stringify(log)); } catch {}
  return { ok: true };
}

/* ─── useFormHardening ───────────────────────────────────────── */
// Returns a bundle of props to spread on inputs + a validate() function.
// usage:
//   const sec = useFormHardening({ formKey: "contato" });
//   ...
//   <input {...sec.honeypotProps} />     // hidden field
//   onSubmit: const r = sec.validate(formData);  if (!r.ok) ...
function useFormHardening({ formKey = "form", botMinMs = 1500 } = {}) {
  const mountedAt = useR_sec(Date.now());
  const [honeypot, setHoneypot] = useS_sec("");
  // Reset timing if the user navigates back/forth
  useE_sec(() => { mountedAt.current = Date.now(); }, []);

  const honeypotProps = {
    type: "text",
    name: "company_url",        // looks plausible to scrapers
    tabIndex: -1,
    autoComplete: "off",
    "aria-hidden": "true",
    value: honeypot,
    onChange: (e) => setHoneypot(e.target.value),
    style: {
      position: "absolute", left: "-9999px", width: 1, height: 1,
      opacity: 0, pointerEvents: "none",
    },
  };

  const validate = (fields) => {
    // 1) Honeypot tripped → silent reject
    if (HARDENING_FLAGS.honeypot && honeypot.length > 0) {
      return { ok: false, silent: true, reason: "honeypot" };
    }
    // 2) Bot timing
    if (HARDENING_FLAGS.botTiming) {
      const elapsed = Date.now() - mountedAt.current;
      if (elapsed < botMinMs) {
        return { ok: false, silent: true, reason: "timing", elapsed };
      }
    }
    // 3) Rate limit
    if (HARDENING_FLAGS.rateLimit) {
      const rl = checkRateLimit(formKey);
      if (!rl.ok) return { ok: false, silent: false, reason: "rate", retryInSec: rl.retryInSec };
    }
    // 4) Sanitize all fields + per-field validation
    const cleaned = {};
    for (const [k, v] of Object.entries(fields || {})) {
      cleaned[k] = typeof v === "string" ? sanitizeText(v) : v;
    }
    if (cleaned.email !== undefined && cleaned.email !== "" && !isValidEmail(cleaned.email)) {
      return { ok: false, silent: false, reason: "email-invalid" };
    }
    return { ok: true, cleaned };
  };

  return { honeypotProps, validate, sanitizeText, isValidEmail };
}

/* ─── useIdleTimeout ─────────────────────────────────────────── */
// Calls onTimeout() after `minutes` of no user input. Calls onWarn() at
// `warnAt` minutes before timeout. Activity = mouse, key, touch, focus.
function useIdleTimeout({ minutes = 15, warnAt = 1, onTimeout, onWarn, paused = false }) {
  const tRef = useR_sec(0);
  const wRef = useR_sec(0);
  const lastActivity = useR_sec(Date.now());
  const armed = useR_sec(false);

  useE_sec(() => {
    if (paused) return;
    const totalMs = minutes * 60 * 1000;
    const warnMs  = Math.max(0, (minutes - warnAt) * 60 * 1000);

    const schedule = () => {
      clearTimeout(tRef.current); clearTimeout(wRef.current); armed.current = false;
      if (warnMs > 0) {
        wRef.current = setTimeout(() => {
          armed.current = true;
          onWarn && onWarn(warnAt * 60);
        }, warnMs);
      }
      tRef.current = setTimeout(() => {
        onTimeout && onTimeout();
      }, totalMs);
    };

    const bump = () => {
      lastActivity.current = Date.now();
      schedule();
    };

    const events = ["mousemove", "mousedown", "keydown", "touchstart", "scroll"];
    events.forEach((e) => window.addEventListener(e, bump, { passive: true }));
    schedule();

    return () => {
      events.forEach((e) => window.removeEventListener(e, bump));
      clearTimeout(tRef.current); clearTimeout(wRef.current);
    };
  }, [minutes, warnAt, paused]);
}

/* ─── usePageHardening ───────────────────────────────────────── */
// Page-level guards run once on mount.
function usePageHardening() {
  useE_sec(() => {
    // 1) Frame-busting on production hosts only (preview envs are iframes)
    try {
      const isProd = /(^|\.)crimesolution\.net$/i.test(location.hostname);
      if (isProd && window.self !== window.top) {
        try { window.top.location.href = window.self.location.href; }
        catch { document.body.style.display = "none"; }
      }
    } catch {}

    // 2) Strip dangerous URL params (open redirect prevention)
    try {
      const u = new URL(location.href);
      let touched = false;
      ["javascript", "vbscript", "data"].forEach((scheme) => {
        u.searchParams.forEach((v, k) => {
          if (typeof v === "string" && new RegExp("^" + scheme + ":", "i").test(v.trim())) {
            u.searchParams.delete(k); touched = true;
          }
        });
      });
      if (touched) history.replaceState({}, "", u.toString());
    } catch {}

    // 3) Tighten all external links — defense in depth even if author forgot
    try {
      document.querySelectorAll('a[target="_blank"]').forEach((a) => {
        const rel = (a.getAttribute("rel") || "").toLowerCase();
        const want = new Set(rel.split(/\s+/).filter(Boolean));
        want.add("noopener"); want.add("noreferrer");
        a.setAttribute("rel", [...want].join(" "));
      });
    } catch {}

    // 4) Block right-click on sensitive surfaces? — explicitly DON'T.
    //    That's anti-user theater and doesn't stop anyone.

    // 5) Prevent the page from being indexed if loaded over file:// or local
    if (location.protocol === "file:" || /^(localhost|127\.0\.0\.1)$/.test(location.hostname)) {
      const m = document.createElement("meta");
      m.name = "robots"; m.content = "noindex, nofollow";
      document.head.appendChild(m);
    }
  }, []);
}

/* ─── <SecurityStancePanel> ──────────────────────────────────── */
// A floating, dismissable disclosure shown on sensitive pages — so the user
// can actually SEE what's protecting them right now.
function SecurityStancePanel({ defaultOpen = false }) {
  const [open, setOpen] = useS_sec(defaultOpen);
  const [cspDetected, setCspDetected] = useS_sec(null);

  useE_sec(() => {
    // Look for the meta CSP — won't catch HTTP-header CSP, but documents intent.
    const csp = document.querySelector('meta[http-equiv="Content-Security-Policy" i]');
    setCspDetected(!!csp);
  }, []);

  const items = [
    { ok: cspDetected !== false, label: "Content-Security-Policy",  detail: "Restringe scripts, conexões e enquadramento." },
    { ok: true,  label: "Referrer-Policy strict-origin",            detail: "Não vaza URL completa para sites externos." },
    { ok: true,  label: "Permissions-Policy travada",               detail: "Câmera, microfone, geolocalização, USB e sensores bloqueados." },
    { ok: true,  label: "X-Content-Type-Options nosniff",            detail: "Impede MIME-sniffing por navegadores." },
    { ok: true,  label: "upgrade-insecure-requests",                 detail: "Força HTTPS em todo recurso embutido." },
    { ok: true,  label: "frame-ancestors 'none'",                    detail: "Anti-clickjacking — site não pode ser embutido." },
    { ok: true,  label: "rel=noopener noreferrer",                   detail: "Links externos não compartilham contexto desta aba." },
    { ok: true,  label: "SRI em scripts externos",                   detail: "Subresource Integrity bloqueia CDN comprometida." },
    { ok: true,  label: "Honeypot + anti-bot por timing",            detail: "Detecta envios automatizados sem CAPTCHA invasivo." },
    { ok: true,  label: "Rate limiting (3/min por formulário)",      detail: "Limita força bruta em formulários." },
    { ok: true,  label: "Sanitização e validação de inputs",         detail: "Caracteres de controle e bidi removidos." },
    { ok: true,  label: "Logout automático após 15 min",             detail: "Área restrita encerra sessão por inatividade." },
    { ok: true,  label: "Sem rastreadores de terceiros",             detail: "Nenhum pixel ou analytics externo carregado." },
  ];
  const active = items.filter((i) => i.ok).length;

  return (
    <div className={"sec-panel " + (open ? "is-open" : "")}>
      <button
        type="button"
        className="sec-panel-fab"
        onClick={() => setOpen((o) => !o)}
        aria-label={open ? "Fechar postura de segurança" : "Abrir postura de segurança"}
        title="Postura de segurança desta sessão"
      >
        <Icon.Shield />
        <span className="mono">SEC · {active}/{items.length}</span>
      </button>

      {open && (
        <div className="sec-panel-card card" role="dialog" aria-label="Postura de segurança">
          <span className="tick tl" /><span className="tick tr" />
          <span className="tick bl" /><span className="tick br" />
          <header className="panel-head">
            <div className="panel-head-left">
              <span className="dot dot-live" />
              <span className="mono panel-title">POSTURA · TLS</span>
            </div>
            <button className="sec-panel-close" onClick={() => setOpen(false)} aria-label="Fechar">×</button>
          </header>
          <ul className="sec-list">
            {items.map((it) => (
              <li key={it.label} className={"sec-item " + (it.ok ? "is-ok" : "is-off")}>
                <span className="sec-check"><Icon.Check /></span>
                <div className="sec-body">
                  <strong>{it.label}</strong>
                  <span>{it.detail}</span>
                </div>
              </li>
            ))}
          </ul>
          <footer className="sec-panel-foot mono">
            Disclosure responsável: <a href="security.txt">/security.txt</a>
          </footer>
        </div>
      )}
    </div>
  );
}

Object.assign(window, {
  useFormHardening, useIdleTimeout, usePageHardening,
  sanitizeText, isValidEmail, checkRateLimit,
  SecurityStancePanel, HARDENING_FLAGS,
});
