import { Database } from 'bun:sqlite';
import {
  DEFAULT_TASK_CONTEXT_MODE,
  WATCH_CI_PROMPT_PREFIX,
} from 'ejclaw-runners-shared';

import {
  AgentType,
  PairedRoomRole,
  ScheduledTask,
  TaskRunLog,
} from '../types.js';

export type CreateScheduledTaskInput = Omit<
  ScheduledTask,
  | 'last_run'
  | 'last_result'
  | 'agent_type'
  | 'room_role'
  | 'ci_provider'
  | 'ci_metadata'
  | 'max_duration_ms'
  | 'status_message_id'
  | 'status_started_at'
> & {
  agent_type?: AgentType | null;
  room_role?: PairedRoomRole | null;
  ci_provider?: ScheduledTask['ci_provider'];
  ci_metadata?: string | null;
  max_duration_ms?: number | null;
  status_message_id?: string | null;
  status_started_at?: string | null;
};

export type ScheduledTaskUpdates = Partial<
  Pick<
    ScheduledTask,
    | 'prompt'
    | 'schedule_type'
    | 'schedule_value'
    | 'next_run'
    | 'status'
    | 'suspended_until'
    | 'ci_metadata'
  >
>;

export type ScheduledTaskStatusTrackingUpdates = Partial<
  Pick<ScheduledTask, 'status_message_id' | 'status_started_at'>
>;

export function createTaskInDatabase(
  database: Database,
  task: CreateScheduledTaskInput,
): void {
  database
    .prepare(
      `
    INSERT INTO scheduled_tasks (id, group_folder, chat_jid, agent_type, room_role, ci_provider, ci_metadata, max_duration_ms, status_message_id, status_started_at, prompt, schedule_type, schedule_value, context_mode, next_run, status, created_at)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `,
    )
    .run(
      task.id,
      task.group_folder,
      task.chat_jid,
      task.agent_type || 'claude-code',
      task.room_role ?? null,
      task.ci_provider ?? null,
      task.ci_metadata ?? null,
      task.max_duration_ms ?? null,
      task.status_message_id || null,
      task.status_started_at || null,
      task.prompt,
      task.schedule_type,
      task.schedule_value,
      task.context_mode || DEFAULT_TASK_CONTEXT_MODE,
      task.next_run,
      task.status,
      task.created_at,
    );
}

export function getTaskByIdFromDatabase(
  database: Database,
  id: string,
): ScheduledTask | undefined {
  const row = database
    .prepare('SELECT * FROM scheduled_tasks WHERE id = ?')
    .get(id) as ScheduledTask | null | undefined;
  return row ?? undefined;
}

export function findDuplicateCiWatcherInDatabase(
  database: Database,
  chatJid: string,
  ciProvider: string,
  ciMetadata: string,
): ScheduledTask | undefined {
  return database
    .prepare(
      `SELECT * FROM scheduled_tasks
       WHERE chat_jid = ? AND ci_provider = ? AND ci_metadata = ?
         AND status IN ('active', 'paused')
       LIMIT 1`,
    )
    .get(chatJid, ciProvider, ciMetadata) as ScheduledTask | undefined;
}

export function getTasksForGroupFromDatabase(
  database: Database,
  groupFolder: string,
  agentType?: AgentType,
): ScheduledTask[] {
  if (agentType) {
    return database
      .prepare(
        'SELECT * FROM scheduled_tasks WHERE group_folder = ? AND agent_type = ? ORDER BY created_at DESC',
      )
      .all(groupFolder, agentType) as ScheduledTask[];
  }

  return database
    .prepare(
      'SELECT * FROM scheduled_tasks WHERE group_folder = ? ORDER BY created_at DESC',
    )
    .all(groupFolder) as ScheduledTask[];
}

export function getAllTasksFromDatabase(
  database: Database,
  agentType?: AgentType,
): ScheduledTask[] {
  if (agentType) {
    return database
      .prepare(
        'SELECT * FROM scheduled_tasks WHERE agent_type = ? ORDER BY created_at DESC',
      )
      .all(agentType) as ScheduledTask[];
  }

  return database
    .prepare('SELECT * FROM scheduled_tasks ORDER BY created_at DESC')
    .all() as ScheduledTask[];
}

export function updateTaskInDatabase(
  database: Database,
  id: string,
  updates: ScheduledTaskUpdates,
): void {
  const fields: string[] = [];
  const values: (string | number | null)[] = [];

  if (updates.prompt !== undefined) {
    fields.push('prompt = ?');
    values.push(updates.prompt);
  }
  if (updates.schedule_type !== undefined) {
    fields.push('schedule_type = ?');
    values.push(updates.schedule_type);
  }
  if (updates.schedule_value !== undefined) {
    fields.push('schedule_value = ?');
    values.push(updates.schedule_value);
  }
  if (updates.next_run !== undefined) {
    fields.push('next_run = ?');
    values.push(updates.next_run);
  }
  if (updates.status !== undefined) {
    fields.push('status = ?');
    values.push(updates.status);
  }
  if (updates.suspended_until !== undefined) {
    fields.push('suspended_until = ?');
    values.push(updates.suspended_until);
  }
  if (updates.ci_metadata !== undefined) {
    fields.push('ci_metadata = ?');
    values.push(updates.ci_metadata);
  }

  if (fields.length === 0) return;

  values.push(id);
  database
    .prepare(`UPDATE scheduled_tasks SET ${fields.join(', ')} WHERE id = ?`)
    .run(...values);
}

export function updateTaskStatusTrackingInDatabase(
  database: Database,
  id: string,
  updates: ScheduledTaskStatusTrackingUpdates,
): void {
  const fields: string[] = [];
  const values: (string | number | null)[] = [];

  if (updates.status_message_id !== undefined) {
    fields.push('status_message_id = ?');
    values.push(updates.status_message_id);
  }
  if (updates.status_started_at !== undefined) {
    fields.push('status_started_at = ?');
    values.push(updates.status_started_at);
  }

  if (fields.length === 0) return;

  values.push(id);
  database
    .prepare(`UPDATE scheduled_tasks SET ${fields.join(', ')} WHERE id = ?`)
    .run(...values);
}

export function hasActiveCiWatcherForChatInDatabase(
  database: Database,
  chatJid: string,
): boolean {
  const row = database
    .prepare(
      `SELECT 1 FROM scheduled_tasks
       WHERE chat_jid = ? AND status = 'active' AND prompt LIKE ?
       LIMIT 1`,
    )
    .get(chatJid, `${WATCH_CI_PROMPT_PREFIX}%`);
  return !!row;
}

export function getDueTasksFromDatabase(database: Database): ScheduledTask[] {
  const now = new Date().toISOString();
  return database
    .prepare(
      `
    SELECT * FROM scheduled_tasks
    WHERE status = 'active' AND next_run IS NOT NULL AND next_run <= ?
      AND (suspended_until IS NULL OR suspended_until <= ?)
    ORDER BY next_run
  `,
    )
    .all(now, now) as ScheduledTask[];
}

export function updateTaskAfterRunInDatabase(
  database: Database,
  id: string,
  nextRun: string | null,
  lastResult: string,
): void {
  const now = new Date().toISOString();
  database
    .prepare(
      `
    UPDATE scheduled_tasks
    SET next_run = ?, last_run = ?, last_result = ?, status = CASE WHEN ? IS NULL THEN 'completed' ELSE status END
    WHERE id = ?
  `,
    )
    .run(nextRun, now, lastResult, nextRun, id);
}

export function logTaskRunInDatabase(
  database: Database,
  log: TaskRunLog,
): void {
  database
    .prepare(
      `
    INSERT INTO task_run_logs (task_id, run_at, duration_ms, status, result, error)
    VALUES (?, ?, ?, ?, ?, ?)
  `,
    )
    .run(
      log.task_id,
      log.run_at,
      log.duration_ms,
      log.status,
      log.result,
      log.error,
    );
}

export function getRecentConsecutiveErrorsFromDatabase(
  database: Database,
  taskId: string,
  limit: number = 5,
): string[] {
  const rows = database
    .prepare(
      `SELECT status, error FROM task_run_logs
       WHERE task_id = ? ORDER BY run_at DESC LIMIT ?`,
    )
    .all(taskId, limit) as Array<{ status: string; error: string | null }>;

  const errors: string[] = [];
  for (const row of rows) {
    if (row.status !== 'error' || !row.error) break;
    errors.push(row.error);
  }
  return errors;
}
