import { openDB, type DBSchema, type IDBPDatabase } from 'idb';
import type {
  Agent,
  Conversation,
  Message,
  Orchestration,
  Project,
  QuickAction,
  ScheduledTask,
} from './types';
import { BUILTIN_QUICK_ACTIONS } from './quick-actions';
import { BUILTIN_AGENTS } from './agents';

interface TaskRunRecord {
  id?: number;
  taskId: string;
  timestamp: number;
  durationMs: number;
  success: boolean;
  result: string;
}

interface ModelRatingRecord {
  id?: number;
  model: string;
  taskType: string;
  rating: 'up' | 'down';
  timestamp: number;
}

interface WorkspaceDB extends DBSchema {
  conversations: {
    key: string;
    value: Conversation;
    indexes: {
      'by-updated-at': number;
      'by-project-id': string;
      'by-archived': number;
    };
  };
  messages: {
    key: string;
    value: Message;
    indexes: {
      'by-conversation-ts': [string, number];
      'by-conversation-parent': [string, string];
    };
  };
  agents: {
    key: string;
    value: Agent;
  };
  quickActions: {
    key: string;
    value: QuickAction;
    indexes: {
      'by-category': string;
      'by-usage-count': number;
    };
  };
  projects: {
    key: string;
    value: Project;
  };
  scheduledTasks: {
    key: string;
    value: ScheduledTask;
  };
  taskRuns: {
    key: number;
    value: TaskRunRecord;
    indexes: {
      'by-task-ts': [string, number];
    };
  };
  modelRatings: {
    key: number;
    value: ModelRatingRecord;
    indexes: {
      'by-model-task': [string, string];
    };
  };
  orchestrations: {
    key: string;
    value: Orchestration;
  };
}

const DB_NAME = 'llm-workspace';
const DB_VERSION = 1;

let dbPromise: Promise<IDBPDatabase<WorkspaceDB>> | null = null;

export function getDb(): Promise<IDBPDatabase<WorkspaceDB>> {
  if (dbPromise) return dbPromise;

  dbPromise = openDB<WorkspaceDB>(DB_NAME, DB_VERSION, {
    upgrade(db) {
      const conversations = db.createObjectStore('conversations', { keyPath: 'id' });
      conversations.createIndex('by-updated-at', 'updatedAt');
      conversations.createIndex('by-project-id', 'projectId');
      conversations.createIndex('by-archived', 'archived');

      const messages = db.createObjectStore('messages', { keyPath: 'id' });
      messages.createIndex('by-conversation-ts', ['conversationId', 'timestamp']);
      messages.createIndex('by-conversation-parent', ['conversationId', 'parentId']);

      db.createObjectStore('agents', { keyPath: 'id' });

      const quickActions = db.createObjectStore('quickActions', { keyPath: 'id' });
      quickActions.createIndex('by-category', 'category');
      quickActions.createIndex('by-usage-count', 'usageCount');

      db.createObjectStore('projects', { keyPath: 'id' });
      db.createObjectStore('scheduledTasks', { keyPath: 'id' });

      const taskRuns = db.createObjectStore('taskRuns', { keyPath: 'id', autoIncrement: true });
      taskRuns.createIndex('by-task-ts', ['taskId', 'timestamp']);

      const modelRatings = db.createObjectStore('modelRatings', {
        keyPath: 'id',
        autoIncrement: true,
      });
      modelRatings.createIndex('by-model-task', ['model', 'taskType']);

      db.createObjectStore('orchestrations', { keyPath: 'id' });
    },
  });

  return dbPromise;
}

export async function addConversation(conversation: Conversation): Promise<void> {
  const db = await getDb();
  await db.put('conversations', conversation);
}

export async function getConversation(id: string): Promise<Conversation | undefined> {
  const db = await getDb();
  return db.get('conversations', id);
}

export async function updateConversation(
  id: string,
  partial: Partial<Conversation>
): Promise<Conversation | null> {
  const db = await getDb();
  const existing = await db.get('conversations', id);
  if (!existing) return null;
  const updated: Conversation = { ...existing, ...partial, id };
  await db.put('conversations', updated);
  return updated;
}

export async function deleteConversation(id: string): Promise<void> {
  const db = await getDb();
  await db.delete('conversations', id);
}

export async function listConversations(opts?: {
  archived?: boolean;
  projectId?: string;
  limit?: number;
  offset?: number;
}): Promise<Conversation[]> {
  const db = await getDb();
  let rows = await db.getAll('conversations');

  if (typeof opts?.archived === 'boolean') {
    rows = rows.filter(row => row.archived === opts.archived);
  }
  if (typeof opts?.projectId === 'string' && opts.projectId.length > 0) {
    rows = rows.filter(row => row.projectId === opts.projectId);
  }

  rows.sort((a, b) => b.updatedAt - a.updatedAt);

  const offset = opts?.offset ?? 0;
  const limit = opts?.limit ?? rows.length;
  return rows.slice(offset, offset + limit);
}

export async function addMessage(message: Message): Promise<void> {
  const db = await getDb();
  await db.put('messages', message);
}

export async function getMessages(
  conversationId: string,
  opts?: { limit?: number; before?: number }
): Promise<Message[]> {
  const db = await getDb();
  let rows = await db.getAllFromIndex(
    'messages',
    'by-conversation-ts',
    IDBKeyRange.bound([conversationId, 0], [conversationId, Number.MAX_SAFE_INTEGER])
  );

  if (typeof opts?.before === 'number') {
    rows = rows.filter(row => row.timestamp < opts.before!);
  }

  rows.sort((a, b) => b.timestamp - a.timestamp);
  const limited = typeof opts?.limit === 'number' ? rows.slice(0, opts.limit) : rows;
  return limited.sort((a, b) => a.timestamp - b.timestamp);
}

export async function updateMessage(
  id: string,
  partial: Partial<Message>
): Promise<Message | null> {
  const db = await getDb();
  const existing = await db.get('messages', id);
  if (!existing) return null;
  const updated: Message = { ...existing, ...partial, id };
  await db.put('messages', updated);
  return updated;
}

export async function deleteMessagesByConversation(conversationId: string): Promise<void> {
  const db = await getDb();
  const tx = db.transaction('messages', 'readwrite');
  const index = tx.store.index('by-conversation-ts');
  let cursor = await index.openCursor(
    IDBKeyRange.bound([conversationId, 0], [conversationId, Number.MAX_SAFE_INTEGER])
  );
  while (cursor) {
    await cursor.delete();
    cursor = await cursor.continue();
  }
  await tx.done;
}

export async function countMessages(conversationId: string): Promise<number> {
  const db = await getDb();
  const range = IDBKeyRange.bound([conversationId, 0], [conversationId, Number.MAX_SAFE_INTEGER]);
  return db.countFromIndex('messages', 'by-conversation-ts', range);
}

export async function addQuickAction(action: QuickAction): Promise<void> {
  const db = await getDb();
  await db.put('quickActions', action);
}

export async function getQuickAction(id: string): Promise<QuickAction | undefined> {
  const db = await getDb();
  return db.get('quickActions', id);
}

export async function listQuickActions(category?: QuickAction['category']): Promise<QuickAction[]> {
  const db = await getDb();
  let rows = await db.getAll('quickActions');
  if (category) {
    rows = rows.filter(row => row.category === category);
  }
  rows.sort((a, b) => {
    if (a.builtin !== b.builtin) return a.builtin ? -1 : 1;
    return b.usageCount - a.usageCount;
  });
  return rows;
}

export async function updateQuickAction(
  id: string,
  partial: Partial<QuickAction>
): Promise<QuickAction | null> {
  const db = await getDb();
  const existing = await db.get('quickActions', id);
  if (!existing) return null;
  const updated: QuickAction = { ...existing, ...partial, id };
  await db.put('quickActions', updated);
  return updated;
}

export async function deleteQuickAction(id: string): Promise<void> {
  const db = await getDb();
  await db.delete('quickActions', id);
}

export async function seedBuiltinQuickActions(): Promise<number> {
  const seeded = typeof window !== 'undefined' ? localStorage.getItem('llm-qa-seeded') : 'true';
  if (seeded === 'true') return 0;

  const db = await getDb();
  const tx = db.transaction('quickActions', 'readwrite');
  let inserted = 0;

  for (const action of BUILTIN_QUICK_ACTIONS) {
    const existing = await tx.store.get(action.id);
    if (!existing) {
      await tx.store.put(action);
      inserted += 1;
    }
  }

  await tx.done;
  if (typeof window !== 'undefined') {
    localStorage.setItem('llm-qa-seeded', 'true');
  }
  return inserted;
}

export async function exportQuickActions(): Promise<QuickAction[]> {
  const rows = await listQuickActions();
  return rows.filter(row => !row.builtin);
}

export async function importQuickActions(actions: QuickAction[]): Promise<number> {
  const db = await getDb();
  let imported = 0;
  for (const action of actions) {
    const existing = await db.get('quickActions', action.id);
    if (existing) continue;
    await db.put('quickActions', action);
    imported += 1;
  }
  return imported;
}

export async function addAgent(agent: Agent): Promise<void> {
  const db = await getDb();
  await db.put('agents', agent);
}

export async function getAgent(id: string): Promise<Agent | undefined> {
  const db = await getDb();
  return db.get('agents', id);
}

export async function listAgents(): Promise<Agent[]> {
  const db = await getDb();
  const rows = await db.getAll('agents');
  rows.sort((a, b) => {
    if (a.builtin !== b.builtin) return a.builtin ? -1 : 1;
    return b.conversationCount - a.conversationCount;
  });
  return rows;
}

export async function updateAgent(id: string, partial: Partial<Agent>): Promise<Agent | null> {
  const db = await getDb();
  const existing = await db.get('agents', id);
  if (!existing) return null;
  const updated: Agent = { ...existing, ...partial, id };
  await db.put('agents', updated);
  return updated;
}

export async function deleteAgent(id: string): Promise<void> {
  const db = await getDb();
  await db.delete('agents', id);
}

export async function seedBuiltinAgents(): Promise<number> {
  const seeded = typeof window !== 'undefined' ? localStorage.getItem('llm-agents-seeded') : 'true';
  if (seeded === 'true') return 0;

  const db = await getDb();
  const tx = db.transaction('agents', 'readwrite');
  let inserted = 0;
  for (const item of BUILTIN_AGENTS) {
    const existing = await tx.store.get(item.id);
    if (!existing) {
      await tx.store.put(item);
      inserted += 1;
    }
  }
  await tx.done;
  if (typeof window !== 'undefined') {
    localStorage.setItem('llm-agents-seeded', 'true');
  }
  return inserted;
}

export async function exportAgents(): Promise<Agent[]> {
  const rows = await listAgents();
  return rows.filter(row => !row.builtin);
}

export async function importAgents(agents: Agent[]): Promise<number> {
  const db = await getDb();
  let imported = 0;
  for (const agent of agents) {
    const existing = await db.get('agents', agent.id);
    if (existing) continue;
    await db.put('agents', agent);
    imported += 1;
  }
  return imported;
}

export async function addScheduledTask(task: ScheduledTask): Promise<void> {
  const db = await getDb();
  await db.put('scheduledTasks', task);
}

export async function getScheduledTask(id: string): Promise<ScheduledTask | undefined> {
  const db = await getDb();
  return db.get('scheduledTasks', id);
}

export async function listScheduledTasks(): Promise<ScheduledTask[]> {
  const db = await getDb();
  const rows = await db.getAll('scheduledTasks');
  rows.sort((a, b) => b.createdAt - a.createdAt);
  return rows;
}

export async function updateScheduledTask(
  id: string,
  partial: Partial<ScheduledTask>
): Promise<ScheduledTask | null> {
  const db = await getDb();
  const existing = await db.get('scheduledTasks', id);
  if (!existing) return null;
  const updated: ScheduledTask = { ...existing, ...partial, id };
  await db.put('scheduledTasks', updated);
  return updated;
}

export async function deleteScheduledTask(id: string): Promise<void> {
  const db = await getDb();
  await db.delete('scheduledTasks', id);
}

export async function addProject(project: Project): Promise<void> {
  const db = await getDb();
  await db.put('projects', project);
}

export async function getProject(id: string): Promise<Project | undefined> {
  const db = await getDb();
  return db.get('projects', id);
}

export async function listProjects(): Promise<Project[]> {
  const db = await getDb();
  const rows = await db.getAll('projects');
  rows.sort((a, b) => {
    if (a.pinned !== b.pinned) return a.pinned ? -1 : 1;
    return b.createdAt - a.createdAt;
  });
  return rows;
}

export async function updateProject(
  id: string,
  partial: Partial<Project>
): Promise<Project | null> {
  const db = await getDb();
  const existing = await db.get('projects', id);
  if (!existing) return null;
  const updated: Project = { ...existing, ...partial, id };
  await db.put('projects', updated);
  return updated;
}

export async function deleteProject(id: string): Promise<void> {
  const db = await getDb();
  await db.delete('projects', id);
}

export async function addOrchestration(orch: Orchestration): Promise<void> {
  const db = await getDb();
  await db.put('orchestrations', orch);
}

export async function getOrchestration(id: string): Promise<Orchestration | undefined> {
  const db = await getDb();
  return db.get('orchestrations', id);
}

export async function listOrchestrations(): Promise<Orchestration[]> {
  const db = await getDb();
  return db.getAll('orchestrations');
}

export async function updateOrchestration(
  id: string,
  partial: Partial<Orchestration>
): Promise<Orchestration | null> {
  const db = await getDb();
  const existing = await db.get('orchestrations', id);
  if (!existing) return null;
  const updated: Orchestration = { ...existing, ...partial, id };
  await db.put('orchestrations', updated);
  return updated;
}

export async function deleteOrchestration(id: string): Promise<void> {
  const db = await getDb();
  await db.delete('orchestrations', id);
}
