import { google, type Auth } from 'googleapis';
import { readFileSync, existsSync } from 'fs';
import { resolve } from 'path';
import { homedir } from 'os';
import type { GtasksConfig } from '../../config/schema.js';
import type { Tool, ToolResult } from '../types.js';

/** Expand ~ to home directory. */
function expandPath(p: string): string {
  if (p.startsWith('~/') || p === '~') {
    return resolve(homedir(), p.slice(2));
  }
  return resolve(p);
}

/** Create an OAuth2 client from Google Tasks config (credentials + token files). */
function createOAuth2Client(config: NonNullable<GtasksConfig>): Auth.OAuth2Client {
  const credentialsPath = config.credentials_file;
  if (!credentialsPath) {
    throw new Error('No credentials_file configured. Set automation.gtasks.credentials_file in config.');
  }

  const expandedCredsPath = expandPath(credentialsPath);
  if (!existsSync(expandedCredsPath)) {
    throw new Error(`Credentials file not found: ${expandedCredsPath}`);
  }

  const credentials = JSON.parse(readFileSync(expandedCredsPath, 'utf-8'));
  const { client_id, client_secret, redirect_uris } = credentials.installed ?? credentials.web ?? {};

  if (!client_id || !client_secret) {
    throw new Error('Invalid credentials file — missing client_id or client_secret');
  }

  const oauth2Client = new google.auth.OAuth2(
    client_id,
    client_secret,
    redirect_uris?.[0] ?? 'http://localhost',
  );

  const tokenPath = expandPath(config.token_file ?? '~/.config/flynn/gtasks-token.json');
  if (!existsSync(tokenPath)) {
    throw new Error(`Token file not found: ${tokenPath}. Run "flynn gtasks-auth" to authenticate.`);
  }

  const token = JSON.parse(readFileSync(tokenPath, 'utf-8'));
  oauth2Client.setCredentials(token);

  return oauth2Client;
}

interface TaskListSummary {
  id: string;
  title: string;
  updated: string;
}

interface TaskSummary {
  id: string;
  title: string;
  status: string;
  due: string;
  notes: string;
  updated: string;
  parent: string;
}

/** Format task lists for tool output. */
function formatTaskLists(lists: TaskListSummary[]): string {
  if (lists.length === 0) {
    return 'No task lists found.';
  }

  return lists
    .map(l => `[${l.id}] ${l.title}\n  Updated: ${l.updated}`)
    .join('\n\n');
}

/** Format tasks for tool output. */
function formatTasks(tasks: TaskSummary[]): string {
  if (tasks.length === 0) {
    return 'No tasks found.';
  }

  return tasks
    .map(t => {
      const checkbox = t.status === 'completed' ? '[x]' : '[ ]';
      const parts = [`${checkbox} ${t.title}`];
      if (t.due) {parts.push(`  Due: ${t.due}`);}
      if (t.notes) {parts.push(`  Notes: ${t.notes}`);}
      parts.push(`  ID: ${t.id}`);
      return parts.join('\n');
    })
    .join('\n\n');
}

/**
 * Creates Google Tasks read-only tools bound to the given GtasksConfig.
 * Tools create their own OAuth2 client per invocation.
 */
export function createGtasksTools(config: NonNullable<GtasksConfig>): Tool[] {
  const tasksLists: Tool = {
    name: 'tasks.lists',
    description:
      'List all Google Tasks task lists. Returns id, title, and last updated time for each list.',
    requiredSecretScopes: ['gtasks'],
    inputSchema: {
      type: 'object',
      properties: {
        maxResults: {
          type: 'number',
          description: 'Maximum number of task lists to return (default: 20)',
        },
      },
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { maxResults?: number };
      const maxResults = args.maxResults ?? 20;

      try {
        const auth = createOAuth2Client(config);
        const tasks = google.tasks({ version: 'v1', auth });

        const response = await tasks.tasklists.list({
          maxResults,
        });

        const items = response.data.items ?? [];
        const lists: TaskListSummary[] = items.map(l => ({
          id: l.id ?? '',
          title: l.title ?? '(untitled)',
          updated: l.updated ?? '',
        }));

        return {
          success: true,
          output: formatTaskLists(lists),
        };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };

  const tasksList: Tool = {
    name: 'tasks.list',
    description:
      'List tasks from a Google Tasks task list. Returns title, status (completed/needsAction), due date, and notes. Use tasks.lists first to get task list IDs.',
    requiredSecretScopes: ['gtasks'],
    inputSchema: {
      type: 'object',
      properties: {
        taskListId: {
          type: 'string',
          description: 'Task list ID (from tasks.lists). Defaults to the primary "@default" list.',
        },
        maxResults: {
          type: 'number',
          description: 'Maximum number of tasks to return (default: 100)',
        },
        showCompleted: {
          type: 'boolean',
          description: 'Whether to include completed tasks (default: true)',
        },
        showHidden: {
          type: 'boolean',
          description: 'Whether to include hidden/deleted tasks (default: false)',
        },
      },
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { taskListId?: string; maxResults?: number; showCompleted?: boolean; showHidden?: boolean };
      const taskListId = args.taskListId ?? '@default';
      const maxResults = args.maxResults ?? 100;

      try {
        const auth = createOAuth2Client(config);
        const tasks = google.tasks({ version: 'v1', auth });

        const response = await tasks.tasks.list({
          tasklist: taskListId,
          maxResults,
          showCompleted: args.showCompleted ?? true,
          showHidden: args.showHidden ?? false,
        });

        const items = response.data.items ?? [];
        const taskList: TaskSummary[] = items.map(t => ({
          id: t.id ?? '',
          title: t.title ?? '(untitled)',
          status: t.status ?? 'needsAction',
          due: t.due ?? '',
          notes: t.notes ?? '',
          updated: t.updated ?? '',
          parent: t.parent ?? '',
        }));

        return {
          success: true,
          output: formatTasks(taskList),
        };
      } catch (error) {
        return {
          success: false,
          output: '',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    },
  };

  return [tasksLists, tasksList];
}
