import type { ToolModule, ToolDefinition, ToolResult } from './types.js';
import type { GSCClient } from '../lib/gsc-client.js';
import { QuickWinsSchema, KeywordCannibalizationSchema, ContentDecaySchema, QueryClusteringSchema } from '../schemas/intelligence.js';
import { parseDateRange, formatDate } from '../lib/date-utils.js';

/**
 * INTELLIGENCE TOOLS
 * The differentiators — smart analysis tools that go beyond raw data
 */

// ============================================================================
// QUICK WINS DETECTION
// ============================================================================

const detectQuickWins: ToolDefinition = {
  name: 'detect_quick_wins',
  description: 'Identify SEO quick wins: pages ranking in positions 4-20 with high impressions but low CTR. These are opportunities to boost traffic with minimal effort by improving title tags, meta descriptions, or content.',
  category: 'intelligence',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: QuickWinsSchema,
  _meta: {
    labels: {
      category: "intelligence",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient): Promise<ToolResult> => {
    try {
      const { startDate, endDate } = parseDateRange(args.startDate, args.endDate, args.days);

      // Fetch analytics with query and page dimensions
      const response = await gscClient.searchAnalytics(args.siteUrl, {
        startDate,
        endDate,
        dimensions: ['query', 'page'],
        rowLimit: 25000 // Max rows to get comprehensive data
      });

      if (!response.rows || response.rows.length === 0) {
        return {
          content: [{
            type: 'text',
            text: 'No search analytics data found for this period.'
          }]
        };
      }

      // Filter and score opportunities
      const opportunities = response.rows
        .filter(row => {
          const position = row.position || 100;
          const impressions = row.impressions || 0;
          const ctr = (row.ctr || 0) * 100;

          return position >= args.positionRangeMin &&
                 position <= args.positionRangeMax &&
                 impressions >= args.minImpressions &&
                 ctr < args.maxCtr;
        })
        .map(row => {
          const position = row.position!;
          const impressions = row.impressions!;
          const clicks = row.clicks || 0;
          const ctr = (row.ctr || 0) * 100;

          // Score: higher impressions + better position = bigger opportunity
          const score = impressions * (args.positionRangeMax - position) / args.positionRangeMax;

          // Estimate click gain if CTR improves to average for that position
          // Rough formula: positions 4-10 get ~15% CTR, 11-20 get ~5% CTR
          const targetCtr = position <= 10 ? 15 : 5;
          const estimatedClickGain = Math.round(impressions * (targetCtr / 100) - clicks);

          return {
            query: row.keys?.[0] || 'unknown',
            page: row.keys?.[1] || 'unknown',
            position: Math.round(position * 10) / 10,
            impressions,
            clicks,
            ctr: Math.round(ctr * 10) / 10,
            score: Math.round(score),
            estimatedClickGain
          };
        })
        .sort((a, b) => b.score - a.score)
        .slice(0, args.limit);

      if (opportunities.length === 0) {
        return {
          content: [{
            type: 'text',
            text: `No quick win opportunities found.\n\nCriteria:\n- Position: ${args.positionRangeMin}-${args.positionRangeMax}\n- Min impressions: ${args.minImpressions}\n- Max CTR: ${args.maxCtr}%\n\nTry adjusting the filters.`
          }]
        };
      }

      // Format results
      const totalClickGain = opportunities.reduce((sum, opp) => sum + opp.estimatedClickGain, 0);
      
      let text = `🎯 **Quick Win Opportunities: ${opportunities.length} found**\n`;
      text += `Potential traffic gain: ~${totalClickGain.toLocaleString()} clicks/month\n\n`;
      text += `Period: ${startDate} to ${endDate}\n`;
      text += `Criteria: Position ${args.positionRangeMin}-${args.positionRangeMax}, ${args.minImpressions}+ impressions, <${args.maxCtr}% CTR\n\n`;
      text += `---\n\n`;

      opportunities.slice(0, 10).forEach((opp, idx) => {
        text += `**${idx + 1}. ${opp.query}**\n`;
        text += `   Page: ${opp.page}\n`;
        text += `   Position: ${opp.position} | Impressions: ${opp.impressions.toLocaleString()} | CTR: ${opp.ctr}%\n`;
        text += `   Current clicks: ${opp.clicks} | Estimated gain: +${opp.estimatedClickGain} clicks\n`;
        text += `   💡 Action: Improve title/meta to boost CTR from page-one visibility\n\n`;
      });

      if (opportunities.length > 10) {
        text += `... and ${opportunities.length - 10} more opportunities\n`;
      }

      return {
        content: [{ type: 'text', text }],
        structuredContent: {
          opportunities,
          summary: {
            total: opportunities.length,
            totalClickGain,
            avgPosition: Math.round((opportunities.reduce((sum, o) => sum + o.position, 0) / opportunities.length) * 10) / 10,
            period: { startDate, endDate }
          }
        }
      };
    } catch (error) {
      return {
        content: [{
          type: 'text',
          text: `Error detecting quick wins: ${error instanceof Error ? error.message : String(error)}`
        }],
        isError: true
      };
    }
  }
};

// ============================================================================
// KEYWORD CANNIBALIZATION
// ============================================================================

const keywordCannibalization: ToolDefinition = {
  name: 'keyword_cannibalization',
  description: 'Detect keyword cannibalization: queries that rank multiple pages on your site, diluting authority and confusing search engines. Identifies which page should "win" and which are competing.',
  category: 'intelligence',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: KeywordCannibalizationSchema,
  _meta: {
    labels: {
      category: "intelligence",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient): Promise<ToolResult> => {
    try {
      const { startDate, endDate } = parseDateRange(args.startDate, args.endDate, args.days);

      const response = await gscClient.searchAnalytics(args.siteUrl, {
        startDate,
        endDate,
        dimensions: ['query', 'page'],
        rowLimit: 25000
      });

      if (!response.rows || response.rows.length === 0) {
        return {
          content: [{
            type: 'text',
            text: 'No search analytics data found for this period.'
          }]
        };
      }

      // Group by query
      const queryMap = new Map<string, Array<{
        page: string;
        clicks: number;
        impressions: number;
        ctr: number;
        position: number;
      }>>();

      for (const row of response.rows) {
        const query = row.keys?.[0] || 'unknown';
        const page = row.keys?.[1] || 'unknown';
        const impressions = row.impressions || 0;

        // Apply filters
        if (impressions < args.minImpressions) continue;
        if (args.queryFilter) {
          try {
            const regex = new RegExp(args.queryFilter, 'i');
            if (!regex.test(query)) continue;
          } catch {
            // Invalid regex, skip filter
          }
        }

        if (!queryMap.has(query)) {
          queryMap.set(query, []);
        }

        queryMap.get(query)!.push({
          page,
          clicks: row.clicks || 0,
          impressions,
          ctr: (row.ctr || 0) * 100,
          position: row.position || 100
        });
      }

      // Find cannibalized queries (2+ pages per query)
      const cannibalized = Array.from(queryMap.entries())
        .filter(([_, pages]) => pages.length >= 2)
        .map(([query, pages]) => {
          // Sort by clicks to find winner
          const sorted = pages.sort((a, b) => b.clicks - a.clicks);
          const winner = sorted[0];
          const cannibals = sorted.slice(1);

          const totalImpressions = pages.reduce((sum, p) => sum + p.impressions, 0);
          const totalClicks = pages.reduce((sum, p) => sum + p.clicks, 0);

          // Severity: more pages + more impressions + fewer total clicks = worse
          const severity = totalClicks > 0
            ? (totalImpressions * pages.length) / totalClicks
            : totalImpressions * pages.length;

          const recommendation = cannibals.length === 1
            ? `Consolidate content or add canonical from ${cannibals[0].page} to ${winner.page}`
            : `Consolidate ${cannibals.length} competing pages into ${winner.page} or clearly differentiate their content`;

          return {
            query,
            pages: sorted.map(p => ({
              url: p.page,
              clicks: p.clicks,
              impressions: p.impressions,
              ctr: Math.round(p.ctr * 10) / 10,
              position: Math.round(p.position * 10) / 10,
              isWinner: p === winner
            })),
            severity: Math.round(severity),
            recommendation
          };
        })
        .sort((a, b) => b.severity - a.severity)
        .slice(0, args.limit);

      if (cannibalized.length === 0) {
        return {
          content: [{
            type: 'text',
            text: `No keyword cannibalization detected.\n\nAll queries map to unique pages — good sign! Your content targeting is clear.`
          }]
        };
      }

      // Format results
      let text = `⚠️ **Keyword Cannibalization Detected: ${cannibalized.length} queries**\n\n`;
      text += `Period: ${startDate} to ${endDate}\n`;
      text += `These queries rank multiple pages, diluting authority:\n\n`;
      text += `---\n\n`;

      cannibalized.slice(0, 10).forEach((issue, idx) => {
        text += `**${idx + 1}. "${issue.query}"** (Severity: ${issue.severity})\n`;
        text += `   Competing pages:\n`;
        issue.pages.forEach(page => {
          const badge = page.isWinner ? '👑 WINNER' : '⚔️ Cannibal';
          text += `   ${badge}: ${page.url}\n`;
          text += `      Position: ${page.position} | Clicks: ${page.clicks} | Impressions: ${page.impressions} | CTR: ${page.ctr}%\n`;
        });
        text += `   💡 **Action:** ${issue.recommendation}\n\n`;
      });

      if (cannibalized.length > 10) {
        text += `... and ${cannibalized.length - 10} more cannibalization issues\n`;
      }

      return {
        content: [{ type: 'text', text }],
        structuredContent: {
          cannibalized,
          summary: {
            total: cannibalized.length,
            avgPagesPerQuery: Math.round((cannibalized.reduce((sum, c) => sum + c.pages.length, 0) / cannibalized.length) * 10) / 10,
            period: { startDate, endDate }
          }
        }
      };
    } catch (error) {
      return {
        content: [{
          type: 'text',
          text: `Error detecting cannibalization: ${error instanceof Error ? error.message : String(error)}`
        }],
        isError: true
      };
    }
  }
};

// ============================================================================
// CONTENT DECAY DETECTION
// ============================================================================

const contentDecayDetection: ToolDefinition = {
  name: 'content_decay_detection',
  description: 'Identify pages suffering from content decay: content that once performed well but has lost significant traffic over time. Flags pages that need refreshing or updating.',
  category: 'intelligence',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: ContentDecaySchema,
  _meta: {
    labels: {
      category: "intelligence",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient): Promise<ToolResult> => {
    try {
      // Calculate date ranges
      const endDate = new Date();
      const startDate = new Date(endDate);
      startDate.setDate(startDate.getDate() - args.lookbackDays);

      const recentStart = new Date(endDate);
      recentStart.setDate(recentStart.getDate() - args.comparisonDays);

      const fmtDate = (d: Date) => d.toISOString().split('T')[0];

      // Fetch full lookback period with page and date dimensions
      const response = await gscClient.searchAnalytics(args.siteUrl, {
        startDate: fmtDate(startDate),
        endDate: fmtDate(endDate),
        dimensions: ['page', 'date'],
        rowLimit: 25000
      });

      if (!response.rows || response.rows.length === 0) {
        return {
          content: [{
            type: 'text',
            text: 'No search analytics data found for this period.'
          }]
        };
      }

      // Group by page and date
      const pageData = new Map<string, Array<{ date: string; clicks: number }>>();

      for (const row of response.rows) {
        const page = row.keys?.[0] || 'unknown';
        const date = row.keys?.[1] || '';
        const clicks = row.clicks || 0;

        if (!pageData.has(page)) {
          pageData.set(page, []);
        }

        pageData.get(page)!.push({ date, clicks });
      }

      // Analyze each page for decay
      const recentStartStr = fmtDate(recentStart);
      const decayingPages = [];

      for (const [page, dataPoints] of pageData.entries()) {
        // Sort by date
        dataPoints.sort((a, b) => a.date.localeCompare(b.date));

        // Calculate rolling 7-day average to find peak
        let peakClicks = 0;
        let peakPeriod = '';

        for (let i = 6; i < dataPoints.length; i++) {
          const weekData = dataPoints.slice(i - 6, i + 1);
          const avgClicks = weekData.reduce((sum, d) => sum + d.clicks, 0) / 7;

          if (avgClicks > peakClicks) {
            peakClicks = avgClicks;
            peakPeriod = `${weekData[0].date} to ${weekData[6].date}`;
          }
        }

        // Calculate recent period clicks
        const recentData = dataPoints.filter(d => d.date >= recentStartStr);
        const recentClicks = recentData.length > 0
          ? recentData.reduce((sum, d) => sum + d.clicks, 0) / recentData.length
          : 0;

        // Check for decay
        if (peakClicks >= args.minClicksInPeak && peakClicks > 0) {
          const decayPercent = (peakClicks - recentClicks) / peakClicks;

          if (decayPercent >= args.decayThreshold) {
            // Find when peak occurred
            const peakDate = dataPoints.find(d => {
              const idx = dataPoints.indexOf(d);
              if (idx < 6) return false;
              const weekData = dataPoints.slice(idx - 6, idx + 1);
              const avgClicks = weekData.reduce((sum, dp) => sum + dp.clicks, 0) / 7;
              return Math.abs(avgClicks - peakClicks) < 0.1;
            })?.date || dataPoints[0].date;

            const daysSincePeak = Math.floor(
              (endDate.getTime() - new Date(peakDate).getTime()) / (1000 * 60 * 60 * 24)
            );

            // Classify severity
            let severity: 'mild' | 'moderate' | 'critical';
            if (decayPercent >= 0.7) severity = 'critical';
            else if (decayPercent >= 0.5) severity = 'moderate';
            else severity = 'mild';

            decayingPages.push({
              page,
              peakClicks: Math.round(peakClicks * 10) / 10,
              recentClicks: Math.round(recentClicks * 10) / 10,
              decayPercent: Math.round(decayPercent * 100),
              severity,
              daysSincePeak,
              peakPeriod,
              clickLoss: Math.round((peakClicks - recentClicks) * 10) / 10
            });
          }
        }
      }

      // Sort by absolute click loss
      decayingPages.sort((a, b) => b.clickLoss - a.clickLoss);
      const results = decayingPages.slice(0, args.limit);

      if (results.length === 0) {
        return {
          content: [{
            type: 'text',
            text: `No significant content decay detected.\n\nYour pages are maintaining their traffic levels — great job keeping content fresh!`
          }]
        };
      }

      // Format results
      const totalClickLoss = results.reduce((sum, p) => sum + p.clickLoss, 0);

      let text = `📉 **Content Decay Detected: ${results.length} pages**\n`;
      text += `Total daily click loss: ~${Math.round(totalClickLoss)} clicks/day\n\n`;
      text += `Period analyzed: Last ${args.lookbackDays} days\n`;
      text += `Recent comparison: Last ${args.comparisonDays} days\n\n`;
      text += `---\n\n`;

      results.slice(0, 10).forEach((page, idx) => {
        const emoji = page.severity === 'critical' ? '🔴' : page.severity === 'moderate' ? '🟡' : '🟢';
        text += `**${idx + 1}. ${page.page}**\n`;
        text += `   ${emoji} Severity: ${page.severity.toUpperCase()} (-${page.decayPercent}%)\n`;
        text += `   Peak: ${page.peakClicks} clicks/day (${page.peakPeriod})\n`;
        text += `   Recent: ${page.recentClicks} clicks/day (last ${args.comparisonDays} days)\n`;
        text += `   Days since peak: ${page.daysSincePeak}\n`;
        text += `   💡 **Action:** Refresh content, update statistics, add new sections, or improve relevance\n\n`;
      });

      if (results.length > 10) {
        text += `... and ${results.length - 10} more decaying pages\n`;
      }

      return {
        content: [{ type: 'text', text }],
        structuredContent: {
          decayingPages: results,
          summary: {
            total: results.length,
            totalClickLoss: Math.round(totalClickLoss),
            bySeverity: {
              critical: results.filter(p => p.severity === 'critical').length,
              moderate: results.filter(p => p.severity === 'moderate').length,
              mild: results.filter(p => p.severity === 'mild').length
            },
            lookbackDays: args.lookbackDays,
            comparisonDays: args.comparisonDays
          }
        }
      };
    } catch (error) {
      return {
        content: [{
          type: 'text',
          text: `Error detecting content decay: ${error instanceof Error ? error.message : String(error)}`
        }],
        isError: true
      };
    }
  }
};

// ============================================================================
// QUERY CLUSTERING
// ============================================================================

const queryClustering: ToolDefinition = {
  name: 'query_clustering',
  description: 'Group similar search queries using word-overlap analysis. Identifies semantic query clusters to reveal content gaps, topic opportunities, and keyword themes.',
  category: 'intelligence',
  annotations: {
    readOnlyHint: true,
    idempotentHint: true
  },
  inputSchema: QueryClusteringSchema,
  _meta: {
    labels: {
      category: "intelligence",
      access: "read",
      complexity: "simple"
    }
  },
  handler: async (args, gscClient: GSCClient): Promise<ToolResult> => {
    try {
      const { startDate, endDate } = parseDateRange(args.startDate, args.endDate, args.days);

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

      if (!response.rows || response.rows.length === 0) {
        return {
          content: [{
            type: 'text',
            text: 'No search analytics data found for this period.'
          }]
        };
      }

      // Filter queries by impressions
      const queries = response.rows
        .filter(row => (row.impressions || 0) >= args.minImpressions)
        .map(row => ({
          query: row.keys?.[0] || 'unknown',
          impressions: row.impressions || 0,
          clicks: row.clicks || 0,
          position: row.position || 100,
          ctr: (row.ctr || 0) * 100
        }));

      if (queries.length === 0) {
        return {
          content: [{
            type: 'text',
            text: `No queries found with ${args.minImpressions}+ impressions.`
          }]
        };
      }

      // Tokenize queries
      const tokenize = (q: string): Set<string> => {
        return new Set(
          q.toLowerCase()
            .replace(/[^\w\s]/g, ' ')
            .split(/\s+/)
            .filter(word => word.length > 2) // Ignore short words
        );
      };

      // Calculate Jaccard similarity
      const jaccardSimilarity = (a: Set<string>, b: Set<string>): number => {
        const intersection = new Set([...a].filter(x => b.has(x)));
        const union = new Set([...a, ...b]);
        return union.size > 0 ? intersection.size / union.size : 0;
      };

      // Simple clustering using similarity threshold
      const clusters: Array<{
        queries: typeof queries;
        tokens: Set<string>;
      }> = [];

      for (const query of queries) {
        const tokens = tokenize(query.query);

        // Find best matching cluster
        let bestCluster = null;
        let bestSimilarity = 0;

        for (const cluster of clusters) {
          const similarity = jaccardSimilarity(tokens, cluster.tokens);
          if (similarity >= args.similarityThreshold && similarity > bestSimilarity) {
            bestCluster = cluster;
            bestSimilarity = similarity;
          }
        }

        if (bestCluster) {
          // Add to existing cluster
          bestCluster.queries.push(query);
          // Update cluster tokens (union)
          tokens.forEach(token => bestCluster.tokens.add(token));
        } else {
          // Create new cluster
          clusters.push({
            queries: [query],
            tokens: new Set(tokens)
          });
        }
      }

      // Format clusters
      const formattedClusters = clusters
        .map(cluster => {
          // Find most common words for cluster name
          const wordFreq = new Map<string, number>();
          cluster.queries.forEach(q => {
            tokenize(q.query).forEach(word => {
              wordFreq.set(word, (wordFreq.get(word) || 0) + 1);
            });
          });

          const topWords = Array.from(wordFreq.entries())
            .sort((a, b) => b[1] - a[1])
            .slice(0, 3)
            .map(([word]) => word);

          const name = topWords.join(' + ');

          const totalImpressions = cluster.queries.reduce((sum, q) => sum + q.impressions, 0);
          const totalClicks = cluster.queries.reduce((sum, q) => sum + q.clicks, 0);
          const avgPosition = cluster.queries.reduce((sum, q) => sum + q.position, 0) / cluster.queries.length;

          return {
            name,
            queries: cluster.queries
              .sort((a, b) => b.impressions - a.impressions)
              .map(q => ({
                query: q.query,
                impressions: q.impressions,
                clicks: q.clicks,
                position: Math.round(q.position * 10) / 10,
                ctr: Math.round(q.ctr * 10) / 10
              })),
            totalImpressions,
            totalClicks,
            avgPosition: Math.round(avgPosition * 10) / 10,
            size: cluster.queries.length
          };
        })
        .filter(c => c.size > 1) // Only show clusters with 2+ queries
        .sort((a, b) => b.totalImpressions - a.totalImpressions)
        .slice(0, args.limit);

      if (formattedClusters.length === 0) {
        return {
          content: [{
            type: 'text',
            text: `No query clusters found.\n\nTry lowering the similarity threshold (current: ${args.similarityThreshold}).`
          }]
        };
      }

      // Format results
      let text = `🔍 **Query Clusters: ${formattedClusters.length} found**\n\n`;
      text += `Period: ${startDate} to ${endDate}\n`;
      text += `Similarity threshold: ${args.similarityThreshold}\n`;
      text += `Total queries analyzed: ${queries.length}\n\n`;
      text += `---\n\n`;

      formattedClusters.slice(0, 10).forEach((cluster, idx) => {
        text += `**${idx + 1}. ${cluster.name}** (${cluster.size} queries)\n`;
        text += `   Total impressions: ${cluster.totalImpressions.toLocaleString()} | Clicks: ${cluster.totalClicks.toLocaleString()}\n`;
        text += `   Avg position: ${cluster.avgPosition}\n`;
        text += `   Top queries:\n`;
        cluster.queries.slice(0, 5).forEach(q => {
          text += `      • "${q.query}" — Position ${q.position}, ${q.impressions.toLocaleString()} impressions\n`;
        });
        if (cluster.queries.length > 5) {
          text += `      ... and ${cluster.queries.length - 5} more\n`;
        }
        text += `   💡 **Opportunity:** Create comprehensive content targeting this cluster\n\n`;
      });

      if (formattedClusters.length > 10) {
        text += `... and ${formattedClusters.length - 10} more clusters\n`;
      }

      return {
        content: [{ type: 'text', text }],
        structuredContent: {
          clusters: formattedClusters,
          summary: {
            total: formattedClusters.length,
            totalQueries: queries.length,
            avgClusterSize: Math.round((formattedClusters.reduce((sum, c) => sum + c.size, 0) / formattedClusters.length) * 10) / 10,
            period: { startDate, endDate }
          }
        }
      };
    } catch (error) {
      return {
        content: [{
          type: 'text',
          text: `Error clustering queries: ${error instanceof Error ? error.message : String(error)}`
        }],
        isError: true
      };
    }
  }
};

// ============================================================================
// EXPORTS
// ============================================================================

export const intelligenceTools: ToolModule = {
  tools: [
    detectQuickWins,
    keywordCannibalization,
    contentDecayDetection,
    queryClustering
  ]
};
