// ==========================================
// NotesPane Kit — useNotes Hook
// ==========================================
// Core state machine: views, CRUD, pagination, search,
// folders, sort, selection, backlinks, daily notes,
// wikilink navigation, pinning, quick switcher.
// Independently importable.
//
// Usage:
//   const notes = useNotes({ onNoteChange });
//   notes.openNote(id);

const { useState, useRef, useCallback, useEffect } = window.hooks;

const PAGE_SIZE = 50;
const PINS_KEY = 'sw_notes_pinned';

function _loadPins() {
    try {
        const raw = localStorage.getItem(PINS_KEY);
        return raw ? new Set(JSON.parse(raw)) : new Set();
    } catch { return new Set(); }
}

function _savePins(set) {
    try { localStorage.setItem(PINS_KEY, JSON.stringify([...set])); } catch {}
}

/**
 * @param {{
 *   initialFolder?: string,
 *   initialNoteId?: string,
 *   onNoteChange?: (note: object|null) => void,
 *   standalone?: boolean,
 *   enableGraph?: boolean,
 *   enableQuickSwitcher?: boolean,
 * }} opts
 */
export function useNotes(opts = {}) {
    const {
        initialFolder,
        initialNoteId,
        onNoteChange,
        enableGraph = true,
        enableQuickSwitcher = true,
    } = opts;

    // ── View state ──────────────────────────
    const [view, _setView] = useState(initialNoteId ? 'reader' : 'list');
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);

    // ── List state ──────────────────────────
    const [notes, setNotes] = useState([]);
    const [folder, setFolder] = useState(initialFolder || '');
    const [searchQuery, setSearchQuery] = useState('');
    const [sort, setSort] = useState('updated_desc');
    const [tagFilter, setTagFilter] = useState('');
    const pageRef = useRef(1);
    const [hasMore, setHasMore] = useState(false);
    const [folders, setFolders] = useState([]);

    // ── Selection state ─────────────────────
    const [selectMode, setSelectMode] = useState(false);
    const [selectedIds, setSelectedIds] = useState(new Set());

    // ── Editor/Reader state ─────────────────
    const [currentNote, setCurrentNote] = useState(null);
    const [editingId, setEditingId] = useState(initialNoteId || null);
    const [editorMode, setEditorMode] = useState('read'); // 'read' | 'edit' | 'preview'
    const [backlinks, setBacklinks] = useState([]);

    // ── Graph state ─────────────────────────
    const [graphData, setGraphData] = useState(null);
    const graphDirtyRef = useRef(true);

    // ── Quick Switcher ──────────────────────
    const [quickSwitcherOpen, setQuickSwitcherOpen] = useState(false);

    // ── Pinning ─────────────────────────────
    const [pinnedIds, setPinnedIds] = useState(_loadPins);

    // ── API shortcut ────────────────────────
    const api = () => window.sw.api.notes;
    const toast = (msg, v) => window.sw?.toast?.(msg, v);
    const confirm = (msg) => window.sw?.confirm?.(msg) ?? Promise.resolve(window.confirm(msg));

    // ── View switching ──────────────────────
    const setView = useCallback((v) => {
        _setView(v);
        if (v === 'list') {
            setCurrentNote(null);
            setEditingId(null);
            setBacklinks([]);
            if (onNoteChange) onNoteChange(null);
        }
    }, [onNoteChange]);

    // ── Load notes list ─────────────────────
    const _loadList = useCallback(async (append = false) => {
        setLoading(true);
        setError(null);
        try {
            let result, isSearch = false;
            if (searchQuery && searchQuery.length >= 2) {
                isSearch = true;
                const resp = await api().search(searchQuery);
                result = resp.data || [];
                setHasMore(false);
            } else {
                const page = append ? pageRef.current : 1;
                if (!append) pageRef.current = 1;
                const offset = (page - 1) * PAGE_SIZE;
                const resp = await api().list({ limit: PAGE_SIZE, offset, folder, tag: tagFilter, sort });
                result = resp.data || [];
                setHasMore(result.length >= PAGE_SIZE);
            }
            if (append) {
                setNotes(prev => [...prev, ...result]);
            } else {
                setNotes(result);
            }
        } catch (e) {
            setError(e.message);
        }
        setLoading(false);
    }, [searchQuery, folder, tagFilter, sort]);

    const refreshList = useCallback(() => {
        pageRef.current = 1;
        _loadList(false);
    }, [_loadList]);

    const loadMore = useCallback(() => {
        pageRef.current += 1;
        _loadList(true);
    }, [_loadList]);

    // ── Load folders ────────────────────────
    const _loadFolders = useCallback(async () => {
        try {
            const data = await api().folders();
            setFolders(data.folders || data || []);
        } catch {}
    }, []);

    // ── Initial load ────────────────────────
    const didMountRef = useRef(false);
    useEffect(() => {
        if (!didMountRef.current) {
            didMountRef.current = true;
            _loadList();
            _loadFolders();
            if (initialNoteId) {
                _openNoteById(initialNoteId);
            }
        }
    }, []);

    // ── Reload on filter changes ────────────
    const prevFiltersRef = useRef({ folder, sort, tagFilter, searchQuery });
    useEffect(() => {
        const prev = prevFiltersRef.current;
        if (prev.folder !== folder || prev.sort !== sort ||
            prev.tagFilter !== tagFilter || prev.searchQuery !== searchQuery) {
            prevFiltersRef.current = { folder, sort, tagFilter, searchQuery };
            if (didMountRef.current) {
                pageRef.current = 1;
                _loadList(false);
            }
        }
    }, [folder, sort, tagFilter, searchQuery, _loadList]);

    // ── Selection ───────────────────────────
    const enterSelectMode = useCallback(() => {
        setSelectMode(true);
        setSelectedIds(new Set());
    }, []);

    const exitSelectMode = useCallback(() => {
        setSelectMode(false);
        setSelectedIds(new Set());
    }, []);

    const toggleSelect = useCallback((id) => {
        setSelectedIds(prev => {
            const next = new Set(prev);
            if (next.has(id)) next.delete(id); else next.add(id);
            return next;
        });
    }, []);

    const toggleSelectAll = useCallback((checked) => {
        if (checked) {
            setSelectedIds(new Set(notes.map(n => n.id)));
        } else {
            setSelectedIds(new Set());
        }
    }, [notes]);

    const bulkDelete = useCallback(async () => {
        if (selectedIds.size === 0) return;
        const count = selectedIds.size;
        if (!await confirm(`Delete ${count} note${count > 1 ? 's' : ''}?`)) return;
        try {
            const resp = await api().bulkDelete([...selectedIds]);
            toast(`Deleted ${resp.deleted || count} note${count !== 1 ? 's' : ''}`, 'success');
            exitSelectMode();
            graphDirtyRef.current = true;
            refreshList();
            _loadFolders();
        } catch (e) { toast(e.message, 'error'); }
    }, [selectedIds, exitSelectMode, refreshList, _loadFolders]);

    // ── Open note ───────────────────────────
    const _openNoteById = useCallback(async (id) => {
        try {
            const note = await api().get(id);
            setCurrentNote(note);
            setEditingId(id);
            setEditorMode('read');
            _setView('reader');
            if (onNoteChange) onNoteChange(note);
            // Load backlinks
            try {
                const bl = await api().backlinks(id);
                setBacklinks(bl || []);
            } catch { setBacklinks([]); }
        } catch (e) {
            toast('Failed to load note: ' + e.message, 'error');
        }
    }, [onNoteChange]);

    const openNote = useCallback((id) => _openNoteById(id), [_openNoteById]);

    const newNote = useCallback(() => {
        setCurrentNote(null);
        setEditingId(null);
        setEditorMode('edit');
        setBacklinks([]);
        _setView('editor');
        if (onNoteChange) onNoteChange(null);
    }, [onNoteChange]);

    // ── Save note ───────────────────────────
    const saveNote = useCallback(async (fields) => {
        const { title, content, folder_path, tags } = fields;
        if (!title) { toast('Title is required', 'warning'); return false; }
        if (!content) { toast('Content is required', 'warning'); return false; }

        try {
            if (editingId) {
                const updated = await api().update(editingId, {
                    title, content, folder_path, tags, mode: 'replace',
                });
                setCurrentNote(updated);
                toast('Note updated', 'success');
                setEditorMode('read');
                _setView('reader');
                // Reload backlinks
                try {
                    const bl = await api().backlinks(editingId);
                    setBacklinks(bl || []);
                } catch {}
            } else {
                const created = await api().create({ title, content, folder_path, tags });
                setCurrentNote(created);
                setEditingId(created.id);
                toast('Note created', 'success');
                setEditorMode('read');
                _setView('reader');
            }
            graphDirtyRef.current = true;
            return true;
        } catch (e) {
            toast(e.message, 'error');
            return false;
        }
    }, [editingId]);

    // ── Delete note ─────────────────────────
    const deleteNote = useCallback(async () => {
        if (!editingId) return;
        if (!await confirm('Delete this note?')) return;
        try {
            await api().del(editingId);
            toast('Note deleted', 'success');
            graphDirtyRef.current = true;
            setView('list');
            refreshList();
            _loadFolders();
        } catch (e) { toast(e.message, 'error'); }
    }, [editingId, setView, refreshList, _loadFolders]);

    // ── Copy note ───────────────────────────
    const copyNote = useCallback(() => {
        if (!currentNote) return;
        const text = `# ${currentNote.title || ''}\n\n${currentNote.content || ''}`;
        navigator.clipboard.writeText(text)
            .then(() => toast('Copied to clipboard', 'success'))
            .catch(() => toast('Copy failed', 'error'));
    }, [currentNote]);

    // ── Wikilink navigation ─────────────────
    const navigateToLink = useCallback(async (title) => {
        try {
            const resp = await api().searchTitles(title, 1);
            const match = (resp || []).find(
                n => n.title.toLowerCase() === title.toLowerCase()
            );
            if (match) {
                await _openNoteById(match.id);
            } else {
                if (await confirm(`Note "${title}" not found. Create it?`)) {
                    const created = await api().create({
                        title,
                        content: `# ${title}\n\n`,
                        folder_path: '/',
                        tags: [],
                    });
                    await _openNoteById(created.id);
                    graphDirtyRef.current = true;
                }
            }
        } catch (e) { toast('Failed to navigate: ' + e.message, 'error'); }
    }, [_openNoteById]);

    // ── Daily Notes ─────────────────────────
    const _isDailyNote = useCallback((note) => {
        return note && /^Daily \u2014 \d{4}-\d{2}-\d{2}$/.test(note.title);
    }, []);

    const isDailyNote = _isDailyNote(currentNote);

    const openDailyNote = useCallback(async () => {
        const today = new Date().toISOString().slice(0, 10);
        const title = `Daily \u2014 ${today}`;
        try {
            const resp = await api().searchTitles(title, 1);
            const existing = (resp || []).find(
                n => n.title.toLowerCase() === title.toLowerCase()
            );
            if (existing) {
                await _openNoteById(existing.id);
            } else {
                const content = `# ${today}\n\n## Tasks\n\n- [ ] \n\n## Notes\n\n`;
                const created = await api().create({
                    title, content, folder_path: '/daily/', tags: ['daily'],
                });
                await _openNoteById(created.id);
                graphDirtyRef.current = true;
            }
        } catch (e) { toast('Failed to open daily note: ' + e.message, 'error'); }
    }, [_openNoteById]);

    const navigateDaily = useCallback(async (offset) => {
        if (!currentNote || !_isDailyNote(currentNote)) return;
        const dateStr = currentNote.title.replace('Daily \u2014 ', '');
        const d = new Date(dateStr + 'T12:00:00');
        d.setDate(d.getDate() + offset);
        const newDate = d.toISOString().slice(0, 10);
        const title = `Daily \u2014 ${newDate}`;
        try {
            const resp = await api().searchTitles(title, 1);
            const existing = (resp || []).find(
                n => n.title.toLowerCase() === title.toLowerCase()
            );
            if (existing) {
                await _openNoteById(existing.id);
            } else {
                const content = `# ${newDate}\n\n## Tasks\n\n- [ ] \n\n## Notes\n\n`;
                const created = await api().create({
                    title, content, folder_path: '/daily/', tags: ['daily'],
                });
                await _openNoteById(created.id);
                graphDirtyRef.current = true;
            }
        } catch (e) { toast('Failed to navigate: ' + e.message, 'error'); }
    }, [currentNote, _isDailyNote, _openNoteById]);

    // ── Graph ───────────────────────────────
    const loadGraph = useCallback(async () => {
        try {
            const data = await api().graph();
            setGraphData(data);
            graphDirtyRef.current = false;
            return data;
        } catch (e) {
            toast('Failed to load graph: ' + e.message, 'error');
            return null;
        }
    }, []);

    const invalidateGraph = useCallback(() => {
        graphDirtyRef.current = true;
    }, []);

    // ── Pinning ─────────────────────────────
    const togglePin = useCallback((id) => {
        setPinnedIds(prev => {
            const next = new Set(prev);
            if (next.has(id)) next.delete(id); else next.add(id);
            _savePins(next);
            return next;
        });
    }, []);

    // ── Keyboard shortcuts ──────────────────
    useEffect(() => {
        const handler = (e) => {
            // Quick switcher: Ctrl/Cmd + K
            if (enableQuickSwitcher && (e.ctrlKey || e.metaKey) && e.key === 'k') {
                e.preventDefault();
                setQuickSwitcherOpen(prev => !prev);
                return;
            }

            // Ctrl/Cmd + N: new note
            if ((e.ctrlKey || e.metaKey) && e.key === 'n') {
                // Only prevent when notes pane is focused context
                if (document.querySelector('.sw-notes-pane')) {
                    e.preventDefault();
                    newNote();
                    return;
                }
            }

            // Ctrl/Cmd + D: daily note
            if ((e.ctrlKey || e.metaKey) && e.key === 'd') {
                if (document.querySelector('.sw-notes-pane')) {
                    e.preventDefault();
                    openDailyNote();
                    return;
                }
            }
        };

        document.addEventListener('keydown', handler);
        return () => document.removeEventListener('keydown', handler);
    }, [enableQuickSwitcher, newNote, openDailyNote]);

    return {
        // View
        view, setView,
        // List
        notes, loading, error,
        folder, setFolder,
        searchQuery, setSearchQuery,
        sort, setSort,
        hasMore, loadMore,
        folders, refreshList,
        tagFilter, setTagFilter,
        // Selection
        selectMode, selectedIds,
        enterSelectMode, exitSelectMode,
        toggleSelect, toggleSelectAll,
        bulkDelete,
        // Editor/Reader
        currentNote, editingId,
        editorMode, setEditorMode,
        openNote, newNote,
        saveNote, deleteNote, copyNote,
        backlinks, navigateToLink,
        // Daily
        openDailyNote, isDailyNote, navigateDaily,
        // Graph
        graphData, loadGraph, invalidateGraph,
        // Quick Switcher
        quickSwitcherOpen, setQuickSwitcherOpen,
        // Pinning
        pinnedIds, togglePin,
        // Misc
        clearError: useCallback(() => setError(null), []),
    };
}
