/**
 * `useMemory` — view-model hook for the memory feature (L14).
 *
 * Owns the recall-oriented memory index of one project and the CRUD actions.
 * Consumes {@link MemoryGateway} exclusively; never touches `invoke()` or
 * `@tauri-apps/api`, keeping the component layer testable with mock gateways
 * (ARCHITECTURE §1.3). Note identity is the immutable **slug**.
 */

import { useCallback, useEffect, useState } from "react";

import type { GatewayError, MemoryIndexEntry, MemoryLink, MemoryType } from "@/domain";
import type { CreateMemoryInput } from "@/ports";
import { useGateways } from "@/app/di";

/** What the memory UI needs from this hook. */
export interface MemoryViewModel {
  /** The recall-oriented memory index entries. */
  entries: MemoryIndexEntry[];
  /** Last error message, or `null`. */
  error: string | null;
  /** Whether a request is in flight. */
  busy: boolean;
  /** Reloads the index. */
  refresh: () => Promise<void>;
  /** Creates a new note and refreshes the index. */
  createMemory: (input: Omit<CreateMemoryInput, "projectId">) => Promise<void>;
  /** Updates a note's mutable fields (slug is immutable) and refreshes. */
  updateMemory: (
    slug: string,
    description: string,
    type: MemoryType,
    content: string,
  ) => Promise<void>;
  /** Deletes a note by slug and refreshes. */
  deleteMemory: (slug: string) => Promise<void>;
  /** Resolves a note's `[[wikilinks]]` to existing target slugs. */
  resolveLinks: (slug: string) => Promise<MemoryLink[]>;
}

function describe(e: unknown): string {
  if (e && typeof e === "object" && "message" in e) {
    return String((e as GatewayError).message);
  }
  return String(e);
}

export function useMemory(projectId: string): MemoryViewModel {
  const { memory, system } = useGateways();

  const [entries, setEntries] = useState<MemoryIndexEntry[]>([]);
  const [error, setError] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);

  const refresh = useCallback(async () => {
    if (!projectId) return;
    setBusy(true);
    setError(null);
    try {
      setEntries(await memory.readIndex(projectId));
    } catch (e) {
      setError(describe(e));
    } finally {
      setBusy(false);
    }
  }, [memory, projectId]);

  useEffect(() => {
    void refresh();
  }, [refresh]);

  useEffect(() => {
    if (!system) return;
    let unsubscribe: (() => void) | undefined;
    let cancelled = false;
    void system.onDomainEvent((event) => {
      if (event.type === "memorySaved") {
        void refresh();
      }
    }).then((u) => {
      if (cancelled) u();
      else unsubscribe = u;
    });
    return () => {
      cancelled = true;
      unsubscribe?.();
    };
  }, [refresh, system]);

  const createMemory = useCallback(
    async (input: Omit<CreateMemoryInput, "projectId">) => {
      setBusy(true);
      setError(null);
      try {
        await memory.createMemory({ ...input, projectId });
        await refresh();
      } catch (e) {
        setError(describe(e));
        setBusy(false);
      }
    },
    [memory, projectId, refresh],
  );

  const updateMemory = useCallback(
    async (
      slug: string,
      description: string,
      type: MemoryType,
      content: string,
    ) => {
      setBusy(true);
      setError(null);
      try {
        await memory.updateMemory(projectId, slug, description, type, content);
        await refresh();
      } catch (e) {
        setError(describe(e));
        setBusy(false);
      }
    },
    [memory, projectId, refresh],
  );

  const deleteMemory = useCallback(
    async (slug: string) => {
      setBusy(true);
      setError(null);
      try {
        await memory.deleteMemory(projectId, slug);
        await refresh();
      } catch (e) {
        setError(describe(e));
        setBusy(false);
      }
    },
    [memory, projectId, refresh],
  );

  const resolveLinks = useCallback(
    (slug: string) => memory.resolveLinks(projectId, slug),
    [memory, projectId],
  );

  return {
    entries,
    error,
    busy,
    refresh,
    createMemory,
    updateMemory,
    deleteMemory,
    resolveLinks,
  };
}
