import { useCallback, useEffect, useMemo, useState } from "react";
import { apiClient } from "../api/client";
import type {
  AutoRunCaseSummary,
  AutoRunDetailResponse,
  AutoRunDialogResponse,
  AutoRunDomainCoverage,
  AutoRunHistoryResponse,
  AutoRunSummary,
  ConnectionState,
  PromptState
} from "../state/types";
import { JsonView } from "./JsonView";
import { PanelFrame } from "./PanelFrame";

interface AutoRunsHistoryPanelProps {
  connection: ConnectionState;
  prompts: PromptState;
  assistantPromptVersion: string;
  decompositionPromptVersion: string;
  onLog?: (message: string) => void;
}

type UseMockFilter = "any" | "true" | "false";

interface AutoRunsFilters {
  fromLocal: string;
  toLocal: string;
  target: string;
  mode: string;
  useMock: UseMockFilter;
  promptContains: string;
  limit: number;
}

const DEFAULT_FILTERS: AutoRunsFilters = {
  fromLocal: "",
  toLocal: "",
  target: "all",
  mode: "all",
  useMock: "any",
  promptContains: "",
  limit: 120
};

function dateToInputValue(date: Date): string {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hour = String(date.getHours()).padStart(2, "0");
  const minute = String(date.getMinutes()).padStart(2, "0");
  return `${year}-${month}-${day}T${hour}:${minute}`;
}

function defaultFromDateValue(): string {
  const date = new Date();
  date.setDate(date.getDate() - 14);
  return dateToInputValue(date);
}

function localInputToIso(value: string): string | undefined {
  if (!value.trim()) return undefined;
  const parsed = Date.parse(value);
  if (!Number.isFinite(parsed)) return undefined;
  return new Date(parsed).toISOString();
}

function formatDateTime(iso: string): string {
  const parsed = Date.parse(iso);
  if (!Number.isFinite(parsed)) return iso;
  return new Date(parsed).toLocaleString("ru-RU");
}

function toPercent(closed: number, total: number): number {
  if (total <= 0) return 0;
  return Math.max(0, Math.min(100, Number(((closed / total) * 100).toFixed(1))));
}

function formatScore(value: number | null): string {
  if (typeof value !== "number") return "n/a";
  return `${value.toFixed(1)}%`;
}

function formatShortTarget(value: string): string {
  if (value === "assistant_stage1") return "assistant/s1";
  if (value === "assistant_stage2") return "assistant/s2";
  if (value === "assistant_p0") return "assistant/p0";
  return value;
}

function trendLabel(value: "up" | "down" | "flat"): string {
  if (value === "up") return "Рост";
  if (value === "down") return "Регресс";
  return "Без изменений";
}

function getSelectedCase(cases: AutoRunCaseSummary[], caseId: string): AutoRunCaseSummary | null {
  return cases.find((item) => item.case_id === caseId) ?? null;
}

function renderCoverageRows(items: AutoRunDomainCoverage[]) {
  if (items.length === 0) {
    return <p className="muted">Покрытие доменов пока не сформировано.</p>;
  }

  return (
    <div className="autoruns-coverage-list">
      {items.map((item) => {
        const percent = toPercent(item.closed_cases, item.total_cases);
        return (
          <div key={item.domain} className="autoruns-coverage-item">
            <div className="autoruns-coverage-head">
              <strong>{item.domain}</strong>
              <span>
                {item.closed_cases}/{item.total_cases} ({percent}%)
              </span>
            </div>
            <div className="autoruns-coverage-bar">
              <div style={{ width: `${percent}%` }} />
            </div>
          </div>
        );
      })}
    </div>
  );
}

export function AutoRunsHistoryPanel({
  connection,
  prompts,
  assistantPromptVersion,
  decompositionPromptVersion,
  onLog
}: AutoRunsHistoryPanelProps) {
  const [filters, setFilters] = useState<AutoRunsFilters>({
    ...DEFAULT_FILTERS,
    fromLocal: defaultFromDateValue()
  });
  const [history, setHistory] = useState<AutoRunHistoryResponse | null>(null);
  const [runDetail, setRunDetail] = useState<AutoRunDetailResponse | null>(null);
  const [dialog, setDialog] = useState<AutoRunDialogResponse | null>(null);
  const [selectedRunId, setSelectedRunId] = useState("");
  const [selectedCaseId, setSelectedCaseId] = useState("");
  const [historyBusy, setHistoryBusy] = useState(false);
  const [detailBusy, setDetailBusy] = useState(false);
  const [dialogBusy, setDialogBusy] = useState(false);
  const [errorText, setErrorText] = useState("");
  const [showAssistantMode, setShowAssistantMode] = useState(true);
  const [showDecompositionMode, setShowDecompositionMode] = useState(true);
  const [showProgressMode, setShowProgressMode] = useState(true);

  const activeRunSummary: AutoRunSummary | null =
    history?.items.find((item) => item.run_id === selectedRunId) ?? null;
  const activeCase = runDetail ? getSelectedCase(runDetail.cases, selectedCaseId) : null;

  const log = useCallback(
    (message: string) => {
      onLog?.(`[autoruns] ${message}`);
    },
    [onLog]
  );

  const loadCaseDialog = useCallback(
    async (runId: string, caseId: string) => {
      setDialogBusy(true);
      try {
        const payload = await apiClient.loadAutoRunCaseDialog(runId, caseId);
        setDialog(payload);
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        setErrorText(`Диалог кейса: ${message}`);
        log(`Dialog load error for ${runId}/${caseId}: ${message}`);
        setDialog(null);
      } finally {
        setDialogBusy(false);
      }
    },
    [log]
  );

  const loadRunDetail = useCallback(
    async (runId: string, preferredCaseId?: string) => {
      setDetailBusy(true);
      try {
        const payload = await apiClient.loadAutoRunDetail(runId);
        setRunDetail(payload);
        const nextCaseId =
          (preferredCaseId && payload.cases.some((item) => item.case_id === preferredCaseId) ? preferredCaseId : "") ||
          payload.cases[0]?.case_id ||
          "";
        setSelectedCaseId(nextCaseId);
        if (nextCaseId) {
          await loadCaseDialog(runId, nextCaseId);
        } else {
          setDialog(null);
        }
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        setErrorText(`Детализация прогона: ${message}`);
        log(`Run detail load error for ${runId}: ${message}`);
        setRunDetail(null);
        setDialog(null);
      } finally {
        setDetailBusy(false);
      }
    },
    [loadCaseDialog, log]
  );

  const loadHistory = useCallback(
    async (options?: { keepSelection?: boolean; preferredRunId?: string; preferredCaseId?: string }) => {
      setHistoryBusy(true);
      setErrorText("");
      try {
        const payload = await apiClient.loadAutoRunsHistory({
          from: localInputToIso(filters.fromLocal),
          to: localInputToIso(filters.toLocal),
          target: filters.target,
          mode: filters.mode,
          use_mock: filters.useMock,
          prompt_contains: filters.promptContains.trim() || undefined,
          limit: filters.limit
        });
        setHistory(payload);
        const hasRuns = payload.items.length > 0;
        if (!hasRuns) {
          setSelectedRunId("");
          setSelectedCaseId("");
          setRunDetail(null);
          setDialog(null);
          return;
        }

        const keepSelection = options?.keepSelection ?? true;
        const preferredRunId = options?.preferredRunId ?? "";
        const preferredCaseId = options?.preferredCaseId ?? "";
        const nextRunId =
          keepSelection && preferredRunId && payload.items.some((item) => item.run_id === preferredRunId)
            ? preferredRunId
            : payload.items[0].run_id;
        setSelectedRunId(nextRunId);
        await loadRunDetail(nextRunId, keepSelection ? preferredCaseId : undefined);
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        setErrorText(`История прогонов: ${message}`);
        log(`History load error: ${message}`);
      } finally {
        setHistoryBusy(false);
      }
    },
    [filters.fromLocal, filters.limit, filters.mode, filters.promptContains, filters.target, filters.toLocal, filters.useMock, loadRunDetail, log]
  );

  useEffect(() => {
    void loadHistory({ keepSelection: false });
  }, [loadHistory]);

  const dynamicColumns = useMemo(() => {
    const columns = ["minmax(290px, 340px)", "minmax(300px, 360px)", "minmax(420px, 1fr)"];
    if (showAssistantMode) columns.push("minmax(280px, 320px)");
    if (showDecompositionMode) columns.push("minmax(280px, 320px)");
    if (showProgressMode) columns.push("minmax(280px, 320px)");
    return columns.join(" ");
  }, [showAssistantMode, showDecompositionMode, showProgressMode]);

  return (
    <PanelFrame
      title="История автопрогонов"
      subtitle="Центральный экран диагностики: фильтры, список прогонов, диалог по кейсу, режимы ассистента/декомпозиции и тренд качества."
      actions={
        <div className="assistant-panel-actions">
          <button type="button" className={showAssistantMode ? "tab active" : "tab"} onClick={() => setShowAssistantMode((prev) => !prev)}>
            Режим ассистента
          </button>
          <button
            type="button"
            className={showDecompositionMode ? "tab active" : "tab"}
            onClick={() => setShowDecompositionMode((prev) => !prev)}
          >
            Режим декомпозиции
          </button>
          <button type="button" className={showProgressMode ? "tab active" : "tab"} onClick={() => setShowProgressMode((prev) => !prev)}>
            Прогресс/регресс
          </button>
        </div>
      }
    >
      <div className="autoruns-columns" style={{ gridTemplateColumns: dynamicColumns }}>
        <section className="autoruns-col">
          <h3>Настройки выборки</h3>
          <div className="autoruns-form-grid">
            <label>
              Дата с
              <input
                type="datetime-local"
                value={filters.fromLocal}
                onChange={(event) => setFilters((prev) => ({ ...prev, fromLocal: event.target.value }))}
              />
            </label>
            <label>
              Дата по
              <input type="datetime-local" value={filters.toLocal} onChange={(event) => setFilters((prev) => ({ ...prev, toLocal: event.target.value }))} />
            </label>
            <label>
              Целевой контур
              <select value={filters.target} onChange={(event) => setFilters((prev) => ({ ...prev, target: event.target.value }))}>
                <option value="all">all</option>
                {(history?.available.targets ?? []).map((item) => (
                  <option key={item} value={item}>
                    {item}
                  </option>
                ))}
              </select>
            </label>
            <label>
              Режим
              <select value={filters.mode} onChange={(event) => setFilters((prev) => ({ ...prev, mode: event.target.value }))}>
                <option value="all">all</option>
                {(history?.available.modes ?? []).map((item) => (
                  <option key={item} value={item}>
                    {item}
                  </option>
                ))}
              </select>
            </label>
            <label>
              use_mock
              <select value={filters.useMock} onChange={(event) => setFilters((prev) => ({ ...prev, useMock: event.target.value as UseMockFilter }))}>
                <option value="any">any</option>
                <option value="true">true</option>
                <option value="false">false</option>
              </select>
            </label>
            <label>
              Лимит
              <input
                type="number"
                min={1}
                max={500}
                value={filters.limit}
                onChange={(event) => setFilters((prev) => ({ ...prev, limit: Number(event.target.value || 120) }))}
              />
            </label>
            <label className="full-width">
              Версия промпта содержит
              <input
                value={filters.promptContains}
                onChange={(event) => setFilters((prev) => ({ ...prev, promptContains: event.target.value }))}
                placeholder="normalizer_v2_0_2 / address_query_runtime_v1"
                list="autoruns-prompt-versions"
              />
            </label>
          </div>
          <datalist id="autoruns-prompt-versions">
            {(history?.available.prompt_versions ?? []).map((item) => (
              <option key={item} value={item} />
            ))}
          </datalist>
          <div className="button-row">
            <button type="button" disabled={historyBusy} onClick={() => void loadHistory({ keepSelection: false })}>
              {historyBusy ? "Обновляю..." : "Применить"}
            </button>
            <button
              type="button"
              className="tab"
              onClick={() => {
                setFilters({
                  ...DEFAULT_FILTERS,
                  fromLocal: defaultFromDateValue()
                });
                setErrorText("");
              }}
            >
              Сбросить фильтры
            </button>
          </div>

          <h4>Контур генерации</h4>
          <div className="autoruns-meta-list">
            <div>
              <span>Провайдер:</span>
              <strong>{connection.llmProvider}</strong>
            </div>
            <div>
              <span>Модель:</span>
              <strong>{connection.model || "n/a"}</strong>
            </div>
            <div>
              <span>Prompt assistant:</span>
              <strong>{assistantPromptVersion}</strong>
            </div>
            <div>
              <span>Prompt decomposition:</span>
              <strong>{decompositionPromptVersion}</strong>
            </div>
          </div>

          <details className="autoruns-prompt-details">
            <summary>Дублирование главного промпта (read-only)</summary>
            <label>
              System
              <textarea readOnly value={prompts.systemPrompt} />
            </label>
            <label>
              Developer
              <textarea readOnly value={prompts.developerPrompt} />
            </label>
            <label>
              Domain
              <textarea readOnly value={prompts.domainPrompt} />
            </label>
            <label>
              Schema notes
              <textarea readOnly value={prompts.schemaNotes} />
            </label>
            <label>
              Few-shot
              <textarea readOnly value={prompts.fewShotExamples} />
            </label>
          </details>

          {errorText ? <p className="error-text">{errorText}</p> : null}
        </section>

        <section className="autoruns-col">
          <h3>Выдача прогонов</h3>
          <div className="autoruns-stats-grid">
            <div>
              <span>Всего</span>
              <strong>{history?.stats.runs_total ?? 0}</strong>
            </div>
            <div>
              <span>Средний score</span>
              <strong>{formatScore(history?.stats.avg_score_index ?? null)}</strong>
            </div>
            <div>
              <span>Тренд</span>
              <strong>{history ? trendLabel(history.stats.trend) : "n/a"}</strong>
            </div>
            <div>
              <span>Блокеры</span>
              <strong>{history?.stats.blocking_runs ?? 0}</strong>
            </div>
          </div>
          <div className="autoruns-run-list">
            {(history?.items ?? []).map((run) => (
              <button
                key={run.run_id}
                type="button"
                className={selectedRunId === run.run_id ? "autoruns-run-item selected" : "autoruns-run-item"}
                onClick={() => {
                  setSelectedRunId(run.run_id);
                  void loadRunDetail(run.run_id);
                }}
              >
                <div className="autoruns-run-head">
                  <strong>{formatDateTime(run.run_timestamp)}</strong>
                  <span>{formatShortTarget(run.eval_target)}</span>
                </div>
                <div className="autoruns-run-meta">{run.run_id}</div>
                <div className="autoruns-run-meta">
                  mode={run.mode ?? "n/a"} | mock={String(run.use_mock)}
                </div>
                {run.llm_provider || run.model ? (
                  <div className="autoruns-run-meta">
                    llm={run.llm_provider ?? "n/a"} | model={run.model ?? "n/a"}
                  </div>
                ) : null}
                <div className="autoruns-run-meta">prompt={run.prompt_version ?? "n/a"}</div>
                <div className="autoruns-run-foot">
                  <span>score: {formatScore(run.score_index)}</span>
                  <span>
                    closed/open: {run.closed_cases}/{run.open_cases}
                  </span>
                </div>
                <div className="autoruns-run-foot">
                  <span>blocking: {run.blocking_failures}</span>
                  <span>quality: {run.quality_failures}</span>
                </div>
              </button>
            ))}
            {(history?.items.length ?? 0) === 0 ? <p className="muted">За выбранный диапазон прогонов нет.</p> : null}
          </div>
        </section>

        <section className="autoruns-col">
          <h3>Диалог прогона</h3>
          <div className="autoruns-dialog-toolbar">
            <label>
              Прогон
              <select
                value={selectedRunId}
                onChange={(event) => {
                  const nextRunId = event.target.value;
                  setSelectedRunId(nextRunId);
                  void loadRunDetail(nextRunId);
                }}
              >
                {(history?.items ?? []).map((item) => (
                  <option key={item.run_id} value={item.run_id}>
                    {formatDateTime(item.run_timestamp)} | {item.run_id}
                  </option>
                ))}
              </select>
            </label>
            <label>
              Кейc
              <select
                value={selectedCaseId}
                onChange={(event) => {
                  const nextCaseId = event.target.value;
                  setSelectedCaseId(nextCaseId);
                  if (selectedRunId && nextCaseId) {
                    void loadCaseDialog(selectedRunId, nextCaseId);
                  }
                }}
              >
                {(runDetail?.cases ?? []).map((item) => (
                  <option key={item.case_id} value={item.case_id}>
                    {item.case_id} | {item.status}
                  </option>
                ))}
              </select>
            </label>
          </div>

          <div className="autoruns-case-list">
            {(runDetail?.cases ?? []).map((item) => (
              <button
                key={item.case_id}
                type="button"
                className={selectedCaseId === item.case_id ? "autoruns-case-item selected" : "autoruns-case-item"}
                onClick={() => {
                  setSelectedCaseId(item.case_id);
                  if (selectedRunId) {
                    void loadCaseDialog(selectedRunId, item.case_id);
                  }
                }}
              >
                <span>{item.case_id}</span>
                <span>{item.status}</span>
              </button>
            ))}
          </div>

          <div className="autoruns-dialog-view">
            {dialogBusy || detailBusy ? <p className="muted">Загружаю диалог...</p> : null}
            {!dialogBusy && !detailBusy && (dialog?.messages.length ?? 0) === 0 ? <p className="muted">Диалог для этого кейса не найден.</p> : null}
            {(dialog?.messages ?? []).map((item, index) => {
              const role = item.role === "assistant" ? "assistant" : "user";
              return (
                <article key={`${role}-${index}`} className={`autoruns-msg ${role}`}>
                  <header>
                    <strong>{role === "assistant" ? "Система" : "Модель/вопрос"}</strong>
                    <span>{item.created_at ? formatDateTime(item.created_at) : "n/a"}</span>
                  </header>
                  <p>{item.text}</p>
                  {(item.trace_id || item.reply_type) && (
                    <footer>
                      {item.trace_id ? <span>trace={item.trace_id}</span> : null}
                      {item.reply_type ? <span>reply_type={item.reply_type}</span> : null}
                    </footer>
                  )}
                </article>
              );
            })}
          </div>
        </section>

        {showAssistantMode ? (
          <section className="autoruns-col">
            <h3>Режим ассистента</h3>
            <div className="autoruns-meta-list">
              <div>
                <span>source:</span>
                <strong>{dialog?.source ?? "n/a"}</strong>
              </div>
              <div>
                <span>session:</span>
                <strong>{dialog?.session_id ?? "n/a"}</strong>
              </div>
              <div>
                <span>run target:</span>
                <strong>{activeRunSummary?.eval_target ?? "n/a"}</strong>
              </div>
              <div>
                <span>run score:</span>
                <strong>{formatScore(activeRunSummary?.score_index ?? null)}</strong>
              </div>
            </div>
            <h4>Assistant mode payload</h4>
            <JsonView value={dialog?.assistant_mode ?? { note: "assistant_mode unavailable" }} />
            <h4 style={{ marginTop: 12 }}>Case checks</h4>
            <JsonView value={activeCase?.checks ?? { note: "checks unavailable" }} />
            <h4 style={{ marginTop: 12 }}>Metric subscores</h4>
            <JsonView value={activeCase?.metric_subscores ?? { note: "metric_subscores unavailable" }} />
          </section>
        ) : null}

        {showDecompositionMode ? (
          <section className="autoruns-col">
            <h3>Режим декомпозиции</h3>
            <div className="autoruns-meta-list">
              <div>
                <span>Case:</span>
                <strong>{activeCase?.case_id ?? "n/a"}</strong>
              </div>
              <div>
                <span>Domain:</span>
                <strong>{activeCase?.domain ?? "n/a"}</strong>
              </div>
              <div>
                <span>Query class:</span>
                <strong>{activeCase?.query_class ?? "n/a"}</strong>
              </div>
              <div>
                <span>Trace:</span>
                <strong>{activeCase?.trace_id ?? "n/a"}</strong>
              </div>
            </div>
            <h4>Шаги декомпозиции</h4>
            {(dialog?.decomposition.length ?? 0) > 0 ? (
              <ol className="autoruns-decomposition-list">
                {(dialog?.decomposition ?? []).map((item, index) => (
                  <li key={`${index}-${item.slice(0, 24)}`}>{item}</li>
                ))}
              </ol>
            ) : (
              <p className="muted">В логах кейса нет явной декомпозиции.</p>
            )}
          </section>
        ) : null}

        {showProgressMode ? (
          <section className="autoruns-col">
            <h3>Прогресс / регресс</h3>
            <div className="autoruns-stats-grid">
              <div>
                <span>Latest score</span>
                <strong>{formatScore(history?.stats.latest_score_index ?? null)}</strong>
              </div>
              <div>
                <span>Previous</span>
                <strong>{formatScore(history?.stats.previous_score_index ?? null)}</strong>
              </div>
              <div>
                <span>Trend</span>
                <strong>{history ? trendLabel(history.stats.trend) : "n/a"}</strong>
              </div>
              <div>
                <span>Quality gaps</span>
                <strong>{history?.stats.quality_gap_runs ?? 0}</strong>
              </div>
            </div>
            <h4>Покрытие доменов (история)</h4>
            {renderCoverageRows(history?.stats.domain_coverage ?? [])}
            <h4 style={{ marginTop: 14 }}>Покрытие доменов (выбранный прогон)</h4>
            {renderCoverageRows(runDetail?.coverage.domain_coverage ?? [])}
          </section>
        ) : null}
      </div>
    </PanelFrame>
  );
}
