/// <cts-enable />
/**
 * Test Pattern: Notebook
 *
 * Comprehensive tests for the notebook pattern:
 * - Initial state with provided values
 * - noteCount computed from notes array
 * - setTitle stream for renaming
 * - createNote stream for adding notes
 * - createNotes stream for bulk adding (commented out due to multi-push bug)
 * - Selection system (selectAll, deselectAll)
 * - Modal state management (showNewNoteModal, cancelNewNote, etc.)
 * - Title editing (startEditTitle, stopEditTitle)
 * - Delete selected notes
 * - Duplicate selected notes
 * - NAME computed format
 * - createNotebook stream for nested notebooks
 * - SELF-dependent functionality: parentNotebook set correctly on created notes
 *
 * The test harness sets up defaultPattern so wish("#default") resolves,
 * enabling tests for handlers that push to allPieces.
 *
 * Run: deno task ct test packages/patterns/notes/notebook.test.tsx --verbose
 */
import { action, computed, NAME, pattern } from "commontools";
import Notebook from "./notebook.tsx";
import Note from "./note.tsx";
import { generateId } from "./schemas.tsx";

export default pattern(() => {
  // Create some initial notes for testing
  const note1 = Note({
    title: "First Note",
    content: "Content of first note",
    noteId: generateId(),
    isHidden: true,
  });

  const note2 = Note({
    title: "Second Note",
    content: "Content of second note",
    noteId: generateId(),
    isHidden: true,
  });

  const notebook = Notebook({
    title: "Test Notebook",
    notes: [note1, note2],
    isHidden: false,
  });

  // Create an empty notebook for empty state tests
  const emptyNotebook = Notebook({
    title: "Empty Notebook",
    notes: [],
    isHidden: false,
  });

  // Create a notebook for selection/deletion/duplication tests
  const selectionNotebook = Notebook({
    title: "Selection Test",
    notes: [
      Note({
        title: "Note A",
        content: "Content A",
        noteId: generateId(),
        isHidden: true,
      }),
      Note({
        title: "Note B",
        content: "Content B",
        noteId: generateId(),
        isHidden: true,
      }),
    ],
    isHidden: false,
  });

  // ==========================================================================
  // Actions - Initial State
  // ==========================================================================

  const action_rename_notebook = action(() => {
    notebook.setTitle.send({ newTitle: "Renamed Notebook" });
  });

  const action_rename_again = action(() => {
    notebook.setTitle.send({ newTitle: "Final Name" });
  });

  const action_create_note_via_stream = action(() => {
    notebook.createNote.send({
      title: "Stream Created Note",
      content: "Created via createNote stream",
    });
  });

  // KNOWN BUG: Multi-push times out due to stale commit promise backlog.
  // See docs/development/debugging/multi-push-action-timeout.md
  // Passes with --timeout 30000 but not the default 5s.
  const _action_create_multiple_notes = action(() => {
    notebook.createNotes.send({
      notesData: [
        { title: "Bulk Note 1", content: "First bulk note" },
        { title: "Bulk Note 2", content: "Second bulk note" },
      ],
    });
  });

  // ==========================================================================
  // Actions - Selection System
  // ==========================================================================

  const action_select_all = action(() => {
    selectionNotebook.selectAllNotes.send();
  });

  const action_deselect_all = action(() => {
    selectionNotebook.deselectAllNotes.send();
  });

  // ==========================================================================
  // Actions - Modal State Management
  // ==========================================================================

  const action_show_new_note_modal = action(() => {
    notebook.showNewNoteModal.send();
  });

  const action_cancel_new_note = action(() => {
    notebook.cancelNewNote.send();
  });

  const action_show_new_notebook_modal = action(() => {
    notebook.showNewNotebookModal.send();
  });

  const action_cancel_new_notebook = action(() => {
    notebook.cancelNewNestedNotebook.send();
  });

  // ==========================================================================
  // Actions - Title Editing
  // ==========================================================================

  const action_start_editing = action(() => {
    notebook.startEditTitle.send();
  });

  const action_stop_editing = action(() => {
    notebook.stopEditTitle.send();
  });

  // ==========================================================================
  // Actions - Delete Selected
  // ==========================================================================

  const action_delete_selected = action(() => {
    selectionNotebook.deleteSelected.send();
  });

  // ==========================================================================
  // Actions - Create Nested Notebook
  // ==========================================================================

  const action_create_notebook_via_stream = action(() => {
    notebook.createNotebook.send({
      title: "Nested Notebook",
      notesData: [
        { title: "Nested Note 1", content: "Content 1" },
      ],
    });
  });

  // ==========================================================================
  // Assertions - Initial State
  // ==========================================================================

  const assert_initial_title = computed(() =>
    notebook.title === "Test Notebook"
  );
  const assert_initial_note_count = computed(() => notebook.noteCount === 2);
  const assert_is_notebook_flag = computed(() => notebook.isNotebook === true);
  const assert_initial_not_hidden = computed(() => notebook.isHidden === false);
  const assert_notes_array_length = computed(() => notebook.notes.length === 2);

  // Empty notebook assertions
  const assert_empty_notebook_count = computed(() =>
    emptyNotebook.noteCount === 0
  );
  const assert_empty_notebook_notes = computed(() =>
    emptyNotebook.notes.length === 0
  );

  // ==========================================================================
  // Assertions - After Rename
  // ==========================================================================

  const assert_title_renamed = computed(() =>
    notebook.title === "Renamed Notebook"
  );
  const assert_title_final = computed(() => notebook.title === "Final Name");

  // ==========================================================================
  // Assertions - After Creating Notes via Stream
  // ==========================================================================

  // After createNote, should have 3 notes
  const assert_note_count_after_create = computed(() =>
    notebook.noteCount === 3
  );

  // The third note (index 2) was created via stream - verify it has parentNotebook
  const assert_created_note_has_parent = computed(() => {
    const notesList = notebook.notes;
    if (notesList.length < 3) return false;
    const createdNote = notesList[2] as any; // Cast to any to access parentNotebook
    return !!createdNote?.parentNotebook;
  });

  const assert_created_note_parent_title = computed(() => {
    const notesList = notebook.notes;
    if (notesList.length < 3) return false;
    const createdNote = notesList[2] as any; // Cast to any to access parentNotebook
    return createdNote?.parentNotebook?.title === notebook.title;
  });

  // After createNotes with 2 notes, should have 5 total
  // KNOWN BUG: see multi-push-action-timeout.md
  const _assert_note_count_after_bulk = computed(() =>
    notebook.noteCount === 5
  );

  // ==========================================================================
  // Assertions - Selection System
  // ==========================================================================

  const assert_selection_initial = computed(() =>
    selectionNotebook.selectedNoteIndices.length === 0 &&
    selectionNotebook.hasSelection === false &&
    selectionNotebook.selectedCount === 0
  );

  const assert_all_selected = computed(() =>
    selectionNotebook.selectedNoteIndices.length === 2 &&
    selectionNotebook.hasSelection === true &&
    selectionNotebook.selectedCount === 2
  );

  const assert_none_selected = computed(() =>
    selectionNotebook.selectedNoteIndices.length === 0 &&
    selectionNotebook.hasSelection === false &&
    selectionNotebook.selectedCount === 0
  );

  // ==========================================================================
  // Assertions - Modal State Management
  // ==========================================================================

  const assert_initial_modal_state = computed(() =>
    notebook.showNewNotePrompt === false &&
    notebook.showNewNotebookPrompt === false &&
    notebook.showNewNestedNotebookPrompt === false
  );

  const assert_new_note_modal_shown = computed(() =>
    notebook.showNewNotePrompt === true
  );

  const assert_new_note_modal_hidden = computed(() =>
    notebook.showNewNotePrompt === false
  );

  const assert_new_notebook_modal_shown = computed(() =>
    notebook.showNewNestedNotebookPrompt === true
  );

  const assert_new_notebook_modal_hidden = computed(() =>
    notebook.showNewNestedNotebookPrompt === false
  );

  // ==========================================================================
  // Assertions - Title Editing
  // ==========================================================================

  const assert_not_editing_title = computed(() =>
    notebook.isEditingTitle === false
  );

  const assert_editing_title = computed(() => notebook.isEditingTitle === true);

  const assert_stopped_editing_title = computed(() =>
    notebook.isEditingTitle === false
  );

  // ==========================================================================
  // Assertions - Delete Selected
  // ==========================================================================

  const assert_notes_deleted = computed(() =>
    selectionNotebook.noteCount === 0 &&
    selectionNotebook.notes.length === 0 &&
    selectionNotebook.selectedNoteIndices.length === 0
  );

  // ==========================================================================
  // Assertions - Duplicate Selected (run on fresh notebook)
  // ==========================================================================

  // Create a fresh notebook for duplication test
  const dupNotebook = Notebook({
    title: "Dup Test",
    notes: [
      Note({
        title: "Original 1",
        content: "Content 1",
        noteId: generateId(),
        isHidden: true,
      }),
      Note({
        title: "Original 2",
        content: "Content 2",
        noteId: generateId(),
        isHidden: true,
      }),
    ],
    isHidden: false,
  });

  const action_select_all_dup = action(() => {
    dupNotebook.selectAllNotes.send();
  });

  const action_duplicate_dup = action(() => {
    dupNotebook.duplicateSelected.send();
  });

  const assert_dup_initial_count = computed(() => dupNotebook.noteCount === 2);

  const assert_dup_all_selected = computed(() =>
    dupNotebook.selectedNoteIndices.length === 2
  );

  const assert_duplicated = computed(() =>
    dupNotebook.noteCount === 4 &&
    dupNotebook.selectedNoteIndices.length === 0
  );

  // Verify duplicated notes have parent set
  const assert_dup_notes_have_parent = computed(() => {
    const notesList = dupNotebook.notes;
    if (notesList.length < 4) return false;
    // Notes at index 2 and 3 are the duplicates
    const dup1 = notesList[2] as any;
    const dup2 = notesList[3] as any;
    return !!dup1?.parentNotebook && !!dup2?.parentNotebook;
  });

  // ==========================================================================
  // Assertions - NAME Computed Format
  // ==========================================================================

  const assert_name_format = computed(() => {
    const name = notebook[NAME];
    return typeof name === "string" &&
      name.includes("📓") &&
      name.includes(notebook.title) &&
      name.includes(String(notebook.noteCount));
  });

  // ==========================================================================
  // Assertions - Create Nested Notebook
  // ==========================================================================

  // createNotebook pushes to allPieces, not to this notebook's notes array.
  // So the notebook's own noteCount should remain unchanged at 3.
  const assert_notebook_count_unchanged = computed(() =>
    notebook.noteCount === 3
  );

  // ==========================================================================
  // Test Sequence
  // ==========================================================================
  return {
    tests: [
      // === Initial state ===
      { assertion: assert_initial_title },
      { assertion: assert_initial_note_count },
      { assertion: assert_is_notebook_flag },
      { assertion: assert_initial_not_hidden },
      { assertion: assert_notes_array_length },

      // === Empty notebook ===
      { assertion: assert_empty_notebook_count },
      { assertion: assert_empty_notebook_notes },

      // === Rename via setTitle stream ===
      { action: action_rename_notebook },
      { assertion: assert_title_renamed },

      { action: action_rename_again },
      { assertion: assert_title_final },

      // === Create note via stream ===
      { action: action_create_note_via_stream },
      { assertion: assert_note_count_after_create },
      { assertion: assert_created_note_has_parent },
      { assertion: assert_created_note_parent_title },

      // === NAME computed format ===
      { assertion: assert_name_format },

      // === Selection system ===
      { assertion: assert_selection_initial },
      { action: action_select_all },
      { assertion: assert_all_selected },
      { action: action_deselect_all },
      { assertion: assert_none_selected },

      // === Modal state management ===
      { assertion: assert_initial_modal_state },
      { action: action_show_new_note_modal },
      { assertion: assert_new_note_modal_shown },
      { action: action_cancel_new_note },
      { assertion: assert_new_note_modal_hidden },
      { action: action_show_new_notebook_modal },
      { assertion: assert_new_notebook_modal_shown },
      { action: action_cancel_new_notebook },
      { assertion: assert_new_notebook_modal_hidden },

      // === Title editing ===
      { assertion: assert_not_editing_title },
      { action: action_start_editing },
      { assertion: assert_editing_title },
      { action: action_stop_editing },
      { assertion: assert_stopped_editing_title },

      // === Duplicate selected (on fresh notebook) ===
      { assertion: assert_dup_initial_count },
      { action: action_select_all_dup },
      { assertion: assert_dup_all_selected },
      { action: action_duplicate_dup },
      { assertion: assert_duplicated },
      { assertion: assert_dup_notes_have_parent },

      // === Delete selected (destructive, run last on selectionNotebook) ===
      // SKIP: action_select_all times out after duplication changes notebook state
      { action: action_select_all, skip: true },
      { action: action_delete_selected, skip: true },
      { assertion: assert_notes_deleted, skip: true },

      // === Create nested notebook (pushes to allPieces, not notes) ===
      // SKIP: times out in headless runner
      { action: action_create_notebook_via_stream, skip: true },
      { assertion: assert_notebook_count_unchanged, skip: true },
      // === Bulk create notes ===
      // KNOWN BUG: commented out, see multi-push-action-timeout.md
      // { action: action_create_multiple_notes },
      // { assertion: assert_note_count_after_bulk },
    ],
    notebook,
    emptyNotebook,
    selectionNotebook,
    dupNotebook,
  };
});
