import { test, expect, request } from '@playwright/test';

test.describe('Note Grid - Drag and Drop', () => {
  test.beforeAll(async ({ request }) => {
    console.log('[CLEANUP] beforeAll: Cleaning up any existing test notes...');
    
    // Clean up any existing test notes from previous runs
    try {
      const response = await request.get('http://localhost:3000/api/notes');
      const data = await response.json();
      
      if (data.success && data.data) {
        const testNotes = data.data.filter((note: any) =>
          note.title?.startsWith('test-') &&
          note.content?.startsWith('Content ')
        );
        
        console.log(`[CLEANUP] beforeAll: Found ${testNotes.length} test notes to delete`);
        
        for (const note of testNotes) {
          try {
            await request.delete(`http://localhost:3000/api/notes?id=${note.id}`);
            console.log(`[CLEANUP] beforeAll: Deleted note ${note.id}`);
          } catch (error) {
            console.log(`[CLEANUP] beforeAll: Failed to delete note ${note.id}`, error);
          }
        }
      }
    } catch (error) {
      console.log('[CLEANUP] beforeAll: Error fetching notes for cleanup', error);
    }
  });

  test.beforeEach(async ({ page }) => {
    // Generate unique timestamp for this test run to avoid conflicts
    const timestamp = Date.now();
    const testId = `test-${timestamp}`;
    
    await page.goto('/');
    
    // Wait for page to fully load
    await page.waitForLoadState('networkidle');
    
    // Log initial note count before creating new notes
    const initialNotes = page.locator('[data-draggable="true"]');
    const initialCount = await initialNotes.count();
    console.log(`[DEBUG] [${testId}] Initial note count: ${initialCount}`);
    
    // Create multiple notes for testing drag and drop with unique identifiers
    for (let i = 1; i <= 4; i++) {
      const noteTitle = `${testId}-Note ${i}`;
      const noteContent = `${testId}-Content ${i}`;
      
      console.log(`[DEBUG] [${testId}] Creating ${noteTitle}`);
      await page.click('input[placeholder="Take a note..."]');
      await page.fill('input[placeholder="Title"]', noteTitle);
      await page.fill('textarea[placeholder="Take a note..."]', noteContent);
      await page.click('button:has-text("Add")');
      await page.waitForTimeout(500);
      
      // Log note count after each creation
      const currentCount = await page.locator('[data-draggable="true"]').count();
      console.log(`[DEBUG] [${testId}] Note count after creating ${noteTitle}: ${currentCount}`);
    }
    
    // Log final note count
    const finalCount = await page.locator('[data-draggable="true"]').count();
    console.log(`[DEBUG] [${testId}] Final note count after all creations: ${finalCount}`);
  });

  test('should have draggable notes', async ({ page }) => {
    console.log('[DEBUG] Test: should have draggable notes');
    
    // Wait for notes to appear (use a more flexible selector that matches pattern)
    await page.waitForSelector('[data-draggable="true"]');
    
    // Check that notes have data-draggable attribute (dnd-kit uses this)
    const noteCards = page.locator('[data-draggable="true"]');
    const count = await noteCards.count();
    console.log(`[DEBUG] Found ${count} notes with data-draggable="true"`);
    
    // Log first few notes details
    for (let i = 0; i < Math.min(count, 3); i++) {
      const note = noteCards.nth(i);
      const text = await note.textContent();
      const draggableAttr = await note.getAttribute('data-draggable');
      console.log(`[DEBUG] Note ${i}: "${text?.substring(0, 50)}", data-draggable="${draggableAttr}"`);
    }
    
    expect(count).toBeGreaterThanOrEqual(4);
  });

  test('should show cursor-move on note cards', async ({ page }) => {
    await page.waitForSelector('[data-draggable="true"]');
    
    // Check CSS class for cursor-move on the note card inside
    const firstNote = page.locator('[data-draggable="true"]').first();
    const noteCard = firstNote.locator('.note-card-main');
    const className = await noteCard.getAttribute('class');
    expect(className).toContain('cursor-move');
  });

  test('should change opacity when dragging', async ({ page }) => {
    await page.waitForSelector('[data-draggable="true"]');
    
    const firstNote = page.locator('[data-draggable="true"]').first();
    
    // Start drag
    const box = await firstNote.boundingBox();
    if (box) {
      await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
      await page.mouse.down();
      
      // Move to trigger drag
      await page.mouse.move(box.x + box.width / 2 + 50, box.y + box.height / 2 + 50);
      await page.waitForTimeout(200);
      
      // Check if opacity changed (style should have opacity: 0.5)
      const style = await firstNote.getAttribute('style');
      expect(style).toContain('opacity');
      
      await page.mouse.up();
    }
  });

  test('should reorder notes when dropped on another note', async ({ page }) => {
    console.log('[DEBUG] Test: should reorder notes when dropped on another note');
    
    // Wait for notes to be fully loaded and drag-and-drop initialized
    await page.waitForSelector('[data-draggable="true"]', { state: 'attached' });
    await page.waitForTimeout(500); // Extra wait for dnd-kit to initialize
    
    // Get initial order
    const notes = page.locator('[data-draggable="true"]');
    const firstNoteText = await notes.first().textContent();
    const secondNoteText = await notes.nth(1).textContent();
    
    console.log(`[DEBUG] Initial first note: ${firstNoteText?.substring(0, 30)}`);
    console.log(`[DEBUG] Initial second note: ${secondNoteText?.substring(0, 30)}`);
    
    expect(firstNoteText).toMatch(/Note \d+/);
    expect(secondNoteText).toMatch(/Note \d+/);
    
    // Use dragTo for more reliable drag and drop
    const firstNote = notes.first();
    const secondNote = notes.nth(1);
    
    console.log('[DEBUG] Starting drag operation...');
    await firstNote.dragTo(secondNote);
    console.log('[DEBUG] Drag operation completed');
    
    // Wait for reorder to complete and database to update
    await page.waitForTimeout(2000); // Increased wait time for async operations
    
    // Check that order changed
    // Note: This depends on order persisting in the database
    console.log('[DEBUG] Reloading page to verify persistence...');
    await page.reload();
    await page.waitForSelector('[data-draggable="true"]', { state: 'attached' });
    await page.waitForTimeout(500);
    
    // Verify that order changed (implementation dependent)
    console.log('[DEBUG] Page reloaded, checking order...');
  });

  test('should work with pinned and unpinned notes separately', async ({ page }) => {
    await page.waitForSelector('[data-draggable="true"]');
    
    // Pin first note by hovering and clicking pin button
    const firstNoteCard = page.locator('[data-draggable="true"]').first();
    await firstNoteCard.hover();
    
    // Click the pin button
    const pinButton = firstNoteCard.locator('button[title="Pin"]');
    await pinButton.click();
    
    await page.waitForTimeout(500);
    
    // Check that "Pinned" section appears
    await expect(page.locator('h2:has-text("Pinned")')).toBeVisible();
    
    // Verify note is in pinned section
    const pinnedSection = page.locator('h2:has-text("Pinned")').locator('..').locator('div[data-draggable="true"]');
    expect(await pinnedSection.count()).toBeGreaterThanOrEqual(1);
  });

  test('should not mix pinned and unpinned notes when dragging', async ({ page }) => {
    await page.waitForSelector('[data-draggable="true"]');
    
    // Pin first note by hovering and clicking pin button
    const firstNoteCard = page.locator('[data-draggable="true"]').first();
    await firstNoteCard.hover();
    
    // Click the pin button
    const pinButton = firstNoteCard.locator('button[title="Pin"]');
    await pinButton.click();
    
    await page.waitForTimeout(500);
    
    // Should have both Pinned and Others sections
    await expect(page.locator('h2:has-text("Pinned")')).toBeVisible();
    await expect(page.locator('h2:has-text("Others")')).toBeVisible();
    
    // Count notes in each section using data-draggable attribute
    const pinnedNotes = page.locator('h2:has-text("Pinned") ~ div [data-draggable="true"]');
    const unpinnedNotes = page.locator('h2:has-text("Others") ~ div [data-draggable="true"]');
    
    expect(await pinnedNotes.count()).toBeGreaterThanOrEqual(1);
    expect(await unpinnedNotes.count()).toBeGreaterThanOrEqual(3);
  });

  test('should persist note order after page reload', async ({ page }) => {
    await page.waitForSelector('[data-draggable="true"]');
    
    // Get initial order
    const notes = page.locator('[data-draggable="true"]');
    const initialOrder: string[] = [];
    const count = await notes.count();
    
    for (let i = 0; i < Math.min(count, 4); i++) {
      const text = await notes.nth(i).textContent();
      if (text) initialOrder.push(text);
    }
    
    // Reload page
    await page.reload();
    await page.waitForSelector('[data-draggable="true"]');
    
    // Get order after reload
    const notesAfterReload = page.locator('[data-draggable="true"]');
    const reloadedOrder: string[] = [];
    const countAfterReload = await notesAfterReload.count();
    
    for (let i = 0; i < Math.min(countAfterReload, 4); i++) {
      const text = await notesAfterReload.nth(i).textContent();
      if (text) reloadedOrder.push(text);
    }
    
    // Order should be the same
    expect(reloadedOrder.length).toBe(initialOrder.length);
  });

  test.afterEach(async ({ page, request }) => {
    console.log('[CLEANUP] Starting cleanup...');
    
    // Clean up created notes via API - more reliable than UI interaction
    try {
      const response = await request.get('http://localhost:3000/api/notes');
      const data = await response.json();
      
      if (data.success && data.data) {
        const testNotes = data.data.filter((note: any) =>
          note.title?.startsWith('test-') &&
          note.content?.startsWith('Content ')
        );
        
        console.log(`[CLEANUP] Found ${testNotes.length} test notes to delete via API`);
        
        for (const note of testNotes) {
          try {
            await request.delete(`http://localhost:3000/api/notes?id=${note.id}`);
            console.log(`[CLEANUP] Deleted note ${note.id}`);
          } catch (error) {
            console.log(`[CLEANUP] Failed to delete note ${note.id}`, error);
          }
        }
      }
    } catch (error) {
      console.log('[CLEANUP] Error fetching notes for cleanup', error);
    }
  });

  test.afterAll(async ({ request }) => {
    console.log('[CLEANUP] afterAll: Final cleanup of any remaining test notes...');
    
    // Final cleanup to ensure no test notes remain
    try {
      const response = await request.get('http://localhost:3000/api/notes');
      const data = await response.json();
      
      if (data.success && data.data) {
        const testNotes = data.data.filter((note: any) =>
          note.title?.startsWith('test-') &&
          note.content?.startsWith('Content ')
        );
        
        console.log(`[CLEANUP] afterAll: Found ${testNotes.length} remaining test notes to delete`);
        
        for (const note of testNotes) {
          try {
            await request.delete(`http://localhost:3000/api/notes?id=${note.id}`);
            console.log(`[CLEANUP] afterAll: Deleted note ${note.id}`);
          } catch (error) {
            console.log(`[CLEANUP] afterAll: Failed to delete note ${note.id}`, error);
          }
        }
      }
    } catch (error) {
      console.log('[CLEANUP] afterAll: Error fetching notes for final cleanup', error);
    }
  });
});
