'use server'

import { revalidatePath } from 'next/cache'
import prisma from '@/lib/prisma'
import { Note, CheckItem } from '@/lib/types'
import { auth } from '@/auth'
import { getAIProvider } from '@/lib/ai/factory'
import { cosineSimilarity, validateEmbedding, calculateRRFK, detectQueryType, getSearchWeights } from '@/lib/utils'
import { getSystemConfig, getConfigNumber, getConfigBoolean, SEARCH_DEFAULTS } from '@/lib/config'
import { contextualAutoTagService } from '@/lib/ai/services/contextual-auto-tag.service'

// Helper function to parse JSON strings from database
function parseNote(dbNote: any): Note {
  // Parse embedding
  const embedding = dbNote.embedding ? JSON.parse(dbNote.embedding) : null

  // Validate embedding if present
  if (embedding && Array.isArray(embedding)) {
    const validation = validateEmbedding(embedding)
    if (!validation.valid) {
      // Don't include invalid embedding in the returned note
      return {
        ...dbNote,
        checkItems: dbNote.checkItems ? JSON.parse(dbNote.checkItems) : null,
        labels: dbNote.labels ? JSON.parse(dbNote.labels) : null,
        images: dbNote.images ? JSON.parse(dbNote.images) : null,
        links: dbNote.links ? JSON.parse(dbNote.links) : null,
        embedding: null, // Exclude invalid embedding
        sharedWith: dbNote.sharedWith ? JSON.parse(dbNote.sharedWith) : [],
        size: dbNote.size || 'small',
      }
    }
  }

  return {
    ...dbNote,
    checkItems: dbNote.checkItems ? JSON.parse(dbNote.checkItems) : null,
    labels: dbNote.labels ? JSON.parse(dbNote.labels) : null,
    images: dbNote.images ? JSON.parse(dbNote.images) : null,
    links: dbNote.links ? JSON.parse(dbNote.links) : null,
    embedding,
    sharedWith: dbNote.sharedWith ? JSON.parse(dbNote.sharedWith) : [],
    size: dbNote.size || 'small',
  }
}

// Helper to get hash color for labels (copied from utils)
function getHashColor(name: string): string {
  const colors = ['red', 'blue', 'green', 'yellow', 'purple', 'pink', 'orange', 'gray']
  let hash = 0
  for (let i = 0; i < name.length; i++) {
    hash = name.charCodeAt(i) + ((hash << 5) - hash)
  }
  return colors[Math.abs(hash) % colors.length]
}

// Comprehensive sync function for labels - ensures consistency between Note.labels and Label table
async function syncLabels(userId: string, noteLabels: string[] = []) {
  try {
    // Step 1: Create Label records for any labels in notes that don't exist in Label table
    // Get all existing labels for this user to do case-insensitive check in JS
    const existingLabels = await prisma.label.findMany({
      where: { userId },
      select: { id: true, name: true }
    })

    // Create a map for case-insensitive lookup
    const existingLabelMap = new Map<string, string>()
    existingLabels.forEach(label => {
      existingLabelMap.set(label.name.toLowerCase(), label.name)
    })

    for (const labelName of noteLabels) {
      if (!labelName || labelName.trim() === '') continue

      const trimmedLabel = labelName.trim()
      const lowerLabel = trimmedLabel.toLowerCase()

      // Check if label already exists (case-insensitive)
      const existingName = existingLabelMap.get(lowerLabel)

      // If label doesn't exist, create it
      if (!existingName) {
        try {
          await prisma.label.create({
            data: {
              userId,
              name: trimmedLabel,
              color: getHashColor(trimmedLabel)
            }
          })
          // Add to map to prevent duplicates in same batch
          existingLabelMap.set(lowerLabel, trimmedLabel)
        } catch (e: any) {
          // Ignore unique constraint violations (race condition)
          if (e.code !== 'P2002') {
            console.error(`[SYNC] Failed to create label "${trimmedLabel}":`, e)
          }
        }
      }
    }

    // Step 2: Get ALL labels currently used in ALL user's notes
    const allNotes = await prisma.note.findMany({
      where: { userId },
      select: { labels: true }
    })

    const usedLabelsSet = new Set<string>()
    allNotes.forEach(note => {
      if (note.labels) {
        try {
          const parsedLabels: string[] = JSON.parse(note.labels)
          if (Array.isArray(parsedLabels)) {
            parsedLabels.forEach(l => {
              if (l && l.trim()) {
                usedLabelsSet.add(l.trim().toLowerCase())
              }
            })
          }
        } catch (e) {
          console.error('[SYNC] Failed to parse labels:', e)
        }
      }
    })

    // Step 3: Delete orphan Label records (labels not in any note)
    const allLabels = await prisma.label.findMany({
      where: { userId }
    })

    for (const label of allLabels) {
      if (!usedLabelsSet.has(label.name.toLowerCase())) {
        try {
          await prisma.label.delete({
            where: { id: label.id }
          })
        } catch (e) {
          console.error(`Failed to delete orphan label:`, e)
        }
      }
    }
  } catch (error) {
    console.error('Fatal error in syncLabels:', error)
  }
}

// Get all notes (non-archived by default)
export async function getNotes(includeArchived = false) {
  const session = await auth();
  if (!session?.user?.id) return [];

  try {
    const notes = await prisma.note.findMany({
      where: {
        userId: session.user.id,
        ...(includeArchived ? {} : { isArchived: false }),
      },
      orderBy: [
        { isPinned: 'desc' },
        { order: 'asc' },
        { updatedAt: 'desc' }
      ]
    })

    return notes.map(parseNote)
  } catch (error) {
    console.error('Error fetching notes:', error)
    return []
  }
}

// Get archived notes only
export async function getArchivedNotes() {
  const session = await auth();
  if (!session?.user?.id) return [];

  try {
    const notes = await prisma.note.findMany({
      where: {
        userId: session.user.id,
        isArchived: true
      },
      orderBy: { updatedAt: 'desc' }
    })

    return notes.map(parseNote)
  } catch (error) {
    console.error('Error fetching archived notes:', error)
    return []
  }
}

// Search notes - SIMPLE AND EFFECTIVE
// Supports contextual search within notebook (IA5)
export async function searchNotes(query: string, useSemantic: boolean = false, notebookId?: string) {
  const session = await auth();
  if (!session?.user?.id) return [];

  try {
    // If query empty, return all notes
    if (!query || !query.trim()) {
      return await getAllNotes();
    }

    // If semantic search is requested, use the full implementation
    if (useSemantic) {
      return await semanticSearch(query, session.user.id, notebookId); // NEW: Pass notebookId for contextual search (IA5)
    }

    // Get all notes
    const allNotes = await prisma.note.findMany({
      where: {
        userId: session.user.id,
        isArchived: false
      }
    });

    const queryLower = query.toLowerCase().trim();

    // SIMPLE FILTER: check if query is in title OR content OR labels
    const filteredNotes = allNotes.filter(note => {
      const title = (note.title || '').toLowerCase();
      const content = note.content.toLowerCase();
      const labels = note.labels ? JSON.parse(note.labels) : [];

      // Check if query exists in title, content, or any label
      return title.includes(queryLower) ||
        content.includes(queryLower) ||
        labels.some((label: string) => label.toLowerCase().includes(queryLower));
    });

    return filteredNotes.map(parseNote);
  } catch (error) {
    console.error('Search error:', error);
    return [];
  }
}

// Semantic search with AI embeddings - SIMPLE VERSION
// Supports contextual search within notebook (IA5)
async function semanticSearch(query: string, userId: string, notebookId?: string) {
  const allNotes = await prisma.note.findMany({
    where: {
      userId: userId,
      isArchived: false,
      ...(notebookId !== undefined ? { notebookId } : {}) // NEW: Filter by notebook (IA5)
    }
  });

  const queryLower = query.toLowerCase().trim();

  // Get query embedding
  let queryEmbedding: number[] | null = null;
  try {
    const provider = getAIProvider(await getSystemConfig());
    queryEmbedding = await provider.getEmbeddings(query);
  } catch (e) {
    console.error('Failed to generate query embedding:', e);
    // Fallback to simple keyword search
    queryEmbedding = null;
  }

  // Filter notes: keyword match OR semantic match (threshold 30%)
  const results = allNotes.map(note => {
    const title = (note.title || '').toLowerCase();
    const content = note.content.toLowerCase();
    const labels = note.labels ? JSON.parse(note.labels) : [];

    // Keyword match
    const keywordMatch = title.includes(queryLower) ||
      content.includes(queryLower) ||
      labels.some((l: string) => l.toLowerCase().includes(queryLower));

    // Semantic match (if embedding available)
    let semanticMatch = false;
    let similarity = 0;
    if (queryEmbedding && note.embedding) {
      similarity = cosineSimilarity(queryEmbedding, JSON.parse(note.embedding));
      semanticMatch = similarity > 0.3; // 30% threshold - works well for related concepts
    }

    return {
      note,
      keywordMatch,
      semanticMatch,
      similarity
    };
  }).filter(r => r.keywordMatch || r.semanticMatch);

  // Parse and add match info
  return results.map(r => {
    const parsed = parseNote(r.note);

    // Determine match type
    let matchType: 'exact' | 'related' | null = null;
    if (r.semanticMatch) {
      matchType = 'related';
    } else if (r.keywordMatch) {
      matchType = 'exact';
    }

    return {
      ...parsed,
      matchType
    };
  });
}

// Create a new note
export async function createNote(data: {
  title?: string
  content: string
  color?: string
  type?: 'text' | 'checklist'
  checkItems?: CheckItem[]
  labels?: string[]
  images?: string[]
  links?: any[]
  isArchived?: boolean
  reminder?: Date | null
  isMarkdown?: boolean
  size?: 'small' | 'medium' | 'large'
  sharedWith?: string[]
  autoGenerated?: boolean
  notebookId?: string | undefined // Assign note to a notebook if provided
}) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    let embeddingString: string | null = null;
    try {
      const provider = getAIProvider(await getSystemConfig());
      const embedding = await provider.getEmbeddings(data.content);
      if (embedding) embeddingString = JSON.stringify(embedding);
    } catch (e) {
      console.error('Embedding generation failed:', e);
    }

    // AUTO-LABELING: If no labels provided and auto-labeling is enabled, suggest labels
    let labelsToUse = data.labels || null;
    if ((!labelsToUse || labelsToUse.length === 0) && data.notebookId) {
      try {
        const autoLabelingEnabled = await getConfigBoolean('AUTO_LABELING_ENABLED', true);
        const autoLabelingConfidence = await getConfigNumber('AUTO_LABELING_CONFIDENCE_THRESHOLD', 70);

        if (autoLabelingEnabled) {

          const suggestions = await contextualAutoTagService.suggestLabels(
            data.content,
            data.notebookId,
            session.user.id
          );

          // Apply suggestions with confidence >= threshold
          const appliedLabels = suggestions
            .filter(s => s.confidence >= autoLabelingConfidence)
            .map(s => s.label);

          if (appliedLabels.length > 0) {
            labelsToUse = appliedLabels;

          } else {

          }
        } else {

        }
      } catch (error) {
        console.error('[AUTO-LABELING] Failed to suggest labels:', error);
        // Continue without auto-labeling on error
      }
    }

    const note = await prisma.note.create({
      data: {
        userId: session.user.id,
        title: data.title || null,
        content: data.content,
        color: data.color || 'default',
        type: data.type || 'text',
        checkItems: data.checkItems ? JSON.stringify(data.checkItems) : null,
        labels: labelsToUse ? JSON.stringify(labelsToUse) : null,
        images: data.images ? JSON.stringify(data.images) : null,
        links: data.links ? JSON.stringify(data.links) : null,
        isArchived: data.isArchived || false,
        reminder: data.reminder || null,
        isMarkdown: data.isMarkdown || false,
        size: data.size || 'small',
        embedding: embeddingString,
        sharedWith: data.sharedWith && data.sharedWith.length > 0 ? JSON.stringify(data.sharedWith) : null,
        autoGenerated: data.autoGenerated || null,
        notebookId: data.notebookId || null, // Assign note to notebook if provided
      }
    })

    // Sync labels to ensure Label records exist
    if (labelsToUse && labelsToUse.length > 0) {
      await syncLabels(session.user.id, labelsToUse)
    }

    // Revalidate main page (handles both inbox and notebook views via query params)
    revalidatePath('/')
    return parseNote(note)
  } catch (error) {
    console.error('Error creating note:', error)
    throw new Error('Failed to create note')
  }
}

// Update a note
export async function updateNote(id: string, data: {
  title?: string | null
  content?: string
  color?: string
  isPinned?: boolean
  isArchived?: boolean
  type?: 'text' | 'checklist'
  checkItems?: CheckItem[] | null
  labels?: string[] | null
  images?: string[] | null
  links?: any[] | null
  reminder?: Date | null
  isMarkdown?: boolean
  size?: 'small' | 'medium' | 'large'
  autoGenerated?: boolean | null
  notebookId?: string | null
}) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    const oldNote = await prisma.note.findUnique({
      where: { id, userId: session.user.id },
      select: { labels: true, notebookId: true }
    })
    const oldLabels: string[] = oldNote?.labels ? JSON.parse(oldNote.labels) : []
    const oldNotebookId = oldNote?.notebookId

    const updateData: any = { ...data }

    if (data.content !== undefined) {
      try {
        const provider = getAIProvider(await getSystemConfig());
        const embedding = await provider.getEmbeddings(data.content);
        updateData.embedding = embedding ? JSON.stringify(embedding) : null;
      } catch (e) {
        console.error('Embedding regeneration failed:', e);
      }
    }

    if ('checkItems' in data) updateData.checkItems = data.checkItems ? JSON.stringify(data.checkItems) : null
    if ('labels' in data) updateData.labels = data.labels ? JSON.stringify(data.labels) : null
    if ('images' in data) updateData.images = data.images ? JSON.stringify(data.images) : null
    if ('links' in data) updateData.links = data.links ? JSON.stringify(data.links) : null
    if ('notebookId' in data) updateData.notebookId = data.notebookId
    // Explicitly handle size to ensure it propagates
    if ('size' in data && data.size) updateData.size = data.size

    // Only update contentUpdatedAt for actual content changes, NOT for property changes
    // (size, color, isPinned, isArchived are properties, not content)
    const contentFields = ['title', 'content', 'checkItems', 'images', 'links']
    const isContentChange = contentFields.some(field => field in data)
    if (isContentChange) {
      updateData.contentUpdatedAt = new Date()
    }

    const note = await prisma.note.update({
      where: { id, userId: session.user.id },
      data: updateData
    })

    // Sync labels to ensure consistency between Note.labels and Label table
    // This handles both creating new Label records and cleaning up orphans
    if (data.labels !== undefined) {
      await syncLabels(session.user.id, data.labels || [])
    }

    // Only revalidate for STRUCTURAL changes that affect the page layout/lists
    // Content edits (title, content, size, color) use optimistic UI — no refresh needed
    const structuralFields = ['isPinned', 'isArchived', 'labels', 'notebookId']
    const isStructuralChange = structuralFields.some(field => field in data)

    if (isStructuralChange) {
      revalidatePath('/')
      revalidatePath(`/note/${id}`)

      if (data.notebookId !== undefined && data.notebookId !== oldNotebookId) {
        if (oldNotebookId) {
          revalidatePath(`/notebook/${oldNotebookId}`)
        }
        if (data.notebookId) {
          revalidatePath(`/notebook/${data.notebookId}`)
        }
      }
    }

    return parseNote(note)
  } catch (error) {
    console.error('Error updating note:', error)
    throw error // Re-throw the REAL error, not a generic one
  }
}

// Delete a note
export async function deleteNote(id: string) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    await prisma.note.delete({ where: { id, userId: session.user.id } })

    // Sync labels with empty array to trigger cleanup of any orphans
    // The syncLabels function will scan all remaining notes and clean up unused labels
    await syncLabels(session.user.id, [])

    revalidatePath('/')
    return { success: true }
  } catch (error) {
    console.error('Error deleting note:', error)
    throw new Error('Failed to delete note')
  }
}

// Toggle functions
export async function togglePin(id: string, isPinned: boolean) { return updateNote(id, { isPinned }) }
export async function toggleArchive(id: string, isArchived: boolean) { return updateNote(id, { isArchived }) }
export async function updateColor(id: string, color: string) { return updateNote(id, { color }) }
export async function updateLabels(id: string, labels: string[]) { return updateNote(id, { labels }) }
export async function removeFusedBadge(id: string) { return updateNote(id, { autoGenerated: null }) }

// Update note size WITHOUT revalidation - client uses optimistic updates
export async function updateSize(id: string, size: 'small' | 'medium' | 'large') {

  const result = await updateNote(id, { size })

  return result
}

// Get all unique labels
export async function getAllLabels() {
  try {
    const notes = await prisma.note.findMany({ select: { labels: true } })
    const labelsSet = new Set<string>()
    notes.forEach((note: any) => {
      const labels = note.labels ? JSON.parse(note.labels) : null
      if (labels) labels.forEach((label: string) => labelsSet.add(label))
    })
    return Array.from(labelsSet).sort()
  } catch (error) {
    console.error('Error fetching labels:', error)
    return []
  }
}

// Reorder
export async function reorderNotes(draggedId: string, targetId: string) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');
  try {
    const draggedNote = await prisma.note.findUnique({ where: { id: draggedId, userId: session.user.id } })
    const targetNote = await prisma.note.findUnique({ where: { id: targetId, userId: session.user.id } })
    if (!draggedNote || !targetNote) throw new Error('Notes not found')
    const allNotes = await prisma.note.findMany({
      where: { userId: session.user.id, isPinned: draggedNote.isPinned, isArchived: false },
      orderBy: { order: 'asc' }
    })
    const reorderedNotes = allNotes.filter((n: any) => n.id !== draggedId)
    const targetIndex = reorderedNotes.findIndex((n: any) => n.id === targetId)
    reorderedNotes.splice(targetIndex, 0, draggedNote)
    const updates = reorderedNotes.map((note: any, index: number) =>
      prisma.note.update({ where: { id: note.id }, data: { order: index } })
    )
    await prisma.$transaction(updates)
    revalidatePath('/')
    return { success: true }
  } catch (error) {
    throw new Error('Failed to reorder notes')
  }
}

export async function updateFullOrder(ids: string[]) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');
  const userId = session.user.id;
  try {
    const updates = ids.map((id: string, index: number) =>
      prisma.note.update({ where: { id, userId }, data: { order: index } })
    )
    await prisma.$transaction(updates)
    revalidatePath('/')
    return { success: true }
  } catch (error) {
    throw new Error('Failed to update order')
  }
}

// Optimized version for drag & drop - no revalidation to prevent double refresh
export async function updateFullOrderWithoutRevalidation(ids: string[]) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');
  const userId = session.user.id;
  try {
    const updates = ids.map((id: string, index: number) =>
      prisma.note.update({ where: { id, userId }, data: { order: index } })
    )
    await prisma.$transaction(updates)
    return { success: true }
  } catch (error) {
    throw new Error('Failed to update order')
  }
}

// Maintenance - Sync all labels and clean up orphans
export async function cleanupAllOrphans() {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');
  const userId = session.user.id;
  let createdCount = 0;
  let deletedCount = 0;
  let errors: any[] = [];
  try {
    // Step 1: Get all labels from notes
    const allNotes = await prisma.note.findMany({ where: { userId }, select: { labels: true } })
    const allNoteLabels = new Set<string>();
    allNotes.forEach(note => {
      if (note.labels) {
        try {
          const parsedLabels: string[] = JSON.parse(note.labels);
          if (Array.isArray(parsedLabels)) {
            parsedLabels.forEach(l => {
              if (l && l.trim()) allNoteLabels.add(l.trim());
            });
          }
        } catch (e) {
          console.error('[CLEANUP] Failed to parse labels:', e);
        }
      }
    });

    // Step 2: Get existing labels for case-insensitive comparison
    const existingLabels = await prisma.label.findMany({
      where: { userId },
      select: { id: true, name: true }
    })
    const existingLabelMap = new Map<string, string>()
    existingLabels.forEach(label => {
      existingLabelMap.set(label.name.toLowerCase(), label.name)
    })

    // Step 3: Create missing Label records
    for (const labelName of allNoteLabels) {
      const lowerLabel = labelName.toLowerCase();
      const existingName = existingLabelMap.get(lowerLabel);

      if (!existingName) {
        try {
          await prisma.label.create({
            data: {
              userId,
              name: labelName,
              color: getHashColor(labelName)
            }
          });
          createdCount++;
          existingLabelMap.set(lowerLabel, labelName);
        } catch (e: any) {
          console.error(`Failed to create label:`, e);
          errors.push({ label: labelName, error: e.message, code: e.code });
          // Continue with next label
        }
      }
    }

    // Step 4: Delete orphan Label records
    const allDefinedLabels = await prisma.label.findMany({ where: { userId }, select: { id: true, name: true } })
    const usedLabelsSet = new Set<string>();
    allNotes.forEach(note => {
      if (note.labels) {
        try {
          const parsedLabels: string[] = JSON.parse(note.labels);
          if (Array.isArray(parsedLabels)) parsedLabels.forEach(l => usedLabelsSet.add(l.toLowerCase()));
        } catch (e) {
          console.error('Failed to parse labels for orphan check:', e);
        }
      }
    });
    const orphans = allDefinedLabels.filter(label => !usedLabelsSet.has(label.name.toLowerCase()));
    for (const orphan of orphans) {
      try {
        await prisma.label.delete({ where: { id: orphan.id } });
        deletedCount++;
      } catch (e) {
        console.error(`Failed to delete orphan:`, e);
      }
    }

    revalidatePath('/')
    return {
      success: true,
      created: createdCount,
      deleted: deletedCount,
      errors,
      message: `Created ${createdCount} missing label records, deleted ${deletedCount} orphan labels${errors.length > 0 ? ` (${errors.length} errors)` : ''}`
    }
  } catch (error) {
    console.error('[CLEANUP] Fatal error:', error);
    throw new Error('Failed to cleanup database')
  }
}

export async function syncAllEmbeddings() {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');
  const userId = session.user.id;
  let updatedCount = 0;
  try {
    const notesToSync = await prisma.note.findMany({ where: { userId, embedding: null } })
    const provider = getAIProvider(await getSystemConfig());
    for (const note of notesToSync) {
      if (!note.content) continue;
      try {
        const embedding = await provider.getEmbeddings(note.content);
        if (embedding) {
          await prisma.note.update({ where: { id: note.id }, data: { embedding: JSON.stringify(embedding) } })
          updatedCount++;
        }
      } catch (e) { }
    }
    return { success: true, count: updatedCount }
  } catch (error: any) {
    throw new Error(`Failed to sync embeddings: ${error.message}`)
  }
}

// Get all notes including those shared with the user
export async function getAllNotes(includeArchived = false) {
  const session = await auth();
  if (!session?.user?.id) return [];

  const userId = session.user.id;

  try {
    // Get user's own notes
    const ownNotes = await prisma.note.findMany({
      where: {
        userId: userId,
        ...(includeArchived ? {} : { isArchived: false }),
      },
      orderBy: [
        { isPinned: 'desc' },
        { order: 'asc' },
        { updatedAt: 'desc' }
      ]
    })

    // Get notes shared with user via NoteShare (accepted only)
    const acceptedShares = await prisma.noteShare.findMany({
      where: {
        userId: userId,
        status: 'accepted'
      },
      include: {
        note: true
      }
    })

    // Filter out archived shared notes if needed
    const sharedNotes = acceptedShares
      .map(share => share.note)
      .filter(note => includeArchived || !note.isArchived)

    const allNotes = [...ownNotes.map(parseNote), ...sharedNotes.map(parseNote)]

    return allNotes
  } catch (error) {
    console.error('Error fetching notes:', error)
    return []
  }
}

// Get pinned notes only
export async function getPinnedNotes(notebookId?: string) {
  const session = await auth();
  if (!session?.user?.id) return [];

  const userId = session.user.id;

  try {
    const notes = await prisma.note.findMany({
      where: {
        userId: userId,
        isPinned: true,
        isArchived: false,
        ...(notebookId !== undefined ? { notebookId } : {})
      },
      orderBy: [
        { order: 'asc' },
        { updatedAt: 'desc' }
      ]
    })

    return notes.map(parseNote)
  } catch (error) {
    console.error('Error fetching pinned notes:', error)
    return []
  }
}

// Get recent notes (notes modified in the last 7 days)
export async function getRecentNotes(limit: number = 3) {
  const session = await auth();
  if (!session?.user?.id) return [];

  const userId = session.user.id;

  try {
    const sevenDaysAgo = new Date()
    sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7)
    sevenDaysAgo.setHours(0, 0, 0, 0) // Set to start of day

    const notes = await prisma.note.findMany({
      where: {
        userId: userId,
        contentUpdatedAt: { gte: sevenDaysAgo },
        isArchived: false
      },
      orderBy: { contentUpdatedAt: 'desc' },
      take: limit
    })

    return notes.map(parseNote)
  } catch (error) {
    console.error('Error fetching recent notes:', error)
    return []
  }
}

export async function getNoteById(noteId: string) {
  const session = await auth();
  if (!session?.user?.id) return null;

  const userId = session.user.id;

  try {
    const note = await prisma.note.findFirst({
      where: {
        id: noteId,
        OR: [
          { userId: userId },
          {
            shares: {
              some: {
                userId: userId,
                status: 'accepted'
              }
            }
          }
        ]
      }
    })

    if (!note) return null

    return parseNote(note)
  } catch (error) {
    console.error('Error fetching note:', error)
    return null
  }
}

// Add a collaborator to a note (updated to use new share request system)
export async function addCollaborator(noteId: string, userEmail: string) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    // Use the new share request system
    const result = await createShareRequest(noteId, userEmail, 'view');

    // Get user info for response
    const targetUser = await prisma.user.findUnique({
      where: { email: userEmail },
      select: { id: true, name: true, email: true, image: true }
    });

    if (!targetUser) {
      throw new Error('User not found')
    }

    return { success: true, user: targetUser }
  } catch (error: any) {
    console.error('Error adding collaborator:', error)
    throw error
  }
}

// Remove a collaborator from a note
export async function removeCollaborator(noteId: string, userId: string) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    const note = await prisma.note.findUnique({
      where: { id: noteId }
    })

    if (!note) {
      throw new Error('Note not found')
    }

    if (note.userId !== session.user.id) {
      throw new Error('You can only manage collaborators on your own notes')
    }

    // Delete the NoteShare record (cascades to remove access)
    await prisma.noteShare.deleteMany({
      where: {
        noteId,
        userId
      }
    })

    // Don't revalidatePath here - it would close all open dialogs!
    // The UI will update via optimistic updates in the dialog
    return { success: true }
  } catch (error: any) {
    console.error('Error removing collaborator:', error)
    throw new Error(error.message || 'Failed to remove collaborator')
  }
}

// Get collaborators for a note
export async function getNoteCollaborators(noteId: string) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    const note = await prisma.note.findUnique({
      where: { id: noteId },
      select: { userId: true }
    })

    if (!note) {
      throw new Error('Note not found')
    }

    if (note.userId !== session.user.id) {
      throw new Error('You do not have access to this note')
    }

    // Get all users who have been shared this note (any status)
    const shares = await prisma.noteShare.findMany({
      where: {
        noteId
      },
      include: {
        user: {
          select: {
            id: true,
            name: true,
            email: true,
            image: true
          }
        }
      }
    })

    return shares.map(share => share.user)
  } catch (error: any) {
    console.error('Error fetching collaborators:', error)
    throw new Error(error.message || 'Failed to fetch collaborators')
  }
}

// Get all users associated with a note (owner + collaborators)
export async function getNoteAllUsers(noteId: string) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    const note = await prisma.note.findUnique({
      where: { id: noteId },
      select: { userId: true }
    })

    if (!note) {
      throw new Error('Note not found')
    }

    // Check access via NoteShare
    const share = await prisma.noteShare.findUnique({
      where: {
        noteId_userId: {
          noteId,
          userId: session.user.id
        }
      }
    })

    const hasAccess = note.userId === session.user.id || (share && share.status === 'accepted')

    if (!hasAccess) {
      throw new Error('You do not have access to this note')
    }

    // Get owner
    const owner = await prisma.user.findUnique({
      where: { id: note.userId! },
      select: {
        id: true,
        name: true,
        email: true,
        image: true
      }
    })

    if (!owner) {
      throw new Error('Owner not found')
    }

    // Get collaborators (accepted shares)
    const shares = await prisma.noteShare.findMany({
      where: {
        noteId,
        status: 'accepted'
      },
      include: {
        user: {
          select: {
            id: true,
            name: true,
            email: true,
            image: true
          }
        }
      }
    })

    const collaborators = shares.map(share => share.user)

    // Return owner + collaborators
    return [owner, ...collaborators]
  } catch (error: any) {
    console.error('Error fetching note users:', error)
    throw new Error(error.message || 'Failed to fetch note users')
  }
}

// ============================================================
// NEW: Share Request System with Accept/Decline Workflow
// ============================================================

// Create a share request (invite user)
export async function createShareRequest(noteId: string, recipientEmail: string, permission: 'view' | 'comment' | 'edit' = 'view') {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    // Find note
    const note = await prisma.note.findUnique({
      where: { id: noteId }
    });

    if (!note) {
      throw new Error('Note not found');
    }

    // Check ownership
    if (note.userId !== session.user.id) {
      throw new Error('Only the owner can share notes');
    }

    // Find recipient by email
    const recipient = await prisma.user.findUnique({
      where: { email: recipientEmail }
    });

    if (!recipient) {
      throw new Error('User not found');
    }

    // Don't share with yourself
    if (recipient.id === session.user.id) {
      throw new Error('You cannot share with yourself');
    }

    // Check if share already exists
    const existingShare = await prisma.noteShare.findUnique({
      where: {
        noteId_userId: {
          noteId,
          userId: recipient.id
        }
      }
    });

    if (existingShare) {
      if (existingShare.status === 'declined') {
        // Reactivate declined share
        await prisma.noteShare.update({
          where: { id: existingShare.id },
          data: {
            status: 'pending',
            notifiedAt: new Date(),
            permission
          }
        });
      } else if (existingShare.status === 'removed') {
        // Reactivate removed share
        await prisma.noteShare.update({
          where: { id: existingShare.id },
          data: {
            status: 'pending',
            notifiedAt: new Date(),
            permission
          }
        });
      } else {
        throw new Error('Note already shared with this user');
      }
    } else {
      // Create new share request
      await prisma.noteShare.create({
        data: {
          noteId,
          userId: recipient.id,
          sharedBy: session.user.id,
          status: 'pending',
          permission,
          notifiedAt: new Date()
        }
      });
    }

    // Don't revalidatePath here - it would close all open dialogs!
    // The UI will update via optimistic updates in the dialog
    return { success: true };
  } catch (error: any) {
    console.error('Error creating share request:', error);
    throw error;
  }
}

// Get pending share requests for current user
export async function getPendingShareRequests() {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    const pendingRequests = await prisma.noteShare.findMany({
      where: {
        userId: session.user.id,
        status: 'pending'
      },
      include: {
        note: {
          select: {
            id: true,
            title: true,
            content: true,
            color: true,
            createdAt: true
          }
        },
        sharer: {
          select: {
            id: true,
            name: true,
            email: true,
            image: true
          }
        }
      },
      orderBy: {
        createdAt: 'desc'
      }
    });

    return pendingRequests;
  } catch (error: any) {
    console.error('Error fetching pending share requests:', error);
    throw error;
  }
}

// Respond to share request (accept/decline)
export async function respondToShareRequest(shareId: string, action: 'accept' | 'decline') {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    const share = await prisma.noteShare.findUnique({
      where: { id: shareId },
      include: {
        note: true,
        sharer: true
      }
    });

    if (!share) {
      throw new Error('Share request not found');
    }

    // Verify this share belongs to current user
    if (share.userId !== session.user.id) {
      throw new Error('Unauthorized');
    }

    // Update share status
    const updatedShare = await prisma.noteShare.update({
      where: { id: shareId },
      data: {
        status: action === 'accept' ? 'accepted' : 'declined',
        respondedAt: new Date()
      },
      include: {
        note: {
          select: {
            title: true
          }
        }
      }
    });

    // Revalidate all relevant cache tags
    revalidatePath('/');

    return { success: true, share: updatedShare };
  } catch (error: any) {
    console.error('Error responding to share request:', error);
    throw error;
  }
}

// Get all accepted shares for current user (notes shared with me)
export async function getAcceptedSharedNotes() {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    const acceptedShares = await prisma.noteShare.findMany({
      where: {
        userId: session.user.id,
        status: 'accepted'
      },
      include: {
        note: true
      },
      orderBy: {
        createdAt: 'desc'
      }
    });

    return acceptedShares.map(share => share.note);
  } catch (error: any) {
    console.error('Error fetching accepted shared notes:', error);
    throw error;
  }
}

// Remove share from my view (hide the note)
export async function removeSharedNoteFromView(shareId: string) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    const share = await prisma.noteShare.findUnique({
      where: { id: shareId }
    });

    if (!share) {
      throw new Error('Share not found');
    }

    // Verify this share belongs to current user
    if (share.userId !== session.user.id) {
      throw new Error('Unauthorized');
    }

    // Update status to 'removed' (note is hidden from user's view)
    await prisma.noteShare.update({
      where: { id: shareId },
      data: {
        status: 'removed'
      }
    });

    revalidatePath('/');
    return { success: true };
  } catch (error: any) {
    console.error('Error removing shared note from view:', error);
    throw error;
  }
}

// Get notification count for pending shares
export async function getPendingShareCount() {
  const session = await auth();
  if (!session?.user?.id) return 0;

  try {
    const count = await prisma.noteShare.count({
      where: {
        userId: session.user.id,
        status: 'pending'
      }
    });

    return count;
  } catch (error) {
    console.error('Error getting pending share count:', error);
    return 0;
  }
}

// Leave a shared note (remove from my list)
export async function leaveSharedNote(noteId: string) {
  const session = await auth();
  if (!session?.user?.id) throw new Error('Unauthorized');

  try {
    // Find the NoteShare record for this user and note
    const share = await prisma.noteShare.findUnique({
      where: {
        noteId_userId: {
          noteId,
          userId: session.user.id
        }
      }
    });

    if (!share) {
      throw new Error('Share not found');
    }

    // Verify this share belongs to current user
    if (share.userId !== session.user.id) {
      throw new Error('Unauthorized');
    }

    // Update status to 'removed' (note is hidden from user's view)
    await prisma.noteShare.update({
      where: { id: share.id },
      data: {
        status: 'removed'
      }
    });

    revalidatePath('/');
    return { success: true };
  } catch (error: any) {
    console.error('Error leaving shared note:', error);
    throw error;
  }
}
