// frontend/src/lib/models/GitConfigModel.svelte.ts
// #region GitConfigModel [C:4] [TYPE Model] [SEMANTICS git,config,settings,screen-model]
// @BRIEF State model for Git configuration management — CRUD operations on git server configs and Gitea repository management.
// @INVARIANT Config list is loaded before edit/create operations are attempted (loadConfigs() on mount).
// @INVARIANT Loading states are mutually exclusive per operation type (configsLoading, saving, testingConnection, deleting, giteaReposLoading, giteaCreating, giteaDeleting).
// @STATE idle         — Config list loaded, no operation in progress.
// @STATE loading      — Fetching configs from API.
// @STATE saving       — Saving config (create or update).
// @STATE testing      — Testing connection to git server.
// @STATE deleting     — Deleting a git config.
// @STATE gitea_loading   — Loading Gitea repositories.
// @STATE gitea_creating  — Creating Gitea repository.
// @STATE gitea_deleting  — Deleting Gitea repository.
// @ACTION loadConfigs()      — GET /git/config
// @ACTION selectConfig(id)   — Set selected config ID
// @ACTION startCreate()      — Reset form for new config creation
// @ACTION startEdit(id)      — Populate form from existing config
// @ACTION cancelEdit()       — Reset form and editing state
// @ACTION saveConfig()       — POST or PUT config based on isEditing
// @ACTION deleteConfig(id)   — DELETE /git/config/{id}
// @ACTION testConnection()   — POST /git/config/test
// @ACTION loadGiteaRepos()   — GET Gitea repositories for selected config
// @ACTION createRemoteRepo() — POST Gitea repository
// @ACTION deleteRemoteRepo(repo) — DELETE Gitea repository
// @RELATION DEPENDS_ON -> [EXT:frontend:gitService]
// @RELATION DEPENDS_ON -> [ToastsModule]
// @REJECTED Inline $state + separate functions in GitDashboardPage rejected — duplicated API logic, hard to test, violated DRY model pattern established by GitManagerModel and MigrationSettingsModel.
// @REJECTED getState/setState proxy pattern rejected — fragile, untyped, hard to test. Real $state atoms with class methods enable L1 testing without DOM render.

import { gitService } from '../../services/gitService.js';
import { addToast } from '$lib/toasts.svelte.js';
import { getT } from '$lib/i18n/index.svelte.js';

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

interface GitConfigItem {
  id: string | number;
  name?: string;
  provider?: string;
  url?: string;
  pat?: string;
  status?: string;
  default_repository?: string;
  default_branch?: string;
  [key: string]: unknown;
}

interface GitConfigFormData {
  name: string;
  provider: string;
  url: string;
  pat: string;
  default_repository: string;
  default_branch: string;
}

interface GiteaRepoForm {
  name: string;
  private: boolean;
  description: string;
  auto_init: boolean;
  default_branch: string;
}

interface GiteaRepo {
  name?: string;
  full_name?: string;
  [key: string]: unknown;
}

interface ConnectionTestPayload {
  name: string;
  provider: string;
  url: string;
  pat: string;
  default_repository: string;
  default_branch: string;
  config_id: string | number | null;
}

interface ConnectionTestResult {
  status: string;
  message?: string;
  [key: string]: unknown;
}

/** @returns Current i18n translations. */
function tt(): Record<string, unknown> {
  return getT();
}

export class GitConfigModel {
  // ── Config list ──────────────────────────────────────────────
  /** List of Git server configurations. */
  configs: GitConfigItem[] = $state([]);
  /** True while fetching configs. */
  configsLoading: boolean = $state(false);
  /** Error message from last failed config load. */
  configsError: string = $state('');

  // ── Selection / editing ─────────────────────────────────────
  /** Currently selected config ID (used for Gitea context). */
  selectedConfigId: string = $state('');
  /** True when editing an existing config (vs creating new). */
  isEditing: boolean = $state(false);
  /** ID of the config being edited (null when creating). */
  editingConfigId: string | number | null = $state(null);

  // ── Form data ────────────────────────────────────────────────
  /** Form fields for adding/editing a git config. */
  formData: GitConfigFormData = $state({
    name: '',
    provider: 'GITHUB',
    url: 'https://github.com',
    pat: '',
    default_repository: '',
    default_branch: 'main',
  });

  // ── Connection testing ───────────────────────────────────────
  /** True while testing a connection. */
  testingConnection: boolean = $state(false);
  /** Result from last connection test (null = not tested). */
  connectionTestResult: ConnectionTestResult | null = $state(null);

  // ── Saving ───────────────────────────────────────────────────
  /** True while saving a config (create or update). */
  saving: boolean = $state(false);
  /** Error message from last failed save. */
  saveError: string = $state('');
  /** Success message from last successful save. */
  saveSuccess: string = $state('');

  // ── Deleting ─────────────────────────────────────────────────
  /** True while deleting a config. */
  deleting: boolean = $state(false);

  // ── Gitea repos ─────────────────────────────────────────────
  /** List of Gitea repositories for the selected config. */
  giteaRepos: GiteaRepo[] = $state([]);
  /** True while loading Gitea repos. */
  giteaReposLoading: boolean = $state(false);
  /** True while creating a Gitea repo. */
  giteaCreating: boolean = $state(false);
  /** True while deleting a Gitea repo. */
  giteaDeleting: boolean = $state(false);
  /** Selected config ID for Gitea operations. */
  selectedGiteaConfigId: string = $state('');

  /** Form fields for creating a new Gitea repository. */
  newGiteaRepo: GiteaRepoForm = $state({
    name: '',
    private: true,
    description: '',
    auto_init: true,
    default_branch: 'main',
  });

  // ── Helpers ──────────────────────────────────────────────────

  /** @private Convenient access to i18n store value. */
  get _t(): Record<string, unknown> {
    return tt();
  }

  // ── Actions: Config CRUD ─────────────────────────────────────

  /**
   * Fetch all Git server configurations.
   * @POST configs is populated from API; configsLoading cycles true→false.
   */
  async loadConfigs(): Promise<void> {
    this.configsLoading = true;
    this.configsError = '';
    try {
      this.configs = await gitService.getConfigs();
    } catch (e: unknown) {
      this.configsError = e instanceof Error ? e.message : 'Failed to load git configs';
      addToast(e instanceof Error ? e.message : 'Failed to load git configs', 'error');
    } finally {
      this.configsLoading = false;
    }
  }

  /**
   * Select a config by ID (sync, no API call).
   * @POST selectedConfigId is set.
   */
  selectConfig(id: string): void {
    this.selectedConfigId = id;
  }

  /**
   * Reset form for creating a new config.
   * @POST isEditing=false, editingConfigId=null, formData reset to defaults.
   */
  startCreate(): void {
    this.isEditing = false;
    this.editingConfigId = null;
    this.formData = {
      name: '',
      provider: 'GITHUB',
      url: 'https://github.com',
      pat: '',
      default_repository: '',
      default_branch: 'main',
    };
    this.saveError = '';
    this.saveSuccess = '';
  }

  /**
   * Populate form from an existing config for editing.
   * @POST isEditing=true, editingConfigId set, formData populated.
   */
  startEdit(id: string | number): void {
    const config = this.configs.find((c: GitConfigItem) => c.id === id);
    if (!config) return;
    this.isEditing = true;
    this.editingConfigId = config.id as string | number;
    this.formData = {
      name: config.name || '',
      provider: config.provider || 'GITHUB',
      url: config.url || 'https://github.com',
      pat: config.pat || '',
      default_repository: config.default_repository || '',
      default_branch: config.default_branch || 'main',
    };
    this.saveError = '';
    this.saveSuccess = '';
  }

  /**
   * Cancel editing / reset form to initial state.
   * @POST Form and editing state fully reset (same as startCreate).
   */
  cancelEdit(): void {
    this.startCreate();
  }

  /**
   * Save config — creates new or updates existing based on isEditing.
   * @POST On success: configs updated, form reset, success toast.
   *       On failure: saveError set, error toast.
   */
  async saveConfig(): Promise<void> {
    this.saving = true;
    this.saveError = '';
    this.saveSuccess = '';
    try {
      if (this.isEditing) {
        const updated = await gitService.updateConfig(this.editingConfigId, this.formData);
        this.configs = this.configs.map((c: GitConfigItem) => (c.id === this.editingConfigId ? updated : c));
        this.saveSuccess = (this._t?.settings as Record<string, unknown>)?.git_config_updated as string || 'Git configuration updated';
        addToast(this.saveSuccess, 'success');
      } else {
        const saved = await gitService.createConfig(this.formData);
        this.configs = [...this.configs, saved];
        this.saveSuccess = (this._t?.settings as Record<string, unknown>)?.git_config_saved as string || 'Git configuration saved';
        addToast(this.saveSuccess, 'success');
      }
      this.cancelEdit();
    } catch (e: unknown) {
      this.saveError = e instanceof Error ? e.message : 'Failed to save git config';
      addToast(e instanceof Error ? e.message : 'Failed to save git config', 'error');
    } finally {
      this.saving = false;
    }
  }

  /**
   * Delete a git config by ID.
   * @SIDE_EFFECT DELETE API call; removes config from local list.
   */
  async deleteConfig(id: string | number): Promise<void> {
    this.deleting = true;
    try {
      await gitService.deleteConfig(id);
      this.configs = this.configs.filter((c: GitConfigItem) => c.id !== id);
      addToast((this._t?.settings as Record<string, unknown>)?.git_config_deleted as string || 'Git configuration deleted', 'success');
      if (this.selectedGiteaConfigId === id) {
        this.selectedGiteaConfigId = '';
        this.giteaRepos = [];
      }
    } catch (e: unknown) {
      addToast(e instanceof Error ? e.message : 'Failed to delete git config', 'error');
    } finally {
      this.deleting = false;
    }
  }

  /**
   * Test connection to the git server with current formData.
   * @POST connectionTestResult set; success/error toast shown.
   */
  async testConnection(): Promise<void> {
    this.testingConnection = true;
    this.connectionTestResult = null;
    try {
      const testPayload: ConnectionTestPayload = {
        ...this.formData,
        config_id: null,
      };
      if (this.isEditing && this.editingConfigId) {
        testPayload.config_id = this.editingConfigId;
      }
      const result: ConnectionTestResult = await gitService.testConnection(testPayload);
      this.connectionTestResult = result;
      if (result.status === 'success') {
        addToast((this._t?.settings as Record<string, unknown>)?.connection_success as string || 'Connection successful', 'success');
      } else {
        addToast(
          result.message || (this._t?.settings as Record<string, unknown>)?.connection_failed_short as string || 'Connection failed',
          'error',
        );
      }
    } catch (e: unknown) {
      addToast((this._t?.settings as Record<string, unknown>)?.connection_failed_short as string || 'Connection failed', 'error');
    } finally {
      this.testingConnection = false;
    }
  }

  // ── Actions: Gitea repositories ──────────────────────────────

  /**
   * Load Gitea repositories for the selected config.
   * @POST giteaRepos populated from API.
   */
  async loadGiteaRepos(): Promise<void> {
    if (!this.selectedGiteaConfigId) {
      this.giteaRepos = [];
      return;
    }
    this.giteaReposLoading = true;
    try {
      this.giteaRepos = await gitService.listGiteaRepositories(this.selectedGiteaConfigId);
    } catch (e: unknown) {
      addToast(e instanceof Error ? e.message : 'Failed to load Gitea repos', 'error');
      this.giteaRepos = [];
    } finally {
      this.giteaReposLoading = false;
    }
  }

  /**
   * Create a new repository on the selected Gitea server.
   * @PRE selectedGiteaConfigId is set; newGiteaRepo.name is non-empty.
   * @POST Repository created; form reset; repos list reloaded.
   */
  async createRemoteRepo(): Promise<void> {
    if (!this.selectedGiteaConfigId || !this.newGiteaRepo.name?.trim()) return;
    this.giteaCreating = true;
    try {
      await gitService.createGiteaRepository(this.selectedGiteaConfigId, {
        ...this.newGiteaRepo,
        name: this.newGiteaRepo.name.trim(),
      } satisfies GiteaRepoForm);
      addToast('Gitea repository created', 'success');
      this.newGiteaRepo = {
        name: '',
        private: true,
        description: '',
        auto_init: true,
        default_branch: 'main',
      };
      await this.loadGiteaRepos();
    } catch (e: unknown) {
      addToast(e instanceof Error ? e.message : 'Failed to create Gitea repo', 'error');
    } finally {
      this.giteaCreating = false;
    }
  }

  /**
   * Delete a repository on the selected Gitea server.
   * @POST Repository deleted on Gitea; repos list reloaded.
   */
  async deleteRemoteRepo(repo: GiteaRepo): Promise<void> {
    if (!this.selectedGiteaConfigId) return;
    const fullName = String(repo.full_name || '');
    const parts = fullName.split('/');
    const owner = parts.length > 1 ? parts[0] : '';
    const repoName = parts.length > 1 ? parts[1] : repo.name;
    if (!owner || !repoName) {
      addToast('Cannot resolve repository owner/name', 'error');
      return;
    }
    this.giteaDeleting = true;
    try {
      await gitService.deleteGiteaRepository(this.selectedGiteaConfigId, owner, repoName);
      addToast('Gitea repository deleted', 'success');
      await this.loadGiteaRepos();
    } catch (e: unknown) {
      addToast(e instanceof Error ? e.message : 'Failed to delete Gitea repo', 'error');
    } finally {
      this.giteaDeleting = false;
    }
  }
}
// #endregion GitConfigModel
