/* The audience builder — a segment written as rules, with a preview that runs
   those rules rather than describing them.

   Commit model: STAGED. Nothing is created until Save.
   Data state: one branch — a form. The preview has a no-matches state, which is
   a state of the form, not of the screen.
   The preview executes the real predicates from SEGMENT_RULES over the real
   forty-contact sample, and says so: the matched count is exact for the sample,
   and the size is an extrapolation to the reachable list, rounded to ten because
   a sample of forty cannot support a number carried out to the digit. The saved
   audience keeps that honesty — it is marked as estimated until the engine
   evaluates it in full. */
(() => {
  const {
    Button, Input, Select, Field, FieldLabel, FieldDescription, FieldError, Badge, Alert, AlertTitle, AlertDescription,
  } = window.OriginDesignSystem_7c998b;

  const EMPTY_RULES = { lifecycle: [], plan: [], engagement: "any", tenure: "any" };

  function ChipSet({ options, value, onChange, label }) {
    return (
      <span className="md-chipset" role="group" aria-label={label}>
        {options.map((option) => {
          const on = value.indexOf(option.value) > -1;
          return (
            <button
              type="button"
              key={option.value}
              className="md-chip"
              aria-pressed={on}
              onClick={() => onChange(on ? value.filter((v) => v !== option.value) : value.concat([option.value]))}
            >
              {option.label}
            </button>
          );
        })}
      </span>
    );
  }

  function NewAudienceScreen({ data, workspace, now, onCreate }) {
    const [name, setName] = React.useState("");
    const [rules, setRules] = React.useState(EMPTY_RULES);
    const [submitted, setSubmitted] = React.useState(false);

    const sample = data.contacts;
    const matched = evaluateSegment(rules, sample, now);
    const reachable = reachableContacts(workspace);
    const estimate = Math.min(reachable, estimateFromSample(matched.length, sample.length, reachable));
    const newlyJoined = matched.filter((c) => now - c.signupAt <= 30 * DAY_MS);
    const joinedEstimate = Math.min(estimate, estimateFromSample(newlyJoined.length, sample.length, reachable));
    const active = activeSegmentRules(rules);

    const nameError = name.trim().length < 3 ? "Name it — at least three characters." : null;
    const ruleError = active.length === 0 ? "Set at least one rule, or this audience is just everybody." : null;
    const emptyError = matched.length === 0 ? "Nothing in the sample matches, so this would very likely send to nobody." : null;
    const valid = !nameError && !ruleError && !emptyError;
    const dirty = name !== "" || active.length > 0;
    const set = (id, value) => setRules((r) => Object.assign({}, r, { [id]: value }));

    return (
      <>
        <PageHeader path="/audiences/new" title="New audience" />

        <div className="md-scope">
          <p className="md-scope__note">
            A rule-based segment: it is re-evaluated before every send, so it always reflects who matches at that moment rather than who matched today.
          </p>
        </div>

        <div className="md-form md-form--one">
          <Field>
            <FieldLabel htmlFor="cd-na-name">What the audience is called</FieldLabel>
            <Input id="cd-na-name" placeholder="Trial, opened this week" value={name} invalid={submitted && !!nameError} onChange={(e) => setName(e.target.value)} />
            {submitted && nameError ? <FieldError>{nameError}</FieldError> : <FieldDescription>Shown wherever a journey or a broadcast names its audience.</FieldDescription>}
          </Field>
        </div>

        <section className="md-band md-band--flush">
          <div className="md-band__head">
            <div>
              <h2 className="md-band__title">Rules</h2>
              <p className="md-band__note">Every rule you set has to be true. A rule left alone does not narrow anything.</p>
            </div>
          </div>
          <div className="md-rules">
            {SEGMENT_RULES.map((rule) => (
              <Field key={rule.id}>
                <FieldLabel>{rule.label}</FieldLabel>
                {rule.kind === "chips" ? (
                  <ChipSet options={rule.options} value={rules[rule.id]} label={rule.label} onChange={(value) => set(rule.id, value)} />
                ) : (
                  <Select items={rule.options} value={rules[rule.id]} aria-label={rule.label} onValueChange={(value) => set(rule.id, value)} />
                )}
                <FieldDescription>
                  {rule.active(rules[rule.id]) ? "Narrowing by this." : "Not narrowing by this."}
                </FieldDescription>
              </Field>
            ))}
          </div>
        </section>

        <Figures three items={[
          {
            label: "Matched in the sample",
            value: formatCount(matched.length) + " of " + sample.length,
            qualifier: active.length
              ? "Exact: the rules were run over the forty most recent contacts"
              : "No rule set, so everybody in the sample matches",
          },
          {
            label: "Estimated size",
            value: matched.length ? "about " + formatCount(estimate) : "0",
            qualifier: matched.length
              ? formatPercent(rate(matched.length, sample.length)) + " of the sample, extrapolated to the " + formatCount(reachable) + " reachable"
              : "Nothing in the sample matches these rules",
          },
          {
            label: "New in the last 30 days",
            value: newlyJoined.length ? "about " + formatCount(joinedEstimate) : "0",
            qualifier: newlyJoined.length
              ? formatCount(newlyJoined.length) + " of the " + matched.length + " matches joined recently"
              : "None of the matches joined in the last 30 days",
          },
        ]} />

        <section className="md-band">
          <div className="md-band__head">
            <div>
              <h2 className="md-band__title">Who this would reach</h2>
              <p className="md-band__note">{describeSegment(rules)}</p>
            </div>
          </div>
          {matched.length === 0 ? (
            <Alert variant="warning">
              <Icon name="user-x" size={16} />
              <AlertTitle>Nothing in the sample matches</AlertTitle>
              <AlertDescription>Loosen a rule. Saving this would create an audience that very likely sends to nobody.</AlertDescription>
            </Alert>
          ) : (
            <ul className="md-sample">
              {matched.slice(0, 5).map((contact) => (
                <li key={contact.id}>
                  <span>{contact.name}</span>
                  <span className="md-sample__email">{contact.email}</span>
                  <span className="md-sample__note">
                    {labelOf(LIFECYCLES, contact.lifecycle)} · {labelOf(PLANS, contact.plan)} · {contact.lastOpenAt ? "opened " + formatWhen(contact.lastOpenAt, now).toLowerCase() : "never opened"}
                  </span>
                </li>
              ))}
            </ul>
          )}
          {matched.length > 5 ? <p className="md-footnote">Five of the {matched.length} sample matches shown.</p> : null}
        </section>

        <FormBar
          dirty={dirty && valid}
          label="New audience"
          note={submitted && !valid
            ? [nameError, ruleError, emptyError].filter(Boolean).join(" ")
            : dirty
              ? "Saves the rules and the estimate. The engine evaluates the full list within the hour and replaces the estimate."
              : "Name it, then set the rules. The numbers above move as you go."}
          onCancel={() => { setName(""); setRules(EMPTY_RULES); setSubmitted(false); }}
          onSave={() => { setSubmitted(true); if (valid) onCreate({ name: name.trim(), rules: rules, estimate: estimate, joined30: joinedEstimate, matched: matched }); }}
        />
      </>
    );
  }

  Object.assign(window, { NewAudienceScreen, ChipSet });
})();
