// frontend/src/lib/models/BranchModel.svelte.ts
// #region BranchModel [C:4] [TYPE Model] [SEMANTICS git,branch,checkout,create,screen-model]
// @BRIEF State model for branch selector — list, checkout, and create Git branches.
// @INVARIANT Loading guards checkout and create operations — only one operation runs at a time.
// @STATE idle         — Branch list loaded, dropdown enabled.
// @STATE loading      — Fetching branch list.
// @STATE createMode   — Inline create form visible.
// @STATE checking_out — Branch checkout in progress.
// @STATE creating     — Branch creation in progress.
// @ACTION loadBranches(dashboardId, envId)   — Fetch branch list from API.
// @ACTION handleCheckout(dashboardId, branchName, envId) — Checkout a branch.
// @ACTION handleCreate(dashboardId, branchName, sourceBranch, envId) — Create a new branch.
// @ACTION handleSelect(event)                 — Handle dropdown selection.
// @ACTION toggleCreateForm()                  — Toggle create form visibility.
// @RELATION DEPENDS_ON -> [EXT:frontend:gitService]
// @RELATION DEPENDS_ON -> [ToastsModule]
// @REJECTED Inline $state in BranchSelector.svelte rejected — model extraction enables L1 tests without DOM render,
//   follows pattern established by GitStatusModel and GitManagerModel.

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

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

interface BranchItem {
  name: string;
  [key: string]: unknown;
}

interface BranchError {
  message: string;
  next_steps: string[];
}

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

export class BranchModel {
  // ── Identity ───────────────────────────────────────────────
  /** Dashboard slug for git API calls. */
  dashboardId: string = $state('');
  /** Environment ID for scoped git operations. */
  envId: string | null = $state(null);
  /** Current active branch name. Updated on checkout. */
  currentBranch: string = $state('main');
  /** Callback invoked when branch changes. */
  onChange: ((event: { branch: string }) => void) | null = $state(null);

  // ── Branch List ────────────────────────────────────────────
  /** Array of branch objects from the API. */
  branches: BranchItem[] = $state([]);
  /** True while fetching branches. */
  loading: boolean = $state(false);

  // ── Create Form ────────────────────────────────────────────
  /** True when the create-branch form is visible. */
  showCreate: boolean = $state(false);
  /** New branch name input value. */
  newBranchName: string = $state('');
  /** Error object { message, next_steps? } from last failed checkout/create. */
  branchError: BranchError | null = $state(null);

  // ── Operation Loading Flags ────────────────────────────────
  /** True while a checkout is in progress. */
  checkingOut: boolean = $state(false);
  /** True while a branch creation is in progress. */
  creating: boolean = $state(false);

  constructor({
    dashboardId = '',
    envId = null,
    currentBranch = 'main',
    onChange = null,
  }: {
    dashboardId?: string;
    envId?: string | null;
    currentBranch?: string;
    onChange?: ((event: { branch: string }) => void) | null;
  } = {}) {
    this.dashboardId = dashboardId;
    this.envId = envId;
    this.currentBranch = currentBranch;
    this.onChange = onChange;
  }

  // ── Branch List ────────────────────────────────────────────

  /**
   * Fetch branch list from the backend.
   * @SIDE_EFFECT GETs /git/repositories/{ref}/branches.
   */
  async loadBranches(dashboardId?: string, envId?: string | null): Promise<void> {
    const ref = dashboardId || this.dashboardId;
    const eid = envId !== undefined ? envId : this.envId;
    if (!ref) return;
    this.loading = true;
    this.branchError = null;
    try {
      this.branches = await gitService.getBranches(ref, eid);
    } catch (e: unknown) {
      const msg = e instanceof Error ? e.message : 'Failed to load branches';
      addToast((tt().git as Record<string, string>)?.load_branches_failed || msg, 'error');
      this.branches = [];
    } finally {
      this.loading = false;
    }
  }

  // ── Select (sync) ──────────────────────────────────────────

  /**
   * Handle dropdown selection — delegates to handleCheckout.
   */
  handleSelect(event: { target?: { value?: string } }): void {
    const branchName = event?.target?.value;
    if (!branchName) return;
    void this.handleCheckout(this.dashboardId, branchName, this.envId);
  }

  // ── Checkout ───────────────────────────────────────────────

  /**
   * Switch repository to the selected branch.
   * @POST currentBranch updated; onChange callback fired on success.
   * @SIDE_EFFECT POSTs to /git/repositories/{ref}/checkout.
   */
  async handleCheckout(dashboardId?: string, branchName?: string, envId?: string | null): Promise<void> {
    const ref = dashboardId || this.dashboardId;
    const eid = envId !== undefined ? envId : this.envId;
    if (!branchName || !ref) return;
    this.branchError = null;
    this.checkingOut = true;
    try {
      await gitService.checkoutBranch(ref, branchName, eid);
      this.currentBranch = branchName;
      if (typeof this.onChange === 'function') {
        this.onChange({ branch: branchName });
      }
      addToast(
        ((tt().git as Record<string, string>)?.switched_to || 'Switched to {branch}').replace('{branch}', branchName),
        'success',
      );
    } catch (e: unknown) {
      const err = e as Record<string, unknown>;
      const detail = typeof err?.detail === 'object' ? err.detail as Record<string, unknown> : {};
      const shortMsg = (detail?.message as string) || (detail?.message_en as string) || (err.message as string) || 'Branch switch failed';
      this.branchError = {
        message: shortMsg,
        next_steps: Array.isArray(detail?.next_steps) ? detail.next_steps as string[] : [],
      };
      addToast(shortMsg, 'warning', 0);
    } finally {
      this.checkingOut = false;
    }
  }

  // ── Create Branch ──────────────────────────────────────────

  /**
   * Create a new branch from the current branch.
   * @POST On success: branch list reloaded, create form closed, input cleared.
   * @SIDE_EFFECT POSTs to /git/repositories/{ref}/branches.
   */
  async handleCreate(dashboardId?: string, branchName?: string, sourceBranch?: string, envId?: string | null): Promise<void> {
    const ref = dashboardId || this.dashboardId;
    const eid = envId !== undefined ? envId : this.envId;
    const name = branchName || this.newBranchName;
    const source = sourceBranch || this.currentBranch;
    if (!name || !ref) return;
    this.branchError = null;
    this.creating = true;
    try {
      await gitService.createBranch(ref, name, source, eid);
      addToast(
        ((tt().git as Record<string, string>)?.created_branch || 'Created branch {branch}').replace('{branch}', name),
        'success',
      );
      this.showCreate = false;
      this.newBranchName = '';
      await this.loadBranches(ref, eid);
    } catch (e: unknown) {
      this.branchError = { message: e instanceof Error ? e.message : 'Branch creation failed', next_steps: [] };
      addToast(e instanceof Error ? e.message : 'Branch creation failed', 'error');
    } finally {
      this.creating = false;
    }
  }

  // ── UI Actions (sync) ──────────────────────────────────────

  /**
   * Toggle create branch form visibility.
   */
  toggleCreateForm(): void {
    this.showCreate = !this.showCreate;
    if (!this.showCreate) {
      this.newBranchName = '';
    }
  }
}
// #endregion BranchModel
