// web/screens/releases.jsx — раздел «Релизы» (Фаза 3, §16.1).
// Стилистика — как в Ревью 1: page / card / btn-варианты / pill / label.
// Ключи ответов капитализированы (Go-JSON): Adult/Child/Property, Surname, Years…

const REL_BLOCKS = [
  ["adult", "Взрослые", "Adult"],
  ["child", "Детские", "Child"],
  ["property", "Проперти", "Property"],
];

function ReleasesScreen({ setRoute }) {
  const [, toast] = useToasts();
  const [tab, setTab] = React.useState("models");
  const [data, setData] = React.useState({});
  const [loading, setLoading] = React.useState(true);
  const [refreshing, setRefreshing] = React.useState(false);
  const [report, setReport] = React.useState(null);
  const [error, setError] = React.useState(null);

  const load = React.useCallback(() => {
    setLoading(true);
    api.release.ListModels()
      .then((d) => { setData(d || {}); setLoading(false); })
      .catch((e) => { setData({}); setLoading(false); toast("Не удалось загрузить модели: " + e.message, "error"); });
  }, []);
  React.useEffect(() => { load(); }, [load]);

  const refresh = () => {
    setRefreshing(true);
    setError(null);
    api.release.RefreshTemplates()
      .then((r) => {
        setReport(r || {});
        setRefreshing(false);
        load();
        const n = (r && r.Downloaded && r.Downloaded.length) || 0;
        toast(n > 0 ? `Обновлено, подтянул ${n} файл(ов)` : "Обновлено", "ok");
      })
      .catch((e) => { setRefreshing(false); setError(e.message || String(e)); toast("Ошибка обновления: " + e.message, "error"); });
  };

  const total = REL_BLOCKS.reduce((n, [, , key]) => n + (data[key] || []).length, 0);

  return (
    <div className="page" style={{ maxWidth: 1000, paddingTop: 16 }}>
      <div style={{ display: "flex", gap: 10, alignItems: "center", marginBottom: 14, flexWrap: "wrap" }}>
        <h1 style={{ margin: 0, fontSize: 16, fontWeight: 500 }}>Релизы</h1>
        <div style={{ width: 1, height: 18, background: "var(--line)" }} />
        <div style={{ display: "flex", gap: 4 }}>
          {[["models", "Модели"], ["templates", "Шаблоны"]].map(([id, label]) => (
            <button key={id}
                    className={"btn btn-sm" + (tab === id ? "" : " btn-ghost")}
                    onClick={() => setTab(id)}>{label}</button>
          ))}
        </div>
        <div style={{ flex: 1 }} />
        {tab === "models" && <span className="muted" style={{ fontSize: 12 }}>{total} модел.</span>}
        <button className="btn btn-sm" disabled={refreshing} onClick={refresh}>
          {refreshing ? <><Spinner /> Обновляю…</> : <>↻ Обновить из Яндекса</>}
        </button>
      </div>

      {error && (
        <div className="card card-pad" style={{ marginBottom: 14, borderColor: "var(--warn)", color: "var(--warn)" }}>
          <b>Ошибка обновления:</b> {error}
          <div className="muted" style={{ marginTop: 4, fontSize: 12 }}>
            Проверь, что файлы на Яндексе в <span className="mono">_Pikasrv/_Releases/templates/</span> и Я.Диск подключён.
          </div>
        </div>
      )}
      {report && <RefreshReport report={report} />}

      {loading ? (
        <div className="muted" style={{ padding: 32 }}><Spinner /> загружаем…</div>
      ) : tab === "models" ? (
        <ModelsTab data={data} onGenerated={load} />
      ) : (
        <TemplatesTab report={report} onRefresh={refresh} />
      )}
    </div>
  );
}

function RefreshReport({ report }) {
  const probs = [];
  (report.MissingMasks || []).forEach((t) => probs.push("нет маски типа «" + t + "»"));
  (report.ModelsWithoutBlank || []).forEach((m) => probs.push("нет бланка: " + m));
  (report.ModelsWithoutPhoto || []).forEach((m) => probs.push("нет фото: " + m));
  return (
    <div className="card card-pad" style={{ marginBottom: 14, fontSize: 13 }}>
      <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
        <b>Обновление из Яндекса</b>
        <span className="pill">подтянул {(report.Downloaded || []).length}</span>
        <span className="pill">моделей {report.Models || 0}</span>
        {(report.Removed || []).length > 0 && <span className="pill">удалил {report.Removed.length}</span>}
      </div>
      {probs.length > 0 && (
        <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 8 }}>
          {probs.map((p, i) => <span key={i} className="pill warn"><span className="pdot" />{p}</span>)}
        </div>
      )}
      {(report.Failed || []).length > 0 && (
        <div style={{ marginTop: 8, color: "var(--warn)", fontSize: 12 }}>
          ⚠ не удалось: {report.Failed.join("; ")}
        </div>
      )}
      {(report.Ignored || []).length > 0 && (
        <div className="muted" style={{ marginTop: 6, fontSize: 12 }}>
          пропущены (имя не подошло): {report.Ignored.join(", ")}
        </div>
      )}
    </div>
  );
}

function ModelsTab({ data, onGenerated }) {
  data = data || {};
  const empty = REL_BLOCKS.every(([, , key]) => !(data[key] || []).length);
  if (empty) {
    return (
      <div className="card card-pad muted">
        Пока нет моделей. Положите шаблоны на Яндекс в <span className="mono">_Releases/templates/</span>
        {" "}(бланк <span className="mono">adult_Фамилия_Имя.jpg</span>, фото
        {" "}<span className="mono">adult_Фамилия_Имя_2026.jpg</span>, маска <span className="mono">adult_mask.png</span>)
        {" "}и нажмите «Обновить из Яндекса».
      </div>
    );
  }
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 22 }}>
      {REL_BLOCKS.map(([type, title, key]) => {
        const models = data[key] || [];
        if (!models.length) return null;
        return (
          <div key={type}>
            <div className="label" style={{ marginBottom: 8 }}>{title} · {models.length}</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
              {models.map((m) => <ModelRow key={m.ID} model={m} onGenerated={onGenerated} />)}
            </div>
          </div>
        );
      })}
    </div>
  );
}

function todayISO() { return new Date().toISOString().slice(0, 10); }

function ModelRow({ model, onGenerated }) {
  const [, toast] = useToasts();
  const [open, setOpen] = React.useState(false);
  const [date, setDate] = React.useState(todayISO());
  const [desc, setDesc] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [result, setResult] = React.useState(null);
  const [translating, setTranslating] = React.useState(false);
  const [releases, setReleases] = React.useState(null);

  const canGen = (model.Years || []).length && model.HasBlank;

  const loadReleases = React.useCallback(() => {
    api.release.ListByModel(model.ID).then((r) => setReleases(r || [])).catch(() => setReleases([]));
  }, [model.ID]);
  React.useEffect(() => { loadReleases(); }, [loadReleases]);

  const del = (id) => {
    api.release.Delete(id)
      .then(() => { loadReleases(); onGenerated && onGenerated(); toast("Релиз удалён", "ok"); })
      .catch((e) => toast("Не удалось удалить: " + e.message, "error"));
  };

  const delModel = () => {
    if (!window.confirm(`Удалить модель «${model.Surname} ${model.GivenName}» из справочника? (для чистки опечаток; готовые релизы не трогаются)`)) return;
    api.release.DeleteModel(model.ID)
      .then(() => { onGenerated && onGenerated(); toast("Модель удалена", "ok"); })
      .catch((e) => toast("Не удалось удалить модель: " + e.message, "error"));
  };

  const translate = () => {
    if (!desc.trim()) return;
    setTranslating(true);
    api.release.TranslateDescription(desc)
      .then((en) => {
        setTranslating(false);
        if (en && en !== desc) setDesc(en);
        else toast("Перевод не сработал (модель недоступна?)", "warn");
      })
      .catch((e) => { setTranslating(false); toast("Перевод не удался: " + e.message, "error"); });
  };

  const generate = () => {
    setBusy(true); setResult(null);
    api.release.Generate(date, [{ ModelID: model.ID, Description: desc }])
      .then((res) => {
        const r = res && res[0];
        setResult(r);
        setBusy(false);
        if (r && !r.Error) { toast("Релиз создан: " + r.OutputName, "ok"); loadReleases(); onGenerated && onGenerated(); }
      })
      .catch((e) => { setBusy(false); toast("Ошибка генерации: " + e.message, "error"); });
  };

  return (
    <div className="card" style={{ padding: "12px 14px" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
        <b style={{ minWidth: 200 }}>{model.Surname} {model.GivenName}</b>
        <div style={{ display: "flex", gap: 4, flexWrap: "wrap" }}>
          {(model.Years || []).length
            ? (model.Years || []).map((y) => <span key={y} className="pill">{y}</span>)
            : <span className="pill warn"><span className="pdot" />нет фото</span>}
          {!model.HasBlank && <span className="pill warn"><span className="pdot" />нет бланка</span>}
        </div>
        <div style={{ flex: 1 }} />
        <button className="btn btn-sm btn-ghost" onClick={delModel} title="Удалить модель из справочника (чистка опечаток)">Удалить</button>
        <button className={"btn btn-sm" + (open ? "" : " btn-primary")} onClick={() => setOpen(!open)}>
          {open ? "Свернуть" : "Создать релиз"}
        </button>
      </div>

      {releases && releases.length > 0 && (
        <div style={{ marginTop: 12, paddingTop: 12, borderTop: "1px solid var(--line)" }}>
          <div className="label" style={{ marginBottom: 6 }}>Готовые релизы</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
            {releases.map((r) => (
              <div key={r.ID} style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 12 }}>
                <span className="pill ok"><span className="pdot" />{r.Date}</span>
                <span className="mono">{r.OutputName}</span>
                {r.UsedNearestYear && <span className="muted">фото за {r.UsedYear} (другой год)</span>}
                <div style={{ flex: 1 }} />
                <button className="btn btn-xs" onClick={() => del(r.ID)}>Удалить</button>
              </div>
            ))}
          </div>
        </div>
      )}

      {open && (
        <div style={{ marginTop: 12, paddingTop: 12, borderTop: "1px solid var(--line)", display: "flex", flexDirection: "column", gap: 12 }}>
          <div className="label">Создать релиз</div>
          <div style={{ display: "flex", gap: 16, flexWrap: "wrap" }}>
            <div className="field" style={{ maxWidth: 200 }}>
              <label className="label">Дата съёмки</label>
              <input type="date" value={date} onChange={(e) => setDate(e.target.value)} />
            </div>
          </div>

          <div className="field">
            <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <label className="label" style={{ margin: 0 }}>Описание</label>
              <MicButton onInsert={(t) => setDesc((d) => (d ? d + " " : "") + t)} title="Надиктовать (можно по-русски)" />
              <button className="btn btn-xs" disabled={translating || !desc.trim()} onClick={translate}>
                {translating ? <><Spinner /> перевожу…</> : "Перевести на английский"}
              </button>
              <span className="muted" style={{ fontSize: 11 }}>EN напрямую или RU → «Перевести»</span>
            </div>
            <textarea rows={2} value={desc} onChange={(e) => setDesc(e.target.value)}
                      placeholder="одно описание на дату съёмки (на английском)" />
          </div>

          <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
            <button className="btn btn-primary btn-sm" disabled={busy || !canGen} onClick={generate}>
              {busy ? <><Spinner /> Генерирую…</> : "Сгенерировать релиз"}
            </button>
            {!canGen && <span className="muted" style={{ fontSize: 12 }}>нужны бланк и фото</span>}
            {result && (result.Error
              ? <span className="pill warn"><span className="pdot" />{result.Error}</span>
              : <span className="pill ok"><span className="pdot" />
                  {result.OutputName}{result.UsedNearestYear ? ` · фото за ${result.UsedYear} (другой год)` : ""}
                </span>)}
          </div>
        </div>
      )}
    </div>
  );
}

function TemplatesTab({ report, onRefresh }) {
  return (
    <div className="card card-pad" style={{ fontSize: 13 }}>
      <div style={{ marginBottom: 10 }}>
        Шаблоны лежат на Яндексе в <span className="mono">_Releases/templates/</span>:
      </div>
      <ul style={{ margin: "0 0 12px", paddingLeft: 18, color: "var(--ink-2)" }}>
        <li>маски по типу: <span className="mono">adult_mask.png</span>, <span className="mono">child_mask.png</span>, <span className="mono">property_mask.png</span> (PNG)</li>
        <li>бланки моделей: <span className="mono">тип_Фамилия_Имя.jpg</span></li>
        <li>фото по годам: <span className="mono">тип_Фамилия_Имя_ГГГГ.jpg</span></li>
      </ul>
      {report ? (
        <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
          {(report.MissingMasks || []).length
            ? report.MissingMasks.map((t) => <span key={t} className="pill warn"><span className="pdot" />нет маски «{t}»</span>)
            : <span className="pill ok"><span className="pdot" />маски на месте</span>}
          {(report.ModelsWithoutBlank || []).length > 0 && <span className="pill warn"><span className="pdot" />без бланка: {report.ModelsWithoutBlank.length}</span>}
          {(report.ModelsWithoutPhoto || []).length > 0 && <span className="pill warn"><span className="pdot" />без фото: {report.ModelsWithoutPhoto.length}</span>}
        </div>
      ) : (
        <button className="btn btn-sm" onClick={onRefresh}>Обновить из Яндекса, чтобы увидеть статус</button>
      )}
    </div>
  );
}
