// #region TranslateHistoryModel [C:4] [TYPE Model] [SEMANTICS translate,history,run,list,pagination,model]
// @ingroup Models
// @BRIEF Screen model for translation run history — manages paginated run list, filters, detail panel, metrics, and run actions.
// @INVARIANT Changing filter resets pagination to page 1.
// @INVARIANT Detail panel opens only when selectedRun is set; closing clears selectedRunDetail.
// @ACTION loadRuns() / loadMetrics() / loadJobs() — Fetch data.
// @ACTION openDetail(run) / closeDetail() — Detail panel management.
// @ACTION applyFilters() — Reset pagination + reload.
// @ACTION handleCancelRun/Retry/Download — Run-level actions.
// @RELATION DEPENDS_ON -> [TranslateApi]

import { addToast } from '$lib/toasts.svelte.js';
import { _, getT } from '$lib/i18n/index.svelte.js';
import { fetchAllRuns, fetchRunDetail, fetchAllMetrics, fetchJobs, downloadSkippedCsv, downloadFailedCsv, cancelRun, retryFailedBatches } from '$lib/api/translate.js';

type UxState = 'idle' | 'loading' | 'empty' | 'populated' | 'detail_open' | 'pruned';

export class TranslateHistoryModel {
  // ── List ──────────────────────────────────────────────────────
  uxState: UxState = $state('idle');
  isLoading: boolean = $state(false);
  runs: Record<string, unknown>[] = $state([]);
  total: number = $state(0);
  currentPage: number = $state(1);
  pageSize: number = $state(20);

  // ── Filters ───────────────────────────────────────────────────
  filterJobId: string = $state('');
  filterStatus: string = $state('');
  filterTrigger: string = $state('');
  filterDateFrom: string = $state('');
  filterDateTo: string = $state('');

  // ── Detail ────────────────────────────────────────────────────
  selectedRun: Record<string, unknown> | null = $state(null);
  selectedRunDetail: Record<string, unknown> | null = $state(null);

  // ── Metrics ───────────────────────────────────────────────────
  metrics: Record<string, unknown>[] = $state([]);
  jobs: Record<string, unknown>[] = $state([]);

  // ── Action spinners ───────────────────────────────────────────
  cancellingRunId: string | null = $state(null);
  retryingRunId: string | null = $state(null);

  // ── Derived ───────────────────────────────────────────────────
  totalMetric = $derived(this._calcTotalMetric());

  // ── Actions ───────────────────────────────────────────────────

  async loadRuns(): Promise<void> {
    this.isLoading = true;
    this.uxState = 'loading';
    try {
      const result = await fetchAllRuns({ page: this.currentPage, page_size: this.pageSize, job_id: this.filterJobId || undefined, status: this.filterStatus || undefined, trigger_type: this.filterTrigger || undefined });
      this.runs = (result as { items?: Record<string, unknown>[] })?.items || [];
      this.total = (result as { total?: number })?.total || 0;
      this.uxState = this.runs.length === 0 ? 'empty' : 'populated';
    } catch (err: unknown) {
      addToast(err instanceof Error ? err.message : _('translate.history.load_failed'), 'error');
      this.uxState = 'empty';
    } finally {
      this.isLoading = false;
    }
  }

  async loadMetrics(): Promise<void> {
    try { this.metrics = await fetchAllMetrics(); } catch { /* non-critical */ }
  }

  async loadJobs(): Promise<void> {
    try {
      const result = await fetchJobs({ page_size: 100 });
      this.jobs = Array.isArray(result) ? result : ((result as { items?: unknown[]; results?: unknown[] })?.items || (result as { items?: unknown[]; results?: unknown[] })?.results || []) as Record<string, unknown>[];
    } catch { /* non-critical */ }
  }

  async openDetail(run: Record<string, unknown>): Promise<void> {
    this.selectedRun = run;
    this.selectedRunDetail = null;
    try {
      this.selectedRunDetail = await fetchRunDetail(run.id as string);
      this.uxState = 'detail_open';
    } catch (err: unknown) {
      addToast(err instanceof Error ? err.message : _('translate.history.load_detail_failed'), 'error');
    }
  }

  closeDetail(): void {
    this.selectedRun = null;
    this.selectedRunDetail = null;
    this.uxState = this.runs.length === 0 ? 'empty' : 'populated';
  }

  applyFilters(): void {
    this.currentPage = 1;
    this.loadRuns();
  }

  // ── Run actions ───────────────────────────────────────────────

  async handleCancelRun(runId: string): Promise<void> {
    this.cancellingRunId = runId;
    try {
      await cancelRun(runId);
      addToast(getT()?.translate?.run?.run_cancelled || 'Run cancelled', 'success');
      if (this.selectedRunDetail?.id === runId) this.selectedRunDetail = await fetchRunDetail(runId);
      this.loadRuns();
    } catch (err: unknown) {
      addToast(err instanceof Error ? err.message : (getT()?.translate?.run?.cancel_failed || 'Failed to cancel run'), 'error');
    } finally { this.cancellingRunId = null; }
  }

  async handleRetryRun(runId: string): Promise<void> {
    this.retryingRunId = runId;
    try {
      await retryFailedBatches(runId);
      addToast(getT()?.translate?.run?.retry_success || 'Retry started', 'success');
      if (this.selectedRunDetail?.id === runId) this.selectedRunDetail = await fetchRunDetail(runId);
      this.loadRuns();
    } catch (err: unknown) {
      addToast(err instanceof Error ? err.message : (getT()?.translate?.run?.retry_failed_msg || 'Retry failed'), 'error');
    } finally { this.retryingRunId = null; }
  }

  async handleDownloadSkipped(runId: string): Promise<void> {
    try { await downloadSkippedCsv(runId); } catch (err: unknown) { addToast(err instanceof Error ? err.message : _('translate.history.download_failed'), 'error'); }
  }

  async handleDownloadFailed(runId: string): Promise<void> {
    try { await downloadFailedCsv(runId); } catch (err: unknown) { addToast(err instanceof Error ? err.message : _('translate.history.download_failed'), 'error'); }
  }

  // ── Utilities ─────────────────────────────────────────────────

  getStatusClass(status: string | undefined): string {
    const map: Record<string, string> = {
      PENDING: 'bg-warning-light text-warning', RUNNING: 'bg-primary-light text-primary',
      COMPLETED: 'bg-success-light text-success', FAILED: 'bg-destructive-light text-destructive',
      CANCELLED: 'bg-surface-muted text-text-muted',
    };
    return map[status ?? ''] || 'bg-surface-muted text-text';
  }

  getJobName(jobId: string | undefined): string {
    const job = this.jobs.find((j: Record<string, unknown>) => j.id === jobId);
    return job ? String(job.name) : (jobId?.substring(0, 8) ?? '') + '...';
  }

  private _calcTotalMetric() {
    if (!this.metrics.length) return null;
    return {
      total_runs: this.metrics.reduce((s: number, m: Record<string, unknown>) => s + ((m.total_runs as number) || 0), 0),
      successful_runs: this.metrics.reduce((s: number, m: Record<string, unknown>) => s + ((m.successful_runs as number) || 0), 0),
      failed_runs: this.metrics.reduce((s: number, m: Record<string, unknown>) => s + ((m.failed_runs as number) || 0), 0),
      total_records: this.metrics.reduce((s: number, m: Record<string, unknown>) => s + ((m.total_records as number) || 0), 0),
    };
  }
}
// #endregion TranslateHistoryModel
