// Clipboard history store for managing copied code snippets
// Implements issue #25 - Clipboard History feature

import { invoke } from "@tauri-apps/api/core";
import { writable, derived } from "svelte/store";

export interface ClipboardEntry {
  id: string;
  content: string;
  language: string | null;
  source: string | null;
  timestamp: string;
  is_pinned: boolean;
}

// Create stores
const entriesStore = writable<ClipboardEntry[]>([]);
const searchQueryStore = writable<string>("");
const languageFilterStore = writable<string | null>(null);
const isLoadingStore = writable<boolean>(false);

// Derived store for filtered entries
const filteredEntriesStore = derived(
  [entriesStore, searchQueryStore, languageFilterStore],
  ([$entries, $searchQuery, $languageFilter]) => {
    let filtered = $entries;

    // Filter by language
    if ($languageFilter) {
      filtered = filtered.filter((e) => e.language === $languageFilter);
    }

    // Filter by search query
    if ($searchQuery) {
      const query = $searchQuery.toLowerCase();
      filtered = filtered.filter(
        (e) =>
          e.content.toLowerCase().includes(query) ||
          e.language?.toLowerCase().includes(query) ||
          e.source?.toLowerCase().includes(query)
      );
    }

    return filtered;
  }
);

// Derived store for unique languages
const languagesStore = derived(entriesStore, ($entries) => {
  const languages = new Set<string>();
  for (const entry of $entries) {
    if (entry.language) {
      languages.add(entry.language);
    }
  }
  return Array.from(languages).sort();
});

// Helper function to detect language from content
function detectLanguage(content: string): string | null {
  // Common language patterns
  const patterns: [RegExp, string][] = [
    [/^(import|export|const|let|var|function|class|interface|type)\s/m, "typescript"],
    [/^(def|class|import|from|if __name__|async def)\s/m, "python"],
    [/^(fn|let|mut|impl|struct|enum|use|mod|pub)\s/m, "rust"],
    [/^(package|import|func|type|var|const)\s/m, "go"],
    [/<\?php/m, "php"],
    [/^(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP)\s/im, "sql"],
    [/^<!DOCTYPE|^<html|^<div|^<span/im, "html"],
    [/^\s*\{[\s\S]*"[\w-]+":/m, "json"],
    [/^---\s*\n/m, "yaml"],
    [/^\s*#\s*(include|define|ifdef)/m, "c"],
    [/^(public|private|protected)\s+(class|interface|static)/m, "java"],
    [/^\$[\w_]+\s*=/m, "bash"],
  ];

  for (const [pattern, lang] of patterns) {
    if (pattern.test(content)) {
      return lang;
    }
  }

  return null;
}

// Store actions
async function loadEntries(): Promise<void> {
  isLoadingStore.set(true);
  try {
    const entries = await invoke<ClipboardEntry[]>("list_clipboard_entries", {
      language: null,
    });
    entriesStore.set(entries);
  } catch (error) {
    console.error("Failed to load clipboard entries:", error);
  } finally {
    isLoadingStore.set(false);
  }
}

async function captureClipboard(
  content: string,
  language?: string | null,
  source?: string | null
): Promise<ClipboardEntry | null> {
  try {
    // Auto-detect language if not provided
    const detectedLanguage = language ?? detectLanguage(content);

    const entry = await invoke<ClipboardEntry>("capture_clipboard", {
      content,
      language: detectedLanguage,
      source: source ?? null,
    });

    // Reload entries to get the updated list
    await loadEntries();

    return entry;
  } catch (error) {
    console.error("Failed to capture clipboard:", error);
    return null;
  }
}

async function deleteEntry(id: string): Promise<void> {
  try {
    await invoke("delete_clipboard_entry", { id });
    entriesStore.update((entries) => entries.filter((e) => e.id !== id));
  } catch (error) {
    console.error("Failed to delete clipboard entry:", error);
  }
}

async function togglePin(id: string): Promise<void> {
  try {
    const updated = await invoke<ClipboardEntry>("toggle_pin_clipboard_entry", { id });
    entriesStore.update((entries) =>
      entries
        .map((e) => (e.id === id ? updated : e))
        .sort((a, b) => {
          // Pinned first, then by timestamp
          if (a.is_pinned && !b.is_pinned) return -1;
          if (!a.is_pinned && b.is_pinned) return 1;
          return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
        })
    );
  } catch (error) {
    console.error("Failed to toggle pin:", error);
  }
}

async function clearHistory(): Promise<void> {
  try {
    await invoke("clear_clipboard_history");
    entriesStore.update((entries) => entries.filter((e) => e.is_pinned));
  } catch (error) {
    console.error("Failed to clear clipboard history:", error);
  }
}

async function updateLanguage(id: string, language: string | null): Promise<void> {
  try {
    const updated = await invoke<ClipboardEntry>("update_clipboard_language", {
      id,
      language,
    });
    entriesStore.update((entries) => entries.map((e) => (e.id === id ? updated : e)));
  } catch (error) {
    console.error("Failed to update language:", error);
  }
}

function setSearchQuery(query: string): void {
  searchQueryStore.set(query);
}

function setLanguageFilter(language: string | null): void {
  languageFilterStore.set(language);
}

// Copy entry content to clipboard
async function copyToClipboard(content: string): Promise<boolean> {
  try {
    await navigator.clipboard.writeText(content);
    return true;
  } catch (error) {
    console.error("Failed to copy to clipboard:", error);
    return false;
  }
}

// Format timestamp for display
function formatTimestamp(timestamp: string): string {
  const date = new Date(timestamp);
  const now = new Date();
  const diffMs = now.getTime() - date.getTime();
  const diffMins = Math.floor(diffMs / 60000);
  const diffHours = Math.floor(diffMs / 3600000);
  const diffDays = Math.floor(diffMs / 86400000);

  if (diffMins < 1) return "Just now";
  if (diffMins < 60) return `${diffMins}m ago`;
  if (diffHours < 24) return `${diffHours}h ago`;
  if (diffDays < 7) return `${diffDays}d ago`;

  return date.toLocaleDateString();
}

// Export the store
export const clipboardStore = {
  subscribe: entriesStore.subscribe,
  entries: entriesStore,
  filteredEntries: filteredEntriesStore,
  languages: languagesStore,
  searchQuery: searchQueryStore,
  languageFilter: languageFilterStore,
  isLoading: isLoadingStore,
  loadEntries,
  captureClipboard,
  deleteEntry,
  togglePin,
  clearHistory,
  updateLanguage,
  setSearchQuery,
  setLanguageFilter,
  copyToClipboard,
  formatTimestamp,
  detectLanguage,
};
