import { defineStore } from 'pinia';
import { ref } from 'vue';
import type {
  CreateGroupRequest,
  CreateGroupSessionRequest,
  GroupResponse,
  GroupSessionResponse,
  GroupsListResponse,
  GroupSummary,
  RepoSummary,
  SessionSummary,
  UpdateGroupRequest,
  WorktreeSummary,
} from '@arboretum/shared';
import { api, ApiError } from '../lib/api';
import { wsClient, type GroupEvent } from '../lib/ws-client';
import { useWorktreesStore } from './worktrees';
import { useSessionsStore } from './sessions';

/** Résultat par repo de la création de worktree (mode feature, tolérance aux échecs partiels). */
export interface CrossRepoResult {
  repoId: string;
  status: 'ok' | 'error';
  message?: string;
  worktreePath?: string;
}

/** Options de lancement d'une session de groupe (P6). */
export interface GroupFeatureOptions {
  command: 'claude' | 'bash';
  /** présent → crée le worktree de cette branche dans chaque repo puis relie ; absent → checkouts principaux. */
  branch?: string;
  newBranch?: boolean;
  baseRef?: string;
}

/** Bilan d'un lancement de session de groupe. */
export interface GroupFeatureOutcome {
  /** résultats de création de worktree par repo ([] en mode checkouts principaux). */
  worktreeResults: CrossRepoResult[];
  session: SessionSummary | null;
  dirs: string[];
  skipped: Array<{ repoId: string; reason: string }>;
}

export const useGroupsStore = defineStore('groups', () => {
  const groups = ref<GroupSummary[]>([]);
  const selectedGroupId = ref<string | null>(null);
  const loading = ref(false);
  const loadError = ref<string | null>(null);
  let unsubscribe: (() => void) | null = null;

  function upsert(group: GroupSummary): void {
    const idx = groups.value.findIndex((g) => g.id === group.id);
    if (idx >= 0) groups.value.splice(idx, 1, group);
    else groups.value.push(group);
  }
  function removeLocal(id: string): void {
    groups.value = groups.value.filter((g) => g.id !== id);
    if (selectedGroupId.value === id) selectedGroupId.value = null;
  }
  function onEvent(e: GroupEvent): void {
    if (e.type === 'group_update') upsert(e.group);
    else removeLocal(e.groupId);
  }

  function byId(id: string): GroupSummary | undefined {
    return groups.value.find((g) => g.id === id);
  }

  // ---- getters dérivés : réutilisent le store worktrees par filtrage repoId (zéro refetch) ----
  function reposInGroup(groupId: string): RepoSummary[] {
    const g = byId(groupId);
    if (!g) return [];
    const wt = useWorktreesStore();
    return g.repoIds
      .map((id) => wt.repos.find((r) => r.id === id))
      .filter((r): r is RepoSummary => r !== undefined);
  }
  function worktreesInGroup(groupId: string): WorktreeSummary[] {
    const g = byId(groupId);
    if (!g) return [];
    const ids = new Set(g.repoIds);
    return useWorktreesStore().worktrees.filter((w) => ids.has(w.repoId));
  }
  function sessionsInGroup(groupId: string): SessionSummary[] {
    // Union { sessions corrélées par cwd aux worktrees du groupe } ∪ { sessions de groupe (groupId) },
    // dé-dupliquée par id : une session de groupe multi-repo ne doit apparaître qu'une fois.
    const byId = new Map<string, SessionSummary>();
    for (const w of worktreesInGroup(groupId)) for (const s of w.sessions) byId.set(s.id, s);
    for (const s of useSessionsStore().sessions) if (s.groupId === groupId) byId.set(s.id, s);
    return [...byId.values()];
  }

  async function fetchGroups(): Promise<void> {
    loading.value = true;
    loadError.value = null;
    try {
      const res = await api.get<GroupsListResponse>('/api/v1/groups');
      groups.value = res.groups;
    } catch (err) {
      loadError.value = err instanceof Error ? err.message : String(err);
    } finally {
      loading.value = false;
    }
  }

  async function createGroup(req: CreateGroupRequest): Promise<GroupSummary> {
    const res = await api.post<GroupResponse>('/api/v1/groups', req);
    upsert(res.group);
    return res.group;
  }
  async function updateGroup(id: string, patch: UpdateGroupRequest): Promise<GroupSummary> {
    const res = await api.patch<GroupResponse>(`/api/v1/groups/${id}`, patch);
    upsert(res.group);
    return res.group;
  }
  async function deleteGroup(id: string): Promise<void> {
    await api.delete<{ ok: true }>(`/api/v1/groups/${id}`);
    removeLocal(id);
  }
  async function addRepoToGroup(groupId: string, repoId: string): Promise<GroupSummary> {
    const res = await api.post<GroupResponse>(`/api/v1/groups/${groupId}/repos`, { repoId });
    upsert(res.group);
    return res.group;
  }
  async function removeRepoFromGroup(groupId: string, repoId: string): Promise<GroupSummary> {
    const res = await api.delete<GroupResponse>(`/api/v1/groups/${groupId}/repos/${repoId}`);
    upsert(res.group);
    return res.group;
  }

  /** Lance UNE session Claude couvrant tous les repos du groupe (--add-dir côté serveur). */
  async function createGroupSession(groupId: string, req: CreateGroupSessionRequest): Promise<GroupSessionResponse> {
    // La session est upsertée dans le store sessions par le broadcast WS `session_update`.
    return api.post<GroupSessionResponse>(`/api/v1/groups/${groupId}/session`, req);
  }

  /**
   * Lance une session de groupe reliant tous les repos (P6). En mode « feature » (branch fournie),
   * crée d'abord le même worktree de branche dans chaque repo — orchestration côté client tolérante
   * aux échecs partiels, feedback par repo — PUIS une seule session couvrant ces worktrees. En mode
   * « checkouts principaux » (sans branch), la session couvre directement le worktree principal de chaque repo.
   */
  async function createGroupFeature(
    groupId: string,
    repoIds: string[],
    opts: GroupFeatureOptions,
    onProgress?: (result: CrossRepoResult) => void,
  ): Promise<GroupFeatureOutcome> {
    const wt = useWorktreesStore();
    let worktreeResults: CrossRepoResult[] = [];
    if (opts.branch) {
      const branch = opts.branch;
      worktreeResults = await Promise.all(
        repoIds.map(async (repoId): Promise<CrossRepoResult> => {
          try {
            const res = await wt.createWorktree(repoId, {
              branch,
              newBranch: opts.newBranch ?? true,
              ...(opts.baseRef ? { baseRef: opts.baseRef } : {}),
              startSession: null,
            });
            const result: CrossRepoResult = { repoId, status: 'ok', worktreePath: res.worktree.path };
            onProgress?.(result);
            return result;
          } catch (err) {
            const result: CrossRepoResult = {
              repoId,
              status: 'error',
              message: err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err),
            };
            onProgress?.(result);
            return result;
          }
        }),
      );
    }
    const res = await createGroupSession(groupId, {
      command: opts.command,
      ...(opts.branch ? { branch: opts.branch } : {}),
    });
    return { worktreeResults, session: res.session, dirs: res.dirs, skipped: res.skipped };
  }

  function startRealtime(): void {
    unsubscribe ??= wsClient.subscribeGroups(onEvent);
  }
  function stopRealtime(): void {
    unsubscribe?.();
    unsubscribe = null;
  }

  return {
    groups,
    selectedGroupId,
    loading,
    loadError,
    byId,
    reposInGroup,
    worktreesInGroup,
    sessionsInGroup,
    fetchGroups,
    createGroup,
    updateGroup,
    deleteGroup,
    addRepoToGroup,
    removeRepoFromGroup,
    createGroupSession,
    createGroupFeature,
    startRealtime,
    stopRealtime,
  };
});
