import type { FastMCP } from 'fastmcp';
import { UserError } from 'fastmcp';
import { getDocsClient } from '../../../clients.js';
import {
  ApplyParagraphStyleToolParameters,
  ApplyParagraphStyleToolArgs,
  NotImplementedError,
} from '../../../types.js';
import * as GDocsHelpers from '../../../googleDocsApiHelpers.js';

export function register(server: FastMCP) {
  server.addTool({
    name: 'applyParagraphStyle',
    description:
      'Applies paragraph-level formatting (alignment, spacing, heading styles) to paragraphs identified by a character range or by searching for text. Use namedStyleType to set heading levels (HEADING_1 through HEADING_6).',
    parameters: ApplyParagraphStyleToolParameters,
    execute: async (args: ApplyParagraphStyleToolArgs, { log }) => {
      const docs = await getDocsClient();
      let startIndex: number | undefined;
      let endIndex: number | undefined;

      log.info(
        `Applying paragraph style to document ${args.documentId}${args.tabId ? ` (tab: ${args.tabId})` : ''}`
      );
      log.info(`Style options: ${JSON.stringify(args.style)}`);
      log.info(`Target specification: ${JSON.stringify(args.target)}`);

      try {
        // STEP 1: Determine the target paragraph's range based on the targeting method
        if ('textToFind' in args.target) {
          // Find the text first
          log.info(
            `Finding text "${args.target.textToFind}" (instance ${args.target.matchInstance || 1})${args.tabId ? ` in tab ${args.tabId}` : ''}`
          );
          const textRange = await GDocsHelpers.findTextRange(
            docs,
            args.documentId,
            args.target.textToFind,
            args.target.matchInstance || 1,
            args.tabId
          );

          if (!textRange) {
            throw new UserError(
              `Could not find "${args.target.textToFind}" in the document${args.tabId ? ` (tab: ${args.tabId})` : ''}.`
            );
          }

          log.info(
            `Found text at range ${textRange.startIndex}-${textRange.endIndex}, now locating containing paragraph`
          );

          // Then find the paragraph containing this text
          const paragraphRange = await GDocsHelpers.getParagraphRange(
            docs,
            args.documentId,
            textRange.startIndex,
            args.tabId
          );

          if (!paragraphRange) {
            throw new UserError(`Found the text but could not determine the paragraph boundaries.`);
          }

          startIndex = paragraphRange.startIndex;
          endIndex = paragraphRange.endIndex;
          log.info(`Text is contained within paragraph at range ${startIndex}-${endIndex}`);
        } else if ('indexWithinParagraph' in args.target) {
          // Find paragraph containing the specified index
          log.info(
            `Finding paragraph containing index ${args.target.indexWithinParagraph}${args.tabId ? ` in tab ${args.tabId}` : ''}`
          );
          const paragraphRange = await GDocsHelpers.getParagraphRange(
            docs,
            args.documentId,
            args.target.indexWithinParagraph,
            args.tabId
          );

          if (!paragraphRange) {
            throw new UserError(
              `Could not find paragraph containing index ${args.target.indexWithinParagraph}${args.tabId ? ` in tab ${args.tabId}` : ''}.`
            );
          }

          startIndex = paragraphRange.startIndex;
          endIndex = paragraphRange.endIndex;
          log.info(`Located paragraph at range ${startIndex}-${endIndex}`);
        } else if ('startIndex' in args.target && 'endIndex' in args.target) {
          // Use directly provided range
          startIndex = args.target.startIndex;
          endIndex = args.target.endIndex;
          log.info(`Using provided paragraph range ${startIndex}-${endIndex}`);
        }

        // Verify that we have a valid range
        if (startIndex === undefined || endIndex === undefined) {
          throw new UserError(
            'Could not determine target paragraph range from the provided information.'
          );
        }

        if (endIndex <= startIndex) {
          throw new UserError(
            `Invalid paragraph range: end index (${endIndex}) must be greater than start index (${startIndex}).`
          );
        }

        // STEP 2: Build and apply the paragraph style request
        log.info(`Building paragraph style request for range ${startIndex}-${endIndex}`);
        const requestInfo = GDocsHelpers.buildUpdateParagraphStyleRequest(
          startIndex,
          endIndex,
          args.style,
          args.tabId
        );

        if (!requestInfo) {
          return 'No valid paragraph styling options were provided.';
        }

        log.info(`Applying styles: ${requestInfo.fields.join(', ')}`);
        await GDocsHelpers.executeBatchUpdate(docs, args.documentId, [requestInfo.request]);

        return `Successfully applied paragraph styles (${requestInfo.fields.join(', ')}) to the paragraph${args.tabId ? ` in tab ${args.tabId}` : ''}.`;
      } catch (error: any) {
        // Detailed error logging
        log.error(`Error applying paragraph style in doc ${args.documentId}:`);
        log.error(error.stack || error.message || error);

        if (error instanceof UserError) throw error;
        if (error instanceof NotImplementedError) throw error;

        // Provide a more helpful error message
        throw new UserError(`Failed to apply paragraph style: ${error.message || 'Unknown error'}`);
      }
    },
  });
}
