/**
 * Analytics tools - Search performance data and analysis
 */

import { z } from 'zod';
import type { ToolModule, ToolDefinition } from './types.js';
import type { GSCClient } from '../lib/gsc-client.js';
import {
  SearchAnalyticsSchema,
  ComparePeriodSchema,
  TopMoversSchema,
  MobileVsDesktopSchema,
  CountryBreakdownSchema
} from '../schemas/analytics.js';
import { parseDateRange } from '../lib/date-utils.js';

/**
 * Core search analytics tool - full GSC performance data
 */
const searchAnalytics: ToolDefinition = {
  name: 'search_analytics',
  description: 'Query Google Search Console performance data with full filtering, grouping, and 25K row limit support. Supports dataState="all" for fresh unconfirmed data.',
  category: 'analytics',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: SearchAnalyticsSchema,
  _meta: {
    labels: {
      category: "search-performance",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient) => {
    try {
      // Resolve dates (server-side to prevent LLM hallucination)
      const { startDate, endDate } = parseDateRange(
        args.startDate,
        args.endDate,
        args.days
      );

      // Build dimension filter groups if filters provided
      let dimensionFilterGroups;
      if (args.filters && args.filters.length > 0) {
        dimensionFilterGroups = [{
          groupType: args.filterOperator || 'and',
          filters: args.filters.map((f: any) => ({
            dimension: f.dimension,
            operator: f.operator,
            expression: f.expression
          }))
        }];
      }

      // Query GSC API
      const response = await gscClient.searchAnalytics(args.siteUrl, {
        startDate,
        endDate,
        dimensions: args.dimensions,
        type: args.type,
        aggregationType: args.aggregationType,
        rowLimit: Math.min(args.rowLimit || 1000, 25000),
        dataState: args.dataState,
        dimensionFilterGroups
      });

      // Format response
      const rows = response.rows || [];
      const summary = {
        rowCount: rows.length,
        totalClicks: rows.reduce((sum, row) => sum + (row.clicks || 0), 0),
        totalImpressions: rows.reduce((sum, row) => sum + (row.impressions || 0), 0),
        avgCTR: rows.length > 0 
          ? rows.reduce((sum, row) => sum + (row.ctr || 0), 0) / rows.length 
          : 0,
        avgPosition: rows.length > 0
          ? rows.reduce((sum, row) => sum + (row.position || 0), 0) / rows.length
          : 0
      };

      return {
        content: [{
          type: 'text',
          text: JSON.stringify({
            query: {
              siteUrl: args.siteUrl,
              startDate,
              endDate,
              dimensions: args.dimensions,
              dataState: args.dataState
            },
            summary,
            rows: rows.slice(0, 100) // Include first 100 rows in text
          }, null, 2)
        }],
        structuredContent: {
          query: { siteUrl: args.siteUrl, startDate, endDate },
          summary,
          rows // Full dataset for apps
        }
      };
    } catch (error: any) {
      return {
        content: [{
          type: 'text',
          text: `Error querying search analytics: ${error.message}`
        }],
        isError: true
      };
    }
  }
};

/**
 * Compare two time periods with delta calculations
 */
const comparePeriods: ToolDefinition = {
  name: 'compare_periods',
  description: 'Compare two time periods for the same property with automatic delta calculations (change in clicks, impressions, CTR, position).',
  category: 'analytics',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: ComparePeriodSchema,
  _meta: {
    labels: {
      category: "search-performance",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient) => {
    try {
      // Resolve current period
      const currentPeriod = parseDateRange(
        args.currentStartDate,
        args.currentEndDate,
        args.currentDays || 7
      );

      // Resolve previous period (same length as current if not specified)
      const previousDays = args.previousDays || args.currentDays || 7;
      const previousPeriod = parseDateRange(
        args.previousStartDate,
        args.previousEndDate,
        previousDays
      );

      // Query both periods
      const [currentData, previousData] = await Promise.all([
        gscClient.searchAnalytics(args.siteUrl, {
          startDate: currentPeriod.startDate,
          endDate: currentPeriod.endDate,
          dimensions: args.dimensions,
          rowLimit: args.rowLimit || 100
        }),
        gscClient.searchAnalytics(args.siteUrl, {
          startDate: previousPeriod.startDate,
          endDate: previousPeriod.endDate,
          dimensions: args.dimensions,
          rowLimit: args.rowLimit || 100
        })
      ]);

      // Build comparison
      const currentRows = currentData.rows || [];
      const previousRows = previousData.rows || [];

      // Create lookup map for previous data
      const previousMap = new Map(
        previousRows.map(row => [
          JSON.stringify(row.keys),
          row
        ])
      );

      // Calculate deltas
      const comparison = currentRows.map(current => {
        const key = JSON.stringify(current.keys);
        const previous = previousMap.get(key);

        if (previous) {
          return {
            keys: current.keys,
            current: {
              clicks: current.clicks,
              impressions: current.impressions,
              ctr: current.ctr,
              position: current.position
            },
            previous: {
              clicks: previous.clicks,
              impressions: previous.impressions,
              ctr: previous.ctr,
              position: previous.position
            },
            delta: {
              clicks: current.clicks - previous.clicks,
              clicksPercent: previous.clicks > 0 
                ? ((current.clicks - previous.clicks) / previous.clicks) * 100 
                : 0,
              impressions: current.impressions - previous.impressions,
              impressionsPercent: previous.impressions > 0
                ? ((current.impressions - previous.impressions) / previous.impressions) * 100
                : 0,
              ctr: current.ctr - previous.ctr,
              position: current.position - previous.position
            }
          };
        } else {
          return {
            keys: current.keys,
            current: {
              clicks: current.clicks,
              impressions: current.impressions,
              ctr: current.ctr,
              position: current.position
            },
            previous: null,
            delta: { isNew: true }
          };
        }
      });

      return {
        content: [{
          type: 'text',
          text: JSON.stringify({
            periods: {
              current: currentPeriod,
              previous: previousPeriod
            },
            comparison: comparison.slice(0, 50)
          }, null, 2)
        }],
        structuredContent: {
          periods: { current: currentPeriod, previous: previousPeriod },
          comparison
        }
      };
    } catch (error: any) {
      return {
        content: [{
          type: 'text',
          text: `Error comparing periods: ${error.message}`
        }],
        isError: true
      };
    }
  }
};

/**
 * Find top movers (biggest gainers and losers)
 */
const topMovers: ToolDefinition = {
  name: 'top_movers',
  description: 'Identify queries or pages with the biggest gains or losses between two periods. Great for spotting trends.',
  category: 'analytics',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: TopMoversSchema,
  _meta: {
    labels: {
      category: "search-performance",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient) => {
    try {
      // Default to last 7 days vs previous 7 days
      const currentDays = args.currentDays || 7;
      const previousDays = args.previousDays || currentDays;

      const currentPeriod = parseDateRange(args.currentStartDate, args.currentEndDate, currentDays);
      const previousPeriod = parseDateRange(args.previousStartDate, args.previousEndDate, previousDays);

      // Query both periods
      const [currentData, previousData] = await Promise.all([
        gscClient.searchAnalytics(args.siteUrl, {
          startDate: currentPeriod.startDate,
          endDate: currentPeriod.endDate,
          dimensions: [args.dimension || 'query'],
          rowLimit: 1000
        }),
        gscClient.searchAnalytics(args.siteUrl, {
          startDate: previousPeriod.startDate,
          endDate: previousPeriod.endDate,
          dimensions: [args.dimension || 'query'],
          rowLimit: 1000
        })
      ]);

      const metric = args.metric || 'clicks';
      const minImpressions = args.minImpressions || 10;

      // Calculate changes
      const currentRows = (currentData.rows || []).filter(r => r.impressions >= minImpressions);
      const previousMap = new Map(
        (previousData.rows || []).map(row => [row.keys![0], row])
      );

      const movers = currentRows
        .map(current => {
          const key = current.keys![0];
          const previous = previousMap.get(key);
          
          if (!previous) return null;

          const currentValue = (current as any)[metric] as number;
          const previousValue = (previous as any)[metric] as number;
          const change = currentValue - previousValue;
          const changePercent = previousValue > 0 ? (change / previousValue) * 100 : 0;

          return {
            [args.dimension || 'query']: key,
            current: { [metric]: currentValue, impressions: current.impressions },
            previous: { [metric]: previousValue, impressions: previous.impressions },
            change,
            changePercent
          };
        })
        .filter(m => m !== null)
        .sort((a, b) => Math.abs(b!.change) - Math.abs(a!.change));

      const limit = args.limit || 20;
      const gainers = movers.filter(m => m!.change > 0).slice(0, limit);
      const losers = movers.filter(m => m!.change < 0).slice(0, limit);

      return {
        content: [{
          type: 'text',
          text: JSON.stringify({
            metric,
            periods: { current: currentPeriod, previous: previousPeriod },
            topGainers: gainers,
            topLosers: losers
          }, null, 2)
        }],
        structuredContent: {
          metric,
          periods: { current: currentPeriod, previous: previousPeriod },
          gainers,
          losers
        }
      };
    } catch (error: any) {
      return {
        content: [{
          type: 'text',
          text: `Error finding top movers: ${error.message}`
        }],
        isError: true
      };
    }
  }
};

/**
 * Mobile vs Desktop performance
 */
const mobileVsDesktop: ToolDefinition = {
  name: 'mobile_vs_desktop',
  description: 'Compare mobile vs desktop performance for queries or pages. Shows device-specific metrics.',
  category: 'analytics',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: MobileVsDesktopSchema,
  _meta: {
    labels: {
      category: "search-performance",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient) => {
    try {
      const { startDate, endDate } = parseDateRange(args.startDate, args.endDate, args.days);

      const dimension = args.dimension || 'page';

      // Query with device dimension
      const response = await gscClient.searchAnalytics(args.siteUrl, {
        startDate,
        endDate,
        dimensions: [dimension, 'device'],
        rowLimit: (args.rowLimit || 100) * 3 // Account for 3 devices
      });

      // Group by dimension and device
      const grouped = new Map<string, any>();

      for (const row of response.rows || []) {
        const key = row.keys![0];
        const device = row.keys![1];

        if (!grouped.has(key)) {
          grouped.set(key, { [dimension]: key, devices: {} });
        }

        grouped.get(key)!.devices[device] = {
          clicks: row.clicks,
          impressions: row.impressions,
          ctr: row.ctr,
          position: row.position
        };
      }

      const breakdown = Array.from(grouped.values())
        .slice(0, args.rowLimit || 100);

      return {
        content: [{
          type: 'text',
          text: JSON.stringify({
            period: { startDate, endDate },
            dimension,
            breakdown
          }, null, 2)
        }],
        structuredContent: {
          period: { startDate, endDate },
          dimension,
          breakdown
        }
      };
    } catch (error: any) {
      return {
        content: [{
          type: 'text',
          text: `Error analyzing mobile vs desktop: ${error.message}`
        }],
        isError: true
      };
    }
  }
};

/**
 * Country breakdown
 */
const countryBreakdown: ToolDefinition = {
  name: 'country_breakdown',
  description: 'Geographic performance breakdown showing clicks, impressions, CTR, and position by country.',
  category: 'analytics',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: CountryBreakdownSchema,
  _meta: {
    labels: {
      category: "search-performance",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient) => {
    try {
      const { startDate, endDate } = parseDateRange(args.startDate, args.endDate, args.days);

      const response = await gscClient.searchAnalytics(args.siteUrl, {
        startDate,
        endDate,
        dimensions: ['country'],
        rowLimit: args.rowLimit || 50
      });

      const countries = (response.rows || []).map(row => ({
        country: row.keys![0],
        clicks: row.clicks,
        impressions: row.impressions,
        ctr: row.ctr,
        position: row.position
      }));

      return {
        content: [{
          type: 'text',
          text: JSON.stringify({
            period: { startDate, endDate },
            countries
          }, null, 2)
        }],
        structuredContent: {
          period: { startDate, endDate },
          countries
        }
      };
    } catch (error: any) {
      return {
        content: [{
          type: 'text',
          text: `Error getting country breakdown: ${error.message}`
        }],
        isError: true
      };
    }
  }
};

export const tools: ToolDefinition[] = [
  searchAnalytics,
  comparePeriods,
  topMovers,
  mobileVsDesktop,
  countryBreakdown
];

export default { tools } as ToolModule;
