// Todoist tools for agap-mcp (Adolf + Claude). Uses the unified Todoist API v1
// (https://api.todoist.com/api/v1) — REST v2 / Sync v9 are deprecated (410).
// Bearer auth with a personal API token (Vaultwarden: TODOIST_TOKEN), injected
// at init like the other services. Deliberately a SMALL, curated tool surface:
// every tool schema here is re-sent to Kimi on every Adolf turn (kb#101), so we
// expose only the essentials and return compact task objects.

const BASE = 'https://api.todoist.com/api/v1';

let _token = null;

export function initTodoist(token) {
  _token = token;
  console.log('Todoist: ready');
}

async function api(method, path, body) {
  if (!_token) throw new Error('Todoist not initialized');
  const res = await fetch(`${BASE}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${_token}`,
      ...(body ? { 'Content-Type': 'application/json' } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const text = await res.text();
  if (!res.ok) throw new Error(`Todoist ${method} ${path}: ${res.status} ${text.slice(0, 300)}`);
  return text ? JSON.parse(text) : null;
}

// Keep tool output (and thus Kimi context) small — return only fields an agent
// needs to reason about or act on, not the full Todoist task object.
function slimTask(t) {
  if (!t || typeof t !== 'object') return t;
  return {
    id: t.id,
    content: t.content,
    project_id: t.project_id,
    priority: t.priority, // API 1=normal .. 4=urgent (inverse of the Todoist UI's p1..p4)
    due: t.due?.string || t.due?.date || null,
    labels: t.labels && t.labels.length ? t.labels : undefined,
  };
}

function results(data) {
  if (Array.isArray(data?.results)) return data.results;
  return Array.isArray(data) ? data : [];
}

// List tasks — either a Todoist filter query (e.g. "today", "overdue",
// "#Work & p1") via the /tasks/filter endpoint, or all active tasks optionally
// scoped to one project.
export async function todoistListTasks({ query, project_id } = {}) {
  let path;
  if (query && query.trim()) {
    path = `/tasks/filter?query=${encodeURIComponent(query.trim())}`;
  } else if (project_id) {
    path = `/tasks?project_id=${encodeURIComponent(project_id)}`;
  } else {
    path = '/tasks';
  }
  return results(await api('GET', path)).map(slimTask);
}

export async function todoistListProjects() {
  return results(await api('GET', '/projects')).map((p) => ({
    id: p.id,
    name: p.name,
    is_favorite: p.is_favorite || undefined,
    is_inbox: p.inbox_project || p.is_inbox_project || undefined,
  }));
}

export async function todoistCreateTask({ content, description, due_string, priority, project_id, labels } = {}) {
  if (!content || !content.trim()) throw new Error('content is required');
  const body = { content: content.trim() };
  if (description) body.description = description;
  if (due_string) body.due_string = due_string;
  if (priority) body.priority = priority;
  if (project_id) body.project_id = project_id;
  if (Array.isArray(labels) && labels.length) body.labels = labels;
  return slimTask(await api('POST', '/tasks', body));
}

export async function todoistUpdateTask({ id, content, description, due_string, priority, labels } = {}) {
  if (!id) throw new Error('id is required');
  const body = {};
  if (content !== undefined) body.content = content;
  if (description !== undefined) body.description = description;
  if (due_string !== undefined) body.due_string = due_string;
  if (priority !== undefined) body.priority = priority;
  if (labels !== undefined) body.labels = labels;
  return slimTask(await api('POST', `/tasks/${encodeURIComponent(id)}`, body));
}

export async function todoistCompleteTask({ id } = {}) {
  if (!id) throw new Error('id is required');
  await api('POST', `/tasks/${encodeURIComponent(id)}/close`);
  return { id, completed: true };
}
