const BASE = () => process.env.KANBOARD_URL || 'http://localhost:4800';
const BOT_USER = () => process.env.KANBOARD_BOT_USER || 'claude';

let _token = null;
let _botId = null;

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

function token() {
  if (!_token) throw new Error('Kanboard not initialized');
  return _token;
}

// JSON-RPC over the app-wide token (login "jsonrpc:<token>") — full access to all projects.
async function rpc(method, params = {}) {
  const auth = Buffer.from(`jsonrpc:${token()}`).toString('base64');
  const res = await fetch(`${BASE()}/jsonrpc.php`, {
    method: 'POST',
    headers: { Authorization: `Basic ${auth}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', method, id: 1, params }),
  });
  if (!res.ok) throw new Error(`Kanboard ${method}: ${res.status} ${await res.text()}`);
  const json = await res.json();
  if (json.error) throw new Error(`Kanboard ${method}: ${json.error.message || JSON.stringify(json.error)}`);
  return json.result;
}

// Resolve + cache the bot user's id (default author for writes, filter for "my tasks").
async function botUserId() {
  if (_botId != null) return _botId;
  const user = await rpc('getUserByName', { username: BOT_USER() });
  if (!user || !user.id) throw new Error(`Bot user not found in Kanboard: ${BOT_USER()}`);
  _botId = parseInt(user.id);
  return _botId;
}

const STATUS = { open: 1, closed: 0 };

// --- Read / monitor ---

export async function kbListProjects() {
  const projects = await rpc('getAllProjects');
  return projects.map(p => ({
    id: parseInt(p.id), name: p.name, is_active: parseInt(p.is_active),
    is_private: parseInt(p.is_private), owner_id: parseInt(p.owner_id),
  }));
}

export async function kbGetProject(projectId) {
  const [project, columns, swimlanes] = await Promise.all([
    rpc('getProjectById', { project_id: projectId }),
    rpc('getColumns', { project_id: projectId }),
    rpc('getAllSwimlanes', { project_id: projectId }),
  ]);
  if (!project) throw new Error(`Project not found: ${projectId}`);
  return {
    id: parseInt(project.id), name: project.name, description: project.description,
    is_active: parseInt(project.is_active), owner_id: parseInt(project.owner_id),
    columns: columns.map(c => ({ id: parseInt(c.id), title: c.title, position: parseInt(c.position) })),
    swimlanes: swimlanes.map(s => ({ id: parseInt(s.id), name: s.name, is_active: parseInt(s.is_active) })),
  };
}

function compactTask(t) {
  return {
    id: parseInt(t.id), title: t.title, project_id: parseInt(t.project_id),
    column_id: parseInt(t.column_id), swimlane_id: parseInt(t.swimlane_id),
    owner_id: parseInt(t.owner_id), is_active: parseInt(t.is_active),
    color_id: t.color_id, priority: parseInt(t.priority || 0),
    date_due: parseInt(t.date_due) || 0, reference: t.reference || '',
  };
}

export async function kbListTasks(projectId, status = 'open') {
  let tasks = [];
  if (status === 'all') {
    const [open, closed] = await Promise.all([
      rpc('getAllTasks', { project_id: projectId, status_id: STATUS.open }),
      rpc('getAllTasks', { project_id: projectId, status_id: STATUS.closed }),
    ]);
    tasks = [...open, ...closed];
  } else {
    tasks = await rpc('getAllTasks', { project_id: projectId, status_id: STATUS[status] ?? STATUS.open });
  }
  return tasks.map(compactTask);
}

// Tasks assigned to the bot across every project. status: open | closed | all
export async function kbMyTasks(status = 'open') {
  const botId = await botUserId();
  const projects = await rpc('getAllProjects');
  const out = [];
  for (const p of projects) {
    const tasks = await kbListTasks(parseInt(p.id), status);
    for (const t of tasks) {
      if (t.owner_id === botId) out.push({ ...t, project_name: p.name });
    }
  }
  return out;
}

export async function kbGetTask(taskId) {
  const task = await rpc('getTask', { task_id: taskId });
  if (!task) throw new Error(`Task not found: ${taskId}`);
  const [subtasks, comments] = await Promise.all([
    rpc('getAllSubtasks', { task_id: taskId }),
    rpc('getAllComments', { task_id: taskId }),
  ]);
  return {
    task,
    subtasks: (subtasks || []).map(s => ({
      id: parseInt(s.id), title: s.title, status: parseInt(s.status),
      status_name: s.status_name, user_id: parseInt(s.user_id) || 0, assignee: s.name || s.username || null,
    })),
    comments: (comments || []).map(c => ({
      id: parseInt(c.id), user_id: parseInt(c.user_id), author: c.name || c.username,
      date: parseInt(c.date_creation), content: c.comment,
    })),
  };
}

export async function kbSearchTasks(projectId, query) {
  const tasks = await rpc('searchTasks', { project_id: projectId, query });
  return (tasks || []).map(compactTask);
}

export async function kbListUsers() {
  const users = await rpc('getAllUsers');
  return users.map(u => ({
    id: parseInt(u.id), username: u.username, name: u.name,
    role: u.role, is_active: parseInt(u.is_active), email: u.email,
  }));
}

export async function kbProjectActivity(projectId) {
  return rpc('getProjectActivity', { project_id: projectId });
}

// --- Write (creation/comments authored by the bot user) ---

export async function kbCreateTask(params) {
  const body = { ...params };
  if (body.creator_id == null) body.creator_id = await botUserId();
  const id = await rpc('createTask', body);
  if (!id) throw new Error('createTask returned false (check required fields: title, project_id)');
  return { task_id: parseInt(id) };
}

export async function kbUpdateTask(params) {
  const ok = await rpc('updateTask', params);
  return { updated: !!ok };
}

// Move a card. project_id and swimlane_id are resolved from the task when omitted.
export async function kbMoveTask({ task_id, column_id, position = 1, swimlane_id, project_id }) {
  if (project_id == null || swimlane_id == null) {
    const task = await rpc('getTask', { task_id });
    if (!task) throw new Error(`Task not found: ${task_id}`);
    if (project_id == null) project_id = parseInt(task.project_id);
    if (swimlane_id == null) swimlane_id = parseInt(task.swimlane_id);
  }
  const ok = await rpc('moveTaskPosition', { project_id, task_id, column_id, position, swimlane_id });
  return { moved: !!ok };
}

export async function kbChangeTaskStatus(taskId, action) {
  const method = action === 'close' ? 'closeTask' : 'openTask';
  const ok = await rpc(method, { task_id: taskId });
  return { [action === 'close' ? 'closed' : 'opened']: !!ok };
}

// Assign a task to a user (username or numeric id).
export async function kbAssignTask(taskId, owner) {
  let ownerId = owner;
  if (typeof owner === 'string' && !/^\d+$/.test(owner)) {
    const user = await rpc('getUserByName', { username: owner });
    if (!user || !user.id) throw new Error(`User not found: ${owner}`);
    ownerId = parseInt(user.id);
  }
  const ok = await rpc('updateTask', { id: taskId, owner_id: parseInt(ownerId) });
  return { assigned: !!ok, owner_id: parseInt(ownerId) };
}

export async function kbAddComment(taskId, content, userId) {
  const uid = userId != null ? userId : await botUserId();
  const id = await rpc('createComment', { task_id: taskId, user_id: uid, content });
  if (!id) throw new Error('createComment returned false');
  return { comment_id: parseInt(id) };
}

export async function kbCreateSubtask(params) {
  const body = { ...params };
  if (body.user_id == null) body.user_id = await botUserId();
  const id = await rpc('createSubtask', body);
  if (!id) throw new Error('createSubtask returned false (required: task_id, title)');
  return { subtask_id: parseInt(id) };
}

export async function kbUpdateSubtask(params) {
  const ok = await rpc('updateSubtask', params);
  return { updated: !!ok };
}

export async function kbRemoveTask(taskId) {
  const ok = await rpc('removeTask', { task_id: taskId });
  return { removed: !!ok };
}

export async function kbRemoveComment(commentId) {
  const ok = await rpc('removeComment', { comment_id: commentId });
  return { removed: !!ok };
}
