import { createStore, produce } from "solid-js/store";
import { invoke } from "../invoke";
import { appLogger } from "./appLogger";

/** A single note */
export interface Note {
	id: string;
	text: string;
	createdAt: number;
	repoPath: string | null;
	repoDisplayName: string | null;
	usedAt: number | null;
	images: string[];
}

/** Notes store state */
interface NotesStoreState {
	notes: Note[];
}

/** Generate a unique note ID */
export function generateId(): string {
	return `note-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}

/** Persist notes to Rust backend (fire-and-forget) */
function saveNotes(notes: Note[]): void {
	invoke("save_notes", { config: { notes } }).catch((err) => appLogger.error("store", "Failed to save notes", err));
}

/** Create the notes store */
function createNotesStore() {
	const [state, setState] = createStore<NotesStoreState>({
		notes: [],
	});

	const actions = {
		/** Load notes from Rust backend */
		async hydrate(): Promise<void> {
			try {
				const loaded = await invoke<{ notes?: Note[] }>("load_notes");
				if (loaded?.notes && Array.isArray(loaded.notes)) {
					const migrated = loaded.notes.map((n) => ({
						...n,
						repoPath: n.repoPath ?? null,
						repoDisplayName: n.repoDisplayName ?? null,
						usedAt: n.usedAt ?? null,
						images: (n as Note & { images?: string[] }).images ?? [],
					}));
					setState("notes", migrated);
				}
			} catch (err) {
				appLogger.debug("store", "Failed to hydrate notes", err);
			}
		},

		/** Add a new note, optionally tagged with a repo and images */
		addNote(
			text: string,
			repoPath?: string | null,
			repoDisplayName?: string | null,
			images?: string[],
			noteId?: string,
		): void {
			const trimmed = text.trim();
			const imgs = images ?? [];
			if (!trimmed && imgs.length === 0) return;

			const note: Note = {
				id: noteId ?? generateId(),
				text: trimmed,
				createdAt: Date.now(),
				repoPath: repoPath ?? null,
				repoDisplayName: repoDisplayName ?? null,
				usedAt: null,
				images: imgs,
			};

			setState(
				produce((s) => {
					s.notes.unshift(note);
				}),
			);
			saveNotes(state.notes);
		},

		/** Remove a note by ID, cleaning up image assets on disk */
		removeNote(id: string): void {
			setState("notes", (notes) => notes.filter((n) => n.id !== id));
			saveNotes(state.notes);
			invoke("delete_note_assets", { noteId: id }).catch((err) =>
				appLogger.debug("store", "Failed to delete note assets", err),
			);
		},

		/** Update a note in-place, preserving id, createdAt, and repo assignment */
		updateNote(id: string, text: string, images: string[]): void {
			setState(
				produce((s) => {
					const note = s.notes.find((n) => n.id === id);
					if (note) {
						note.text = text.trim();
						note.images = images;
					}
				}),
			);
			saveNotes(state.notes);
		},

		/** Reassign a note to a different project */
		reassignNote(id: string, repoPath: string | null, repoDisplayName: string | null): void {
			setState(
				produce((s) => {
					const note = s.notes.find((n) => n.id === id);
					if (note) {
						note.repoPath = repoPath;
						note.repoDisplayName = repoDisplayName;
					}
				}),
			);
			saveNotes(state.notes);
		},

		/** Mark a note as used (sent to terminal) */
		markUsed(id: string): void {
			setState(
				produce((s) => {
					const note = s.notes.find((n) => n.id === id);
					if (note) {
						note.usedAt = Date.now();
					}
				}),
			);
			saveNotes(state.notes);
		},

		/** Get notes filtered by active repo. null = all notes. */
		getFilteredNotes(activeRepo: string | null): Note[] {
			if (!activeRepo) return state.notes;
			return state.notes.filter((n) => n.repoPath === null || n.repoPath === activeRepo);
		},

		/** Count of notes visible for the given repo filter */
		filteredCount(activeRepo: string | null): number {
			if (!activeRepo) return state.notes.length;
			return state.notes.filter((n) => n.repoPath === null || n.repoPath === activeRepo).length;
		},

		/** Count of pending (not yet used) notes for the given repo filter */
		pendingCount(activeRepo: string | null): number {
			return state.notes.filter((n) => !n.usedAt && (!activeRepo || n.repoPath === null || n.repoPath === activeRepo))
				.length;
		},

		/** Remove all notes that have been used (usedAt !== null) */
		clearCompleted(): void {
			const completed = state.notes.filter((n) => n.usedAt !== null);
			if (completed.length === 0) return;
			const completedIds = completed.map((n) => n.id);
			setState("notes", (notes) => notes.filter((n) => !completedIds.includes(n.id)));
			saveNotes(state.notes);
			invoke("delete_note_assets_batch", { noteIds: completedIds }).catch((err) =>
				appLogger.debug("store", "Failed to delete note assets", err),
			);
		},

		/** Get total note count */
		count(): number {
			return state.notes.length;
		},
	};

	return { state, ...actions };
}

export const notesStore = createNotesStore();

// Debug registry — expose notes for MCP introspection
import { registerDebugSnapshot } from "./debugRegistry";

registerDebugSnapshot("notes", () => {
	return notesStore.state.notes.map((n) => ({
		id: n.id,
		text: n.text.substring(0, 100),
		repoPath: n.repoPath,
		createdAt: n.createdAt,
	}));
});
