import { defineStore } from 'pinia';
import { ref } from 'vue';
import type { CreateProjectRequest, CreateProjectResponse, CreateSessionRequest, HideDiscoveredResponse, SessionResponse, SessionSummary, SessionsListResponse } from '@arboretum/shared';
import { api } from '../lib/api';
import { wsClient, type SessionEvent } from '../lib/ws-client';

export const useSessionsStore = defineStore('sessions', () => {
  const sessions = ref<SessionSummary[]>([]);
  const loading = ref(false);
  const loadError = ref<string | null>(null);
  // false (défaut) : les sessions masquées sont exclues ; true : on les inclut (mode « afficher les masquées »).
  const showHidden = ref(false);
  // false (défaut) : les sessions auto-archivées sont exclues ; true : on les inclut (P10). Filtre
  // indépendant et cumulable avec showHidden.
  const showArchived = ref(false);
  let unsubscribe: (() => void) | null = null;

  // vivantes d'abord, puis par date de création décroissante (même ordre que le serveur)
  function sorted(list: SessionSummary[]): SessionSummary[] {
    return [...list].sort((a, b) => Number(b.live) - Number(a.live) || b.createdAt.localeCompare(a.createdAt));
  }

  function remove(id: string): void {
    const idx = sessions.value.findIndex((s) => s.id === id);
    if (idx >= 0) sessions.value.splice(idx, 1);
  }

  function upsert(session: SessionSummary): void {
    // session masquée et mode « afficher les masquées » inactif : on la retire de la liste.
    if (session.hidden && !showHidden.value) {
      remove(session.id);
      return;
    }
    // idem pour les sessions auto-archivées quand « afficher les archivées » est inactif.
    if (session.archived && !showArchived.value) {
      remove(session.id);
      return;
    }
    const idx = sessions.value.findIndex((s) => s.id === session.id);
    if (idx >= 0) sessions.value.splice(idx, 1, session);
    else sessions.value.push(session);
    sessions.value = sorted(sessions.value);
  }

  function onEvent(e: SessionEvent): void {
    if (e.type === 'session_update') {
      upsert(e.session);
      return;
    }
    if (e.type === 'session_archived') {
      // marque la session archivée puis upsert (qui la retire si « afficher les archivées » est off).
      const current = sessions.value.find((s) => s.id === e.sessionId);
      if (current) upsert({ ...current, archived: true });
      return;
    }
    const current = sessions.value.find((s) => s.id === e.sessionId);
    if (!current) return;
    upsert({
      ...current,
      status: 'exited',
      live: false,
      exitCode: e.exitCode,
      endedAt: new Date().toISOString(),
      clients: 0,
    });
  }

  async function fetchSessions(): Promise<void> {
    loading.value = true;
    loadError.value = null;
    try {
      const params = new URLSearchParams();
      if (showHidden.value) params.set('includeHidden', 'true');
      if (showArchived.value) params.set('includeArchived', 'true');
      const qs = params.toString();
      const res = await api.get<SessionsListResponse>(`/api/v1/sessions${qs ? `?${qs}` : ''}`);
      sessions.value = sorted(res.sessions);
    } catch (err) {
      loadError.value = err instanceof Error ? err.message : String(err);
    } finally {
      loading.value = false;
    }
  }

  async function setShowHidden(value: boolean): Promise<void> {
    showHidden.value = value;
    await fetchSessions();
  }

  async function setShowArchived(value: boolean): Promise<void> {
    showArchived.value = value;
    await fetchSessions();
  }

  // Archive une session managée terminée (retrait optimiste si on n'affiche pas les archivées).
  async function archiveSession(id: string): Promise<void> {
    await api.post<{ ok: true }>(`/api/v1/sessions/${id}/archive`);
    if (!showArchived.value) remove(id);
  }

  // Dés-archive une session (un session_update WS la ré-affichera ; mise à jour optimiste locale).
  async function unarchiveSession(id: string): Promise<void> {
    await api.delete<{ ok: true }>(`/api/v1/sessions/${id}/archive`);
    const current = sessions.value.find((s) => s.id === id);
    if (current) upsert({ ...current, archived: false });
  }

  // Masque une session découverte (retrait optimiste si on n'affiche pas les masquées).
  async function hideSession(id: string): Promise<void> {
    await api.post<{ ok: true }>(`/api/v1/sessions/${id}/hide`);
    if (!showHidden.value) remove(id);
  }

  // Ré-affiche une session masquée.
  async function unhideSession(id: string): Promise<void> {
    await api.delete<{ ok: true }>(`/api/v1/sessions/${id}/hide`);
  }

  // Masque tout l'historique de sessions externes visible. Renvoie le nombre masqué.
  async function hideDiscovered(): Promise<number> {
    const res = await api.post<HideDiscoveredResponse>('/api/v1/sessions/hide-discovered');
    if (!showHidden.value) {
      sessions.value = sessions.value.filter((s) => !(s.source === 'discovered' && !s.hidden));
    }
    return res.hidden;
  }

  function startRealtime(): void {
    unsubscribe ??= wsClient.subscribeSessions(onEvent);
  }

  function stopRealtime(): void {
    unsubscribe?.();
    unsubscribe = null;
  }

  async function createSession(cwd: string, command: NonNullable<CreateSessionRequest['command']>): Promise<SessionSummary> {
    const body: CreateSessionRequest = { cwd, command };
    const res = await api.post<SessionResponse>('/api/v1/sessions', body);
    upsert(res.session);
    return res.session;
  }

  // Crée un nouveau dossier de projet `<root>/<name>` côté serveur puis y lance une session.
  async function createProject(req: CreateProjectRequest): Promise<CreateProjectResponse> {
    const res = await api.post<CreateProjectResponse>('/api/v1/projects', req);
    upsert(res.session);
    return res;
  }

  async function killSession(id: string): Promise<void> {
    await api.delete<{ ok: true }>(`/api/v1/sessions/${id}`);
  }

  // Reprise d'une session morte (nouveau PTY managé dans son cwd d'origine, côté serveur).
  async function resumeSession(id: string): Promise<SessionSummary> {
    const res = await api.post<SessionResponse>(`/api/v1/sessions/${id}/resume`);
    upsert(res.session);
    return res.session;
  }

  // Fork d'une session (vivante ou morte) : ne corrompt jamais l'originale.
  async function forkSession(id: string): Promise<SessionSummary> {
    const res = await api.post<SessionResponse>(`/api/v1/sessions/${id}/fork`);
    upsert(res.session);
    return res.session;
  }

  return {
    sessions,
    loading,
    loadError,
    showHidden,
    showArchived,
    fetchSessions,
    setShowHidden,
    setShowArchived,
    startRealtime,
    stopRealtime,
    createSession,
    createProject,
    killSession,
    resumeSession,
    forkSession,
    hideSession,
    unhideSession,
    hideDiscovered,
    archiveSession,
    unarchiveSession,
  };
});
