import { google } from 'googleapis';
import type { GcalConfig } from '../../config/schema.js';
import { createGoogleOAuth2Client } from '../../google/oauth.js';
import type { Tool, ToolResult } from '../types.js';

function createOAuth2Client(config: NonNullable<GcalConfig>) {
  return createGoogleOAuth2Client({
    service: 'gcal',
    credentialsFile: config.credentials_file,
    tokenFile: config.token_file,
  });
}

interface EventSummary {
  id: string;
  summary: string;
  start: string;
  end: string;
  location: string;
  attendees: string[];
  htmlLink: string;
}

/** Fetch events from Google Calendar. */
async function fetchEvents(
  calendar: ReturnType<typeof google.calendar>,
  params: {
    calendarId?: string;
    timeMin?: string;
    timeMax?: string;
    q?: string;
    maxResults?: number;
  },
): Promise<EventSummary[]> {
  const response = await calendar.events.list({
    calendarId: params.calendarId ?? 'primary',
    timeMin: params.timeMin,
    timeMax: params.timeMax,
    q: params.q,
    maxResults: params.maxResults ?? 25,
    singleEvents: true,
    orderBy: 'startTime',
  });

  const items = response.data.items ?? [];
  return items.map(event => ({
    id: event.id ?? '',
    summary: event.summary ?? '(no title)',
    start: event.start?.dateTime ?? event.start?.date ?? '',
    end: event.end?.dateTime ?? event.end?.date ?? '',
    location: event.location ?? '',
    attendees: (event.attendees ?? []).map(a => a.email ?? '').filter(Boolean),
    htmlLink: event.htmlLink ?? '',
  }));
}

/** Format a list of event summaries for tool output. */
function formatEvents(events: EventSummary[]): string {
  if (events.length === 0) {
    return 'No events found.';
  }

  return events
    .map(e => {
      const parts = [`[${e.id}] ${e.summary}`, `  Time: ${e.start} — ${e.end}`];
      if (e.location) {parts.push(`  Location: ${e.location}`);}
      if (e.attendees.length > 0) {parts.push(`  Attendees: ${e.attendees.join(', ')}`);}
      if (e.htmlLink) {parts.push(`  Link: ${e.htmlLink}`);}
      return parts.join('\n');
    })
    .join('\n\n');
}

function parseErrorMessage(error: unknown): string {
  if (error instanceof Error) {
    return error.message;
  }
  return String(error);
}

function formatToolError(message: string): string {
  const needsScopeHint = /insufficient.*scope|insufficientPermissions|Request had insufficient authentication scopes/i.test(message);
  return needsScopeHint
    ? `${message}. Re-run "flynn gcal-auth" to grant Google Calendar read permissions.`
    : message;
}

/**
 * Creates Google Calendar query tools bound to the given GcalConfig.
 * Tools create their own OAuth2 client per invocation.
 */
export function createGcalTools(config: NonNullable<GcalConfig>): Tool[] {
  const calendarToday: Tool = {
    name: 'calendar.today',
    description:
      "List today's events from Google Calendar. Returns summary, time, location, attendees, and link for each event.",
    requiredSecretScopes: ['gcal'],
    inputSchema: {
      type: 'object',
      properties: {
        calendarId: {
          type: 'string',
          description: 'Calendar ID to query (default: primary)',
        },
      },
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { calendarId?: string };

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

        const now = new Date();
        const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);

        const events = await fetchEvents(calendar, {
          calendarId: args.calendarId,
          timeMin: startOfDay.toISOString(),
          timeMax: endOfDay.toISOString(),
        });

        return {
          success: true,
          output: formatEvents(events),
        };
      } catch (error) {
        const message = parseErrorMessage(error);
        return {
          success: false,
          output: '',
          error: formatToolError(message),
        };
      }
    },
  };

  const calendarList: Tool = {
    name: 'calendar.list',
    description:
      'List events from Google Calendar in a date range. Returns summary, time, location, attendees, and link for each event.',
    requiredSecretScopes: ['gcal'],
    inputSchema: {
      type: 'object',
      properties: {
        startDate: {
          type: 'string',
          description: 'Start date in ISO format (e.g. 2026-02-10)',
        },
        endDate: {
          type: 'string',
          description: 'End date in ISO format (e.g. 2026-02-14)',
        },
        calendarId: {
          type: 'string',
          description: 'Calendar ID to query (default: primary)',
        },
        maxResults: {
          type: 'number',
          description: 'Maximum number of events to return (default: 25)',
        },
      },
      required: ['startDate', 'endDate'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { startDate: string; endDate: string; calendarId?: string; maxResults?: number };

      try {
        const startMs = Date.parse(args.startDate);
        const endMs = Date.parse(args.endDate);
        if (isNaN(startMs) || isNaN(endMs)) {
          return { success: false, output: '', error: 'Invalid date format. Use ISO format (e.g. 2026-02-10).' };
        }

        const auth = createOAuth2Client(config);
        const calendar = google.calendar({ version: 'v3', auth });

        const events = await fetchEvents(calendar, {
          calendarId: args.calendarId,
          timeMin: new Date(startMs).toISOString(),
          timeMax: new Date(endMs).toISOString(),
          maxResults: args.maxResults,
        });

        return {
          success: true,
          output: formatEvents(events),
        };
      } catch (error) {
        const message = parseErrorMessage(error);
        return {
          success: false,
          output: '',
          error: formatToolError(message),
        };
      }
    },
  };

  const calendarSearch: Tool = {
    name: 'calendar.search',
    description:
      'Search Google Calendar events by text query. Returns summary, time, location, attendees, and link for each match.',
    requiredSecretScopes: ['gcal'],
    inputSchema: {
      type: 'object',
      properties: {
        query: {
          type: 'string',
          description: 'Full-text search query',
        },
        calendarId: {
          type: 'string',
          description: 'Calendar ID to query (default: primary)',
        },
        maxResults: {
          type: 'number',
          description: 'Maximum number of results to return (default: 25)',
        },
      },
      required: ['query'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      const args = rawArgs as { query: string; calendarId?: string; maxResults?: number };

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

        // For search, use a wide time window (1 year back to 1 year ahead)
        const now = new Date();
        const yearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
        const yearAhead = new Date(now.getFullYear() + 1, now.getMonth(), now.getDate());

        const events = await fetchEvents(calendar, {
          calendarId: args.calendarId,
          timeMin: yearAgo.toISOString(),
          timeMax: yearAhead.toISOString(),
          q: args.query,
          maxResults: args.maxResults,
        });

        return {
          success: true,
          output: formatEvents(events),
        };
      } catch (error) {
        const message = parseErrorMessage(error);
        return {
          success: false,
          output: '',
          error: formatToolError(message),
        };
      }
    },
  };

  return [calendarToday, calendarList, calendarSearch];
}
