import {
  type CorrectionAdminView,
  ErrorCode,
  type RetrainResultView,
  type TrainingSuggestionAdminView,
  type TrainingSuggestionGroupView,
} from "@batch-cooking/shared";
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { AdminApiError, adminApiClient } from "../../../api/admin-client";
import { errorMessageService } from "../../../services/error-message.service";
import "../admin-page.scss";
import "./corrections-page.scss";
import { linesToList, listsDiffer, listToLines } from "./corrections";

type Tab = "suggestions" | "corrections";

/**
 * Tech-step correction triage. Two tabs — curated `TrainingSuggestion`s and
 * raw `StepTechStepCorrection`s — plus the snippet generator and the F1
 * gate + backfill trigger. Replaces the `list-pending-training-suggestions.ts`
 * / `retrain-tech-steps.ts` CLI pair.
 */
export function CorrectionsPage() {
  const { t } = useTranslation();
  const [tab, setTab] = useState<Tab>("suggestions");

  return (
    <div className="admin-page">
      <h1 className="admin-page__title">{t("admin.corrections.title")}</h1>
      <p className="admin-page__lead">{t("admin.corrections.lead")}</p>

      <p className="corrections-caveat">{t("admin.corrections.caveat")}</p>

      <div className="corrections-tabs">
        <button
          type="button"
          className={tab === "suggestions" ? "active" : undefined}
          onClick={() => setTab("suggestions")}
        >
          {t("admin.corrections.tab.suggestions")}
        </button>
        <button
          type="button"
          className={tab === "corrections" ? "active" : undefined}
          onClick={() => setTab("corrections")}
        >
          {t("admin.corrections.tab.corrections")}
        </button>
      </div>

      {tab === "suggestions" ? <SuggestionsTab /> : <CorrectionsTab />}
    </div>
  );
}

// --- Suggestions tab -------------------------------------------------------

type SuggestionsState =
  | { status: "loading" }
  | { status: "loaded"; groups: TrainingSuggestionGroupView[] }
  | { status: "error" };

function SuggestionsTab() {
  const { t } = useTranslation();
  const [statusFilter, setStatusFilter] = useState("");
  const [sourceFilter, setSourceFilter] = useState("");
  const [state, setState] = useState<SuggestionsState>({ status: "loading" });

  const load = useCallback(() => {
    setState({ status: "loading" });
    adminApiClient
      .getSuggestions({
        status: statusFilter || undefined,
        sourceType: sourceFilter || undefined,
      })
      .then((groups) => setState({ status: "loaded", groups }))
      .catch(() => setState({ status: "error" }));
  }, [statusFilter, sourceFilter]);

  useEffect(load, [load]);

  return (
    <div className="suggestions-tab">
      <RetrainPanel />
      <SnippetPanel />

      <div className="corrections-filters">
        <label>
          {t("admin.corrections.filter.status")}
          <select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
            <option value="">{t("admin.corrections.filter.any")}</option>
            <option value="pending">pending</option>
            <option value="applied">applied</option>
            <option value="rejected">rejected</option>
          </select>
        </label>
        <label>
          {t("admin.corrections.filter.source")}
          <select value={sourceFilter} onChange={(e) => setSourceFilter(e.target.value)}>
            <option value="">{t("admin.corrections.filter.any")}</option>
            <option value="correction">correction</option>
            <option value="llm_audit">llm_audit</option>
          </select>
        </label>
      </div>

      {state.status === "loading" && (
        <p className="admin-page__placeholder">{t("admin.common.loading")}</p>
      )}
      {state.status === "error" && (
        <p className="admin-page__placeholder">{t("admin.common.loadError")}</p>
      )}
      {state.status === "loaded" && state.groups.length === 0 && (
        <p className="admin-page__placeholder">{t("admin.corrections.noSuggestions")}</p>
      )}
      {state.status === "loaded" &&
        state.groups.map((group) => (
          <section key={group.techStepKey} className="suggestion-group">
            <h2>{group.techStepKey}</h2>
            {group.suggestions.map((suggestion) => (
              <SuggestionCard key={suggestion.id} suggestion={suggestion} onMutated={load} />
            ))}
          </section>
        ))}
    </div>
  );
}

function SuggestionCard({
  suggestion,
  onMutated,
}: {
  suggestion: TrainingSuggestionAdminView;
  onMutated: () => void;
}) {
  const { t } = useTranslation();
  const [synonyms, setSynonyms] = useState(listToLines(suggestion.suggestedSynonyms));
  const [utterances, setUtterances] = useState(listToLines(suggestion.suggestedUtterances));
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const dirty =
    listsDiffer(linesToList(synonyms), suggestion.suggestedSynonyms) ||
    listsDiffer(linesToList(utterances), suggestion.suggestedUtterances);

  async function patch(body: Parameters<typeof adminApiClient.updateSuggestion>[1]) {
    setBusy(true);
    setError(null);
    try {
      await adminApiClient.updateSuggestion(suggestion.id, body);
      onMutated();
    } catch (err) {
      setError(
        errorMessageService.getLabel(
          err instanceof AdminApiError ? err.code : ErrorCode.INTERNAL_ERROR,
        ),
      );
    } finally {
      setBusy(false);
    }
  }

  return (
    <article className={`suggestion-card suggestion-card--${suggestion.status}`}>
      <header className="suggestion-card__head">
        <span className="suggestion-card__meta">
          #{suggestion.id} · {suggestion.locale} · {suggestion.sourceType} ·{" "}
          <strong>{suggestion.status}</strong>
        </span>
      </header>

      {suggestion.sourceCorrection && (
        <p className="suggestion-card__source">
          <span className="suggestion-card__clause">
            « {suggestion.sourceCorrection.clauseText} »
          </span>{" "}
          {suggestion.sourceCorrection.previousTechStepKey ?? "∅"} →{" "}
          {suggestion.sourceCorrection.correctedTechStepKey ?? "∅"}
        </p>
      )}

      <label>
        {t("admin.corrections.synonyms")}
        <textarea value={synonyms} onChange={(e) => setSynonyms(e.target.value)} rows={3} />
      </label>
      <label>
        {t("admin.corrections.utterances")}
        <textarea value={utterances} onChange={(e) => setUtterances(e.target.value)} rows={3} />
      </label>

      {error && <p className="suggestion-card__error">{error}</p>}

      <div className="suggestion-card__actions">
        <button
          type="button"
          disabled={busy || !dirty}
          onClick={() =>
            patch({
              suggestedSynonyms: linesToList(synonyms),
              suggestedUtterances: linesToList(utterances),
            })
          }
        >
          {t("admin.corrections.save")}
        </button>
        <button type="button" disabled={busy} onClick={() => patch({ status: "applied" })}>
          {t("admin.corrections.apply")}
        </button>
        <button type="button" disabled={busy} onClick={() => patch({ status: "rejected" })}>
          {t("admin.corrections.reject")}
        </button>
      </div>
    </article>
  );
}

// --- Snippet + retrain panels ------------------------------------------------

function SnippetPanel() {
  const { t } = useTranslation();
  const [techStepKey, setTechStepKey] = useState("");
  const [snippet, setSnippet] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  async function generate() {
    setError(null);
    setSnippet(null);
    try {
      const result = await adminApiClient.getTrainingDataSnippet({
        techStepKey: techStepKey.trim(),
        status: "applied",
      });
      setSnippet(result.snippet);
    } catch (err) {
      setError(
        errorMessageService.getLabel(
          err instanceof AdminApiError ? err.code : ErrorCode.INTERNAL_ERROR,
        ),
      );
    }
  }

  return (
    <section className="corrections-panel">
      <h2>{t("admin.corrections.snippet.title")}</h2>
      <p>{t("admin.corrections.snippet.help")}</p>
      <div className="corrections-panel__row">
        <input
          value={techStepKey}
          onChange={(e) => setTechStepKey(e.target.value)}
          placeholder={t("admin.corrections.snippet.keyPlaceholder")}
        />
        <button type="button" disabled={techStepKey.trim().length === 0} onClick={generate}>
          {t("admin.corrections.snippet.generate")}
        </button>
      </div>
      {error && <p className="suggestion-card__error">{error}</p>}
      {snippet !== null && (
        <textarea className="corrections-snippet" readOnly rows={10} value={snippet} />
      )}
    </section>
  );
}

function RetrainPanel() {
  const { t } = useTranslation();
  const [busy, setBusy] = useState(false);
  const [result, setResult] = useState<RetrainResultView | null>(null);
  const [error, setError] = useState<string | null>(null);

  async function run() {
    setBusy(true);
    setError(null);
    setResult(null);
    try {
      setResult(await adminApiClient.retrain({}));
    } catch (err) {
      setError(
        errorMessageService.getLabel(
          err instanceof AdminApiError ? err.code : ErrorCode.INTERNAL_ERROR,
        ),
      );
    } finally {
      setBusy(false);
    }
  }

  return (
    <section className="corrections-panel corrections-panel--retrain">
      <h2>{t("admin.corrections.retrain.title")}</h2>
      <p>{t("admin.corrections.retrain.help")}</p>
      <button type="button" disabled={busy} onClick={run}>
        {busy ? t("admin.corrections.retrain.running") : t("admin.corrections.retrain.run")}
      </button>
      {error && <p className="suggestion-card__error">{error}</p>}
      {result && (
        <p className={`retrain-result retrain-result--${result.gatePassed ? "ok" : "fail"}`}>
          F1 {result.f1.toFixed(3)} / {result.minF1} —{" "}
          {result.gatePassed
            ? t("admin.corrections.retrain.passed", {
                total: result.backfilled?.total ?? 0,
                changed: result.backfilled?.changed ?? 0,
              })
            : t("admin.corrections.retrain.failed")}
        </p>
      )}
    </section>
  );
}

// --- Raw corrections tab --------------------------------------------------

type CorrectionsState =
  | { status: "loading" }
  | { status: "loaded"; rows: CorrectionAdminView[] }
  | { status: "error" };

function CorrectionsTab() {
  const { t } = useTranslation();
  const [consumed, setConsumed] = useState("");
  const [hasCorrected, setHasCorrected] = useState("");
  const [state, setState] = useState<CorrectionsState>({ status: "loading" });

  useEffect(() => {
    let cancelled = false;
    setState({ status: "loading" });
    adminApiClient
      .getCorrections({
        consumed: consumed || undefined,
        hasCorrectedTechStep: hasCorrected || undefined,
      })
      .then((rows) => {
        if (!cancelled) setState({ status: "loaded", rows });
      })
      .catch(() => {
        if (!cancelled) setState({ status: "error" });
      });
    return () => {
      cancelled = true;
    };
  }, [consumed, hasCorrected]);

  return (
    <div className="corrections-tab">
      <div className="corrections-filters">
        <label>
          {t("admin.corrections.filter.consumed")}
          <select value={consumed} onChange={(e) => setConsumed(e.target.value)}>
            <option value="">{t("admin.corrections.filter.any")}</option>
            <option value="true">{t("admin.corrections.filter.yes")}</option>
            <option value="false">{t("admin.corrections.filter.no")}</option>
          </select>
        </label>
        <label>
          {t("admin.corrections.filter.hasCorrected")}
          <select value={hasCorrected} onChange={(e) => setHasCorrected(e.target.value)}>
            <option value="">{t("admin.corrections.filter.any")}</option>
            <option value="true">{t("admin.corrections.filter.yes")}</option>
            <option value="false">{t("admin.corrections.filter.no")}</option>
          </select>
        </label>
      </div>

      {state.status === "loading" && (
        <p className="admin-page__placeholder">{t("admin.common.loading")}</p>
      )}
      {state.status === "error" && (
        <p className="admin-page__placeholder">{t("admin.common.loadError")}</p>
      )}
      {state.status === "loaded" && (
        <div className="corrections-table-wrap">
          <table className="corrections-table">
            <thead>
              <tr>
                <th>#</th>
                <th>{t("admin.corrections.col.clause")}</th>
                <th>{t("admin.corrections.col.change")}</th>
                <th>{t("admin.corrections.col.created")}</th>
                <th>{t("admin.corrections.col.consumed")}</th>
              </tr>
            </thead>
            <tbody>
              {state.rows.map((row) => (
                <tr key={row.id}>
                  <td>{row.id}</td>
                  <td className="corrections-table__clause">« {row.clauseText} »</td>
                  <td>
                    {row.previousTechStepKey ?? "∅"} → {row.correctedTechStepKey ?? "∅"}
                  </td>
                  <td>{new Date(row.createdAt).toLocaleDateString("fr-FR")}</td>
                  <td>{row.consumedAt ? "✓" : "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}
