import type { FastMCP } from 'fastmcp';
import { UserError } from 'fastmcp';
import { z } from 'zod';
import { getSheetsClient } from '../../clients.js';
import * as SheetsHelpers from '../../googleSheetsApiHelpers.js';

export function register(server: FastMCP) {
  server.addTool({
    name: 'insertChart',
    description:
      'Inserts a chart into a Google Sheet. Supports bar, column, line, area, scatter, pie, donut, and treemap (hierarchical) chart types. ' +
      'For treemap charts, the data must have a label column and a parent label column (use empty string for root nodes) plus a numeric size column. ' +
      'Chart is placed as an overlay at the specified anchor cell.',
    parameters: z.strictObject({
      spreadsheetId: z
        .string()
        .describe(
          'The spreadsheet ID — the long string between /d/ and /edit in a Google Sheets URL.'
        ),
      sheetName: z
        .string()
        .optional()
        .describe('Name of the sheet/tab containing the data. Defaults to the first sheet.'),
      chartType: z
        .enum(['BAR', 'COLUMN', 'LINE', 'AREA', 'SCATTER', 'PIE', 'DONUT', 'TREEMAP'])
        .describe('Chart type to create.'),
      stackedType: z
        .enum(['NOT_STACKED', 'STACKED', 'PERCENT_STACKED'])
        .default('NOT_STACKED')
        .describe(
          'For bar/column/area charts: whether to stack series. NOT_STACKED = grouped, STACKED = absolute stacked, PERCENT_STACKED = 100% stacked.'
        ),
      title: z.string().optional().describe('Chart title.'),
      dataRange: z
        .string()
        .describe(
          'A1 notation range of the data (e.g., "A1:E50"). Include the header row if present.'
        ),
      headerRow: z
        .boolean()
        .default(true)
        .describe('Whether the first row of the data range is a header row.'),
      // Column indices for flexible data mapping
      labelColumnIndex: z
        .number()
        .int()
        .min(1)
        .optional()
        .describe(
          'For pie/donut/treemap: 1-based column index for node labels (default: 1). For treemap this is the leaf/child label.'
        ),
      parentColumnIndex: z
        .number()
        .int()
        .min(1)
        .optional()
        .describe(
          'For treemap: 1-based column index for parent node labels. Root nodes should have an empty string in this column.'
        ),
      valueColumnIndex: z
        .number()
        .int()
        .min(1)
        .optional()
        .describe(
          'For pie/donut/treemap: 1-based column index for the numeric size/value (default: 2).'
        ),
      // Chart position and size
      anchorRow: z
        .number()
        .int()
        .min(0)
        .default(0)
        .describe('Row index (0-based) of the anchor cell for chart placement.'),
      anchorColumn: z
        .number()
        .int()
        .min(0)
        .default(6)
        .describe('Column index (0-based) of the anchor cell for chart placement.'),
      offsetXPixels: z
        .number()
        .int()
        .default(0)
        .describe('Horizontal offset in pixels from the anchor cell.'),
      offsetYPixels: z
        .number()
        .int()
        .default(0)
        .describe('Vertical offset in pixels from the anchor cell.'),
      widthPixels: z.number().int().default(600).describe('Chart width in pixels.'),
      heightPixels: z.number().int().default(400).describe('Chart height in pixels.'),
    }),
    execute: async (args, { log }) => {
      const sheets = await getSheetsClient();
      log.info(`Inserting ${args.chartType} chart into spreadsheet ${args.spreadsheetId}`);

      try {
        const sheetId = await SheetsHelpers.resolveSheetId(
          sheets,
          args.spreadsheetId,
          args.sheetName
        );

        const { a1Range } = SheetsHelpers.parseRange(args.dataRange);
        const gridRange = SheetsHelpers.parseA1ToGridRange(a1Range, sheetId);

        const startRow = gridRange.startRowIndex ?? 0;
        const endRow = gridRange.endRowIndex ?? startRow + 1;
        const startCol = gridRange.startColumnIndex ?? 0;
        const endCol = gridRange.endColumnIndex ?? startCol + 1;
        const dataStartRow = args.headerRow ? startRow + 1 : startRow;

        const labelCol = startCol + (args.labelColumnIndex ? args.labelColumnIndex - 1 : 0);
        const valueCol = startCol + (args.valueColumnIndex ? args.valueColumnIndex - 1 : 1);
        const parentCol = startCol + (args.parentColumnIndex ? args.parentColumnIndex - 1 : 0);

        const makeSourceRange = (colStart: number, colEnd: number, rowStart = dataStartRow) => ({
          sources: [
            {
              sheetId,
              startRowIndex: rowStart,
              endRowIndex: endRow,
              startColumnIndex: colStart,
              endColumnIndex: colEnd,
            },
          ],
        });

        let chartSpec: Record<string, unknown> = {};

        if (args.chartType === 'PIE' || args.chartType === 'DONUT') {
          chartSpec.pieChart = {
            legendPosition: 'LABELED_LEGEND',
            pieHole: args.chartType === 'DONUT' ? 0.5 : 0,
            domain: {
              data: { sourceRange: makeSourceRange(labelCol, labelCol + 1) },
            },
            series: {
              data: { sourceRange: makeSourceRange(valueCol, valueCol + 1) },
            },
          };
        } else if (args.chartType === 'TREEMAP') {
          chartSpec.treemapChart = {
            labels: {
              sourceRange: makeSourceRange(labelCol, labelCol + 1),
            },
            parentLabels: {
              sourceRange: makeSourceRange(parentCol, parentCol + 1),
            },
            sizeData: {
              sourceRange: makeSourceRange(valueCol, valueCol + 1),
            },
            colorData: {
              sourceRange: makeSourceRange(valueCol, valueCol + 1),
            },
          };
        } else {
          // Basic chart types: BAR, COLUMN, LINE, AREA, SCATTER
          // Each series must be a separate entry with a single-column source range.
          const seriesCount = endCol - startCol - 1;
          const series = Array.from({ length: seriesCount }, (_, i) => ({
            series: {
              sourceRange: {
                sources: [
                  {
                    sheetId,
                    startRowIndex: startRow, // include header so Sheets names the series automatically
                    endRowIndex: endRow,
                    startColumnIndex: startCol + 1 + i,
                    endColumnIndex: startCol + 2 + i,
                  },
                ],
              },
            },
            targetAxis: 'LEFT_AXIS',
          }));

          chartSpec.basicChart = {
            chartType: args.chartType,
            stackedType: args.stackedType,
            legendPosition: 'BOTTOM_LEGEND',
            axis: [
              { position: 'BOTTOM_AXIS', title: '' },
              { position: 'LEFT_AXIS', title: '' },
            ],
            domains: [
              {
                domain: {
                  sourceRange: {
                    sources: [
                      {
                        sheetId,
                        startRowIndex: startRow,
                        endRowIndex: endRow,
                        startColumnIndex: startCol,
                        endColumnIndex: startCol + 1,
                      },
                    ],
                  },
                },
                reversed: false,
              },
            ],
            series,
            headerCount: args.headerRow ? 1 : 0,
          };
        }

        if (args.title) {
          chartSpec.title = args.title;
        }

        const response = await sheets.spreadsheets.batchUpdate({
          spreadsheetId: args.spreadsheetId,
          requestBody: {
            requests: [
              {
                addChart: {
                  chart: {
                    spec: chartSpec,
                    position: {
                      overlayPosition: {
                        anchorCell: {
                          sheetId,
                          rowIndex: args.anchorRow,
                          columnIndex: args.anchorColumn,
                        },
                        offsetXPixels: args.offsetXPixels,
                        offsetYPixels: args.offsetYPixels,
                        widthPixels: args.widthPixels,
                        heightPixels: args.heightPixels,
                      },
                    },
                  },
                },
              },
            ],
          },
        });

        const chartId = response.data.replies?.[0]?.addChart?.chart?.chartId;
        return `Chart created successfully${chartId ? ` (Chart ID: ${chartId})` : ''}.`;
      } catch (error: any) {
        log.error(`Error inserting chart: ${error.message || error}`);
        if (error instanceof UserError) throw error;
        throw new UserError(`Failed to insert chart: ${error.message || 'Unknown error'}`);
      }
    },
  });
}
