// #region DashboardDetailModel [C:4] [TYPE Model] [SEMANTICS dashboard,detail,git,task-history,thumbnail,validation,model]
// @ingroup Models
// @BRIEF Screen model for dashboard detail page — loads metadata, thumbnail, task history, validation history, and manages Git status.
// @INVARIANT loadDashboardPage() must only fire when dashboardRef and envId are both truthy.
// @INVARIANT Thumbnail blob URLs are released on reassignment or model destruction.
// @STATE idle      — No context available.
// @STATE loading   — Dashboard detail is loading.
// @STATE loaded    — Dashboard metadata and linked assets rendered.
// @STATE error     — Load failed, error message displayed with retry.
// @ACTION loadDashboardPage()       — Loads all dashboard data in parallel.
// @ACTION loadDashboardDetail()     — Fetches dashboard metadata from API.
// @ACTION loadTaskHistory()         — Fetches recent task history.
// @ACTION loadThumbnail(force?)     — Fetches dashboard thumbnail as blob URL.
// @ACTION loadValidationHistory()   — Fetches LLM validation run history.
// @ACTION runBackupTask()           — Starts backup task for this dashboard.
// @ACTION goBack()                  — Navigates back to dashboard list.
// @ACTION openDataset(id)           — Navigates to dataset detail.
// @RELATION DEPENDS_ON -> [ApiModule]
// @RELATION DEPENDS_ON -> [GitStatusModel]
// @RELATION CALLS -> [CotLogger]

import { goto } from '$app/navigation';
import { ROUTES } from '$lib/routes';
import { api } from '$lib/api.js';
import { fetchApi } from '$lib/api';
import { log } from '$lib/cot-logger';
import { GitStatusModel } from '$lib/models/GitStatusModel.svelte.ts';
import { addToast } from '$lib/toasts.svelte.js';
import { openDrawerForTaskIfPreferred } from '$lib/stores/taskDrawer.svelte.js';
import { getT } from '$lib/i18n/index.svelte.js';
import { SvelteURLSearchParams, SvelteDate } from "svelte/reactivity";

// ── Types ───────────────────────────────────────────────────────

type ScreenState = 'idle' | 'loading' | 'loaded' | 'error';
type ActiveTab = 'resources' | 'git-history' | 'validation-history';

interface DashboardMetadata {
  id: number;
  title: string;
  slug?: string;
  chart_count?: number;
  dataset_count?: number;
  [key: string]: unknown;
}

interface TaskHistoryItem {
  id: string;
  plugin_id?: string;
  status?: string;
  validation_status?: string;
  timestamp?: string;
  started_at?: string;
  finished_at?: string;
  summary?: string;
  [key: string]: unknown;
}

interface ValidationRunItem {
  id: string;
  plugin_id?: string;
  status?: string;
  validation_status?: string;
  timestamp?: string;
  run_timestamp?: string;
  started_at?: string;
  [key: string]: unknown;
}

interface ValidationStatus {
  label: string;
  level: 'fail' | 'warn' | 'pass' | 'unknown' | 'na';
  icon: string;
}

export class DashboardDetailModel {
  // ── Context ───────────────────────────────────────────────────
  dashboardRef: string = $state('');
  envId: string = $state('');

  // ── Dashboard metadata ────────────────────────────────────────
  dashboard: DashboardMetadata | null = $state(null);
  screenState: ScreenState = $state('idle');
  error: string | null = $state(null);

  // ── Task history ─────────────────────────────────────────────
  taskHistory: TaskHistoryItem[] = $state([]);
  isTaskHistoryLoading: boolean = $state(false);
  taskHistoryError: string | null = $state(null);

  // ── Backup ────────────────────────────────────────────────────
  isStartingBackup: boolean = $state(false);

  // ── Thumbnail ────────────────────────────────────────────────
  thumbnailUrl: string = $state('');
  isThumbnailLoading: boolean = $state(false);
  thumbnailError: string | null = $state(null);

  // ── Validation history ───────────────────────────────────────
  validationHistory: ValidationRunItem[] = $state([]);
  isValidationHistoryLoading: boolean = $state(false);
  validationHistoryError: string | null = $state(null);

  // ── Tabs / UI ─────────────────────────────────────────────────
  activeTab: ActiveTab = $state('resources');

  // ── Git ───────────────────────────────────────────────────────
  gitModel: GitStatusModel = new GitStatusModel();
  showGitManager: boolean = $state(false);
  private _wasGitManagerOpen: boolean = $state(false);

  // ── Derived ───────────────────────────────────────────────────
  isLoading = $derived(this.screenState === 'loading');
  gitDashboardRef = $derived(this.dashboard?.slug || this.dashboardRef || '');
  resolvedDashboardId = $derived(this.dashboard?.id ?? (/^\d+$/.test(String(this.dashboardRef || '')) ? Number(this.dashboardRef) : null));

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

  async loadDashboardPage(): Promise<void> {
    await this.loadDashboardDetail();
    this.gitModel.dashboardId = this.gitDashboardRef;
    this.gitModel.envId = this.envId || null;
    const effectiveRef = this.dashboard?.id ?? this.dashboardRef;
    await Promise.all([
      this.loadTaskHistory(effectiveRef),
      this.loadThumbnail(false, effectiveRef),
      this.loadValidationHistory(effectiveRef),
      this.gitModel.loadStatus(),
    ]);
  }

  async loadDashboardDetail(): Promise<void> {
    if (!this.dashboardRef || !this.envId) {
      this.error = getT()?.dashboard?.missing_context;
      this.screenState = 'error';
      return;
    }

    log('DashboardDetailModel', 'REASON', 'Loading dashboard detail', { ref: this.dashboardRef, envId: this.envId });
    this.screenState = 'loading';
    this.error = null;
    try {
      this.dashboard = await api.getDashboardDetail(this.envId, this.dashboardRef) as DashboardMetadata;
      this.screenState = 'loaded';
      log('DashboardDetailModel', 'REFLECT', 'Dashboard detail loaded', { id: this.dashboard?.id, title: this.dashboard?.title });
    } catch (err: unknown) {
      this.error = err instanceof Error ? err.message : String(getT()?.dashboard?.load_detail_failed);
      this.screenState = 'error';
      log('DashboardDetailModel', 'EXPLORE', 'Failed to load dashboard', { ref: this.dashboardRef }, String(this.error));
    }
  }

  async loadTaskHistory(targetRef?: string | number): Promise<void> {
    const ref = targetRef ?? this.dashboardRef;
    if (!ref || !this.envId) return;
    this.isTaskHistoryLoading = true;
    this.taskHistoryError = null;
    try {
      const response = await api.getDashboardTaskHistory(this.envId, String(ref), { limit: 30 }) as { items?: TaskHistoryItem[] };
      this.taskHistory = response?.items || [];
    } catch (err: unknown) {
      this.taskHistoryError = err instanceof Error ? err.message : 'Failed to load task history';
      this.taskHistory = [];
    } finally {
      this.isTaskHistoryLoading = false;
    }
  }

  async loadThumbnail(force = false, targetRef?: string | number): Promise<void> {
    const ref = targetRef ?? this.dashboardRef;
    if (!ref || !this.envId) return;
    this.isThumbnailLoading = true;
    this.thumbnailError = null;
    try {
      const blob = await api.getDashboardThumbnail(this.envId, String(ref), { force }) as Blob;
      this._releaseThumbnail();
      this.thumbnailUrl = URL.createObjectURL(blob);
    } catch (err: unknown) {
      if ((err as { status?: number })?.status === 202) {
        this.thumbnailError = getT()?.dashboard?.thumbnail_generating || 'Thumbnail is being generated';
      } else {
        this.thumbnailError = err instanceof Error ? err.message : String(getT()?.dashboard?.thumbnail_failed);
      }
    } finally {
      this.isThumbnailLoading = false;
    }
  }

  async loadValidationHistory(targetRef?: string | number): Promise<void> {
    const ref = targetRef ?? this.dashboardRef;
    if (!ref || !this.envId) return;
    this.isValidationHistoryLoading = true;
    this.validationHistoryError = null;
    try {
      const qs = new SvelteURLSearchParams();
      qs.append('dashboard_id', String(ref));
      qs.append('environment_id', this.envId);
      qs.append('page_size', '10');
      const response = await fetchApi(`/validation-tasks/runs/all?${qs.toString()}`);
      this.validationHistory = (response as { items?: ValidationRunItem[]; results?: ValidationRunItem[] })?.items
        || (response as { items?: ValidationRunItem[]; results?: ValidationRunItem[] })?.results
        || [];
    } catch (err: unknown) {
      this.validationHistoryError = err instanceof Error ? err.message : 'Failed to load validation history';
      this.validationHistory = [];
    } finally {
      this.isValidationHistoryLoading = false;
    }
  }

  async runBackupTask(): Promise<void> {
    if (this.isStartingBackup || !this.envId || !this.resolvedDashboardId) return;
    this.isStartingBackup = true;
    try {
      const response = await api.postApi('/dashboards/backup', {
        env_id: this.envId,
        dashboard_ids: [this.resolvedDashboardId],
      }) as { task_id?: string };
      const taskId = response?.task_id;
      if (taskId) {
        openDrawerForTaskIfPreferred(taskId);
        addToast(getT()?.dashboard?.backup_started || 'Backup task started', 'success');
      }
      await this.loadTaskHistory();
    } catch (err: unknown) {
      addToast(err instanceof Error ? err.message : (getT()?.dashboard?.backup_task_failed || 'Failed to start backup'), 'error');
    } finally {
      this.isStartingBackup = false;
    }
  }

  async handleSyncAndOpenCommit(): Promise<void> {
    const success = await this.gitModel.syncRepository();
    if (success) this.showGitManager = true;
  }

  trackGitManagerClose(): void {
    if (this.showGitManager) {
      this._wasGitManagerOpen = true;
    } else if (this._wasGitManagerOpen) {
      this._wasGitManagerOpen = false;
      void this.gitModel.loadStatus();
    }
  }

  cleanup(): void {
    this._releaseThumbnail();
  }

  // ── Navigation ────────────────────────────────────────────────

  goBack(): void {
    goto(ROUTES.dashboards.list(this.envId));
  }

  openDataset(datasetId: string | number): void {
    goto(ROUTES.datasets.detail(String(datasetId), this.envId));
  }

  // ── Private ───────────────────────────────────────────────────

  private _releaseThumbnail(): void {
    if (this.thumbnailUrl) {
      URL.revokeObjectURL(this.thumbnailUrl);
      this.thumbnailUrl = '';
    }
  }

  // ── Static utilities ──────────────────────────────────────────

  static toTaskTypeLabel(pluginId: string | undefined): string {
    if (pluginId === 'superset-backup') return getT()?.dashboard?.backup || 'Backup';
    if (pluginId === 'llm_dashboard_validation') return getT()?.dashboard?.llm_check || 'LLM Check';
    return pluginId || '-';
  }

  static getTaskStatusClasses(status: string | undefined): string {
    const n = (status || '').toLowerCase();
    if (n === 'running' || n === 'pending') return 'bg-primary-light text-primary';
    if (n === 'success') return 'bg-success-light text-success';
    if (n === 'failed' || n === 'error') return 'bg-destructive-light text-destructive';
    if (n === 'awaiting_input' || n === 'waiting_input') return 'bg-warning-light text-warning';
    return 'bg-surface-muted text-text';
  }

  static getValidationStatus(task: { plugin_id?: string; validation_status?: string }): ValidationStatus {
    if (task?.plugin_id !== 'llm_dashboard_validation') {
      return { label: '-', level: 'na', icon: '' };
    }
    const raw = String(task?.validation_status || '').toUpperCase();
    if (raw === 'FAIL') return { label: 'FAIL', level: 'fail', icon: '!' };
    if (raw === 'WARN') return { label: 'WARN', level: 'warn', icon: '!' };
    if (raw === 'PASS') return { label: 'PASS', level: 'pass', icon: 'OK' };
    return { label: 'UNKNOWN', level: 'unknown', icon: '?' };
  }

  static getValidationStatusClasses(level: ValidationStatus['level']): string {
    if (level === 'fail') return 'bg-destructive-light text-destructive border-destructive-ring';
    if (level === 'warn') return 'bg-warning-light text-warning border-warning';
    if (level === 'pass') return 'bg-success-light text-success border-success';
    if (level === 'unknown') return 'bg-surface-muted text-text border-border';
    if (level === 'na') return 'bg-surface-page text-text-subtle border-border';
    return 'bg-surface-page text-text-subtle border-border';
  }

  static formatDate(value: unknown): string {
    if (!value) return '-';
    const parsed = new SvelteDate(value as string);
    if (Number.isNaN(parsed.getTime())) return '-';
    return `${parsed.toLocaleDateString()} ${parsed.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;
  }
}
// #endregion DashboardDetailModel
