import { defineStore } from 'pinia';
import { ref } from 'vue';
import type {
  CreateGroupRequest,
  CreateWorktreeRequest,
  GroupResponse,
  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';

/** Requête « feature cross-repo » : un worktree (et éventuellement une session) par repo. */
export type CrossRepoRequest = Omit<CreateWorktreeRequest, 'path'>;

/** Résultat par repo d'une action cross-repo (tolérance aux échecs partiels). */
export interface CrossRepoResult {
  repoId: string;
  status: 'ok' | 'error';
  message?: string;
  worktreePath?: string;
  sessionId?: 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[] {
    return worktreesInGroup(groupId).flatMap((w) => w.sessions);
  }

  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;
  }

  /**
   * Crée le même worktree (et éventuellement une session) dans chaque repo, en une action.
   * Orchestration côté client : les worktrees sont indépendants entre repos, donc on tolère
   * les échecs partiels (un repo en échec n'empêche pas les autres). Feedback par repo.
   */
  async function createCrossRepoFeature(
    repoIds: string[],
    req: CrossRepoRequest,
    onProgress?: (result: CrossRepoResult) => void,
  ): Promise<CrossRepoResult[]> {
    const wt = useWorktreesStore();
    return Promise.all(
      repoIds.map(async (repoId): Promise<CrossRepoResult> => {
        try {
          const res = await wt.createWorktree(repoId, req);
          const result: CrossRepoResult = {
            repoId,
            status: 'ok',
            worktreePath: res.worktree.path,
            ...(res.session ? { sessionId: res.session.id } : {}),
          };
          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;
        }
      }),
    );
  }

  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,
    createCrossRepoFeature,
    startRealtime,
    stopRealtime,
  };
});
