// Aria console — creazione organizzazione + form connettore condiviso.
//
// Il wizard: nome → slug proposto/modificabile con verifica di disponibilità in
// tempo reale (grammatica DNS + Aria + provisioner, fatta dal backend), toggle
// OFFLINE (forzato e non modificabile quando il provisioning non è configurato,
// da GET /provisioning), e — solo online — lo step del connettore di
// autenticazione: catalogo da GET /connectors, form generato dal JSON Schema.
// La config del connettore va dritta al provisioner e non viene mai persistita.
const { Card, Badge, Banner, Button, Icon, Select, Switch, TextField, Dialog } = window.AriaDesignSystem_af77ab;

// ---- Form schema-driven del connettore -------------------------------------
// Genera il form dal JSON Schema (draft-07) del connettore, come lo pubblica
// l'auth-module via provisioner: campo `configSchema`, properties anche di
// tipo object (gruppi annidati, es. retry / eime-data-manager), tipi
// string/integer/number/boolean, `description` come hint, `required` per
// livello. La validazione vera resta server-side (fail-fast dell'auth-module).

// Lettura/scrittura immutabile su path annidati dentro authConfig.
function cfgGet(obj, path) {
  return path.reduce((o, k) => (o && typeof o === "object" ? o[k] : undefined), obj);
}
function cfgSet(obj, path, value) {
  const key = path[0];
  if (path.length === 1) {
    const out = { ...(obj || {}) };
    if (value === undefined) delete out[key];
    else out[key] = value;
    return out;
  }
  const child = cfgSet((obj || {})[key], path.slice(1), value);
  const out = { ...(obj || {}) };
  // Un gruppo opzionale svuotato sparisce dal payload (additionalProperties:false
  // e required annidati: meglio omettere che inviare un oggetto vuoto/parziale).
  if (Object.keys(child).length === 0) delete out[key];
  else out[key] = child;
  return out;
}

function connectorSchemaOf(connector) {
  // `configSchema` è il contratto reale; `schema` tollerato per compatibilità.
  const s = connector && (connector.configSchema || connector.schema);
  return s && typeof s === "object" ? s : {};
}

// Un campo del form: foglia (testo/numero/booleano) o gruppo object ricorsivo.
function SchemaField({ name, prop, required, path, config, onConfig }) {
  const value = cfgGet(config, path);
  if (prop && prop.type === "object") {
    const props = prop.properties && typeof prop.properties === "object" ? prop.properties : {};
    const req = Array.isArray(prop.required) ? prop.required : [];
    return (
      <div style={{ border: "1px solid var(--border-subtle)", borderRadius: "var(--radius-sm)", padding: "12px 14px", display: "flex", flexDirection: "column", gap: 12 }}>
        <span style={{ fontSize: 12.5, fontWeight: 600, color: "var(--text-strong)" }}>
          {name} {!required && <span style={{ color: "var(--text-subtle)", fontWeight: 400 }}>(opzionale)</span>}
        </span>
        {Object.keys(props).map((k) => (
          <SchemaField key={k} name={k} prop={props[k]} required={req.indexOf(k) !== -1}
            path={path.concat([k])} config={config} onConfig={onConfig} />
        ))}
      </div>
    );
  }
  if (prop && prop.type === "boolean") {
    return <Switch checked={!!value} label={name} onChange={(e) => onConfig(path, e.target.checked)} />;
  }
  const numeric = prop && (prop.type === "integer" || prop.type === "number");
  return (
    <TextField label={name} required={required} hint={prop && prop.description}
      value={value == null ? "" : String(value)}
      onChange={(e) => {
        const raw = e.target.value;
        if (raw === "") { onConfig(path, undefined); return; }
        if (numeric) {
          const n = prop.type === "integer" ? parseInt(raw, 10) : parseFloat(raw);
          onConfig(path, Number.isNaN(n) ? raw : n); // il testo non numerico lo boccia il server
          return;
        }
        onConfig(path, raw);
      }} />
  );
}

function ConnectorSetup({ connectors, authModule, authConfig, onModule, onConfig }) {
  const current = connectors.find((c) => c.name === authModule) || null;
  const schema = connectorSchemaOf(current);
  const props = schema.properties && typeof schema.properties === "object" ? schema.properties : {};
  const required = Array.isArray(schema.required) ? schema.required : [];
  const setAt = (path, value) => onConfig(cfgSet(authConfig || {}, path, value));

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <Select label="Connettore di autenticazione" required value={authModule || ""}
        options={[{ value: "", label: "Scegli…" }].concat(connectors.map((c) => ({ value: c.name, label: c.name })))}
        onChange={(e) => { onModule(e.target.value); onConfig({}); }} />
      {current && Object.keys(props).map((key) => (
        <SchemaField key={key} name={key} prop={props[key]} required={required.indexOf(key) !== -1}
          path={[key]} config={authConfig || {}} onConfig={setAt} />
      ))}
      {current && Object.keys(props).length === 0 && (
        <span style={{ fontSize: 12, color: "var(--text-subtle)" }}>Questo connettore non richiede configurazione.</span>
      )}
    </div>
  );
}

// ---- Porta online (usato dalla schermata organizzazione) --------------------
// La config del connettore si fornisce QUI, al momento del go-online: il
// backend la inoltra al provisioner senza salvarla (niente segreti nel DB).
function GoOnlineDialog({ organization, onClose, onDone, notify }) {
  const [connectors, setConnectors] = React.useState(null); // null = caricamento
  const [unavailable, setUnavailable] = React.useState(null); // messaggio 503
  const [authModule, setAuthModule] = React.useState("");
  const [authConfig, setAuthConfig] = React.useState({});
  const [error, setError] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  React.useEffect(() => {
    let alive = true;
    window.AriaData.getConnectors().then(
      (res) => {
        if (!alive) return;
        if (res.ok) setConnectors(res.connectors);
        else { setConnectors([]); setUnavailable(res.error); }
      },
      (err) => { if (alive) { setConnectors([]); setUnavailable(err.message || "Errore inatteso."); } }
    );
    return () => { alive = false; };
  }, []);

  async function submit() {
    setError(null);
    setBusy(true);
    try {
      const res = await window.AriaData.provisionOrg({ authModule, authConfig });
      if (res.ok) onDone(res.organization);
      else setError(res.error);
    } catch (err) {
      setError(err.message || "Errore inatteso.");
    } finally { setBusy(false); }
  }

  return (
    <Dialog open onClose={() => (busy ? null : onClose())} title={`Porta online «${organization.domain}»`}
      footer={<React.Fragment>
        <Button variant="secondary" disabled={busy} onClick={onClose}>Annulla</Button>
        <Button variant="primary" disabled={busy || !authModule || !!unavailable}
          iconLeft={busy ? <Icon name="loader" size={16} color="#fff" /> : <Icon name="cloud-upload" size={16} color="#fff" />}
          onClick={submit}>Avvia provisioning</Button>
      </React.Fragment>}>
      <div style={{ display: "flex", flexDirection: "column", gap: 14, minWidth: 380 }}>
        <span style={{ fontSize: 13, color: "var(--text-muted)" }}>
          Il dominio <b>{organization.domain}</b> verrà creato sull'infrastruttura. Scegli il backend di autenticazione degli utenti del dominio.
        </span>
        {unavailable && <Banner tone="warning" title="Provisioning non disponibile" icon="cloud-off">{unavailable}</Banner>}
        {error && <Banner tone="danger" icon="alert-circle">{error}</Banner>}
        {connectors == null ? (
          <span style={{ display: "flex", alignItems: "center", gap: 8, color: "var(--text-subtle)", fontSize: 13 }}>
            <Icon name="loader" size={15} color="var(--text-subtle)" /> Caricamento connettori…
          </span>
        ) : !unavailable && (
          <ConnectorSetup connectors={connectors} authModule={authModule} authConfig={authConfig}
            onModule={setAuthModule} onConfig={setAuthConfig} />
        )}
      </div>
    </Dialog>
  );
}

// ---- Wizard di creazione ----------------------------------------------------
function CreateOrgWizard({ onCancel, onCreated }) {
  const [name, setName] = React.useState("");
  const [slug, setSlug] = React.useState("");
  const [slugTouched, setSlugTouched] = React.useState(false);
  const [slugInfo, setSlugInfo] = React.useState(null); // { slug, available } | null
  const [checking, setChecking] = React.useState(false);
  const [provisioning, setProvisioning] = React.useState(null); // { available } | null
  const [connectors, setConnectors] = React.useState([]);
  const [offline, setOffline] = React.useState(false);
  const [authModule, setAuthModule] = React.useState("");
  const [authConfig, setAuthConfig] = React.useState({});
  const [error, setError] = React.useState(null);
  const [submitting, setSubmitting] = React.useState(false);

  // Disponibilità del provisioning: senza domain-provisioner l'offline è
  // l'unica modalità (il toggle diventa readonly).
  React.useEffect(() => {
    let alive = true;
    window.AriaData.getProvisioning().then(
      (info) => {
        if (!alive) return;
        setProvisioning(info);
        if (!info.available) setOffline(true);
        else window.AriaData.getConnectors().then((res) => { if (alive && res.ok) setConnectors(res.connectors); });
      },
      () => { if (alive) { setProvisioning({ available: false }); setOffline(true); } }
    );
    return () => { alive = false; };
  }, []);

  // Slug proposto dal nome (finché l'utente non lo tocca) e verifica di
  // disponibilità in tempo reale, con debounce sulle digitazioni.
  React.useEffect(() => {
    const source = slugTouched ? { slug } : { name };
    if ((slugTouched && slug === "") || (!slugTouched && name.trim() === "")) { setSlugInfo(null); return undefined; }
    setChecking(true);
    const t = setTimeout(async () => {
      try {
        const info = await window.AriaData.checkSlug(source);
        setSlugInfo(info);
        if (!slugTouched) setSlug(info.slug);
      } catch (_e) {
        setSlugInfo(null); // la rete di sicurezza resta il 409 del POST
      } finally {
        setChecking(false);
      }
    }, 350);
    return () => clearTimeout(t);
  }, [name, slug, slugTouched]);

  const forcedOffline = provisioning != null && !provisioning.available;
  const slugKnownTaken = slugInfo != null && slugInfo.slug === slug && !slugInfo.available;
  const canSubmit = !submitting && name.trim() !== "" && slug !== "" && !slugKnownTaken &&
    (offline || (authModule !== "" && provisioning != null && provisioning.available));

  async function submit(e) {
    if (e) e.preventDefault();
    if (!canSubmit) return;
    setError(null);
    setSubmitting(true);
    try {
      const payload = offline
        ? { name: name.trim(), slug, offline: true }
        : { name: name.trim(), slug, authModule, authConfig };
      const res = await window.AriaData.createOrg(payload);
      if (res.ok) onCreated(res);
      else setError(res.error);
    } catch (err) {
      setError(err.message || "Errore inatteso.");
    } finally {
      setSubmitting(false);
    }
  }

  const slugHint = checking
    ? "Verifica disponibilità…"
    : slugInfo && slugInfo.slug === slug
      ? (slugInfo.available ? `«${slug}» è disponibile.` : undefined)
      : "Il dominio dell'organizzazione: minuscole, numeri e trattini.";

  return (
    <div style={{ display: "flex", justifyContent: "center", padding: 28 }}>
      <form onSubmit={submit} style={{ width: "100%", maxWidth: 620, display: "flex", flexDirection: "column", gap: 16 }}>
        <Card>
          <div style={{ padding: "18px 20px", display: "flex", flexDirection: "column", gap: 16 }}>
            <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
              <span style={{ font: "var(--fw-semibold) 16px/1.3 var(--font-sans)", color: "var(--text-strong)" }}>Dati dell'organizzazione</span>
              <span style={{ fontSize: 13, color: "var(--text-muted)" }}>Il nome è libero; lo slug diventa il dominio (stile GitLab) e non si cambia.</span>
            </div>
            {error && <Banner tone="danger" icon="alert-circle">{error}</Banner>}
            <TextField label="Nome" required value={name} placeholder="Es. Monk Software"
              onChange={(e) => setName(e.target.value)} />
            <TextField label="Slug (dominio)" required value={slug}
              hint={slugHint}
              error={slugKnownTaken ? `«${slug}» non è disponibile.` : undefined}
              onChange={(e) => { setSlugTouched(true); setSlug(e.target.value); }} />
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <Switch checked={offline} disabled={forcedOffline || provisioning == null}
                onChange={(e) => setOffline(e.target.checked)}
                label="Organizzazione offline (solo Aria, senza dominio)" />
              {forcedOffline && <Badge tone="outline">provisioning non disponibile</Badge>}
            </div>
            {forcedOffline && (
              <span style={{ fontSize: 12, color: "var(--text-subtle)" }}>
                Il domain-provisioner non è configurato: l'organizzazione nasce offline e potrà essere portata online in seguito dalla sua schermata.
              </span>
            )}
          </div>
        </Card>

        {!offline && provisioning != null && provisioning.available && (
          <Card>
            <div style={{ padding: "18px 20px", display: "flex", flexDirection: "column", gap: 16 }}>
              <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                <span style={{ font: "var(--fw-semibold) 16px/1.3 var(--font-sans)", color: "var(--text-strong)" }}>Autenticazione del dominio</span>
                <span style={{ fontSize: 13, color: "var(--text-muted)" }}>
                  Il backend che verificherà le credenziali degli utenti del dominio. La configurazione va al provisioner e non viene salvata da Aria.
                </span>
              </div>
              <ConnectorSetup connectors={connectors} authModule={authModule} authConfig={authConfig}
                onModule={setAuthModule} onConfig={setAuthConfig} />
            </div>
          </Card>
        )}

        <div style={{ display: "flex", justifyContent: "flex-end", gap: 10 }}>
          <Button variant="secondary" type="button" disabled={submitting} onClick={onCancel}>Annulla</Button>
          <Button variant="primary" type="submit" disabled={!canSubmit}
            iconLeft={submitting ? <Icon name="loader" size={16} color="#fff" /> : <Icon name="plus" size={16} color="#fff" />}>
            {offline ? "Crea offline" : "Crea e avvia provisioning"}
          </Button>
        </div>
      </form>
    </div>
  );
}

Object.assign(window, { CreateOrgWizard, GoOnlineDialog, ConnectorSetup });
