import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import { platformFetch } from '../../lib/platform-client.js';

type CommitFile = {
  path: string;
  additions: number;
  deletions: number;
};

type ChangelogCommit = {
  sha: string;
  message: string;
  author: string;
  date: string;
  prNumber?: number;
  prTitle?: string;
  prBody?: string;
  files: CommitFile[];
};

registerTool({
  name: 'changelog.generate',
  description:
    'Generate changelog from merged PRs and telemetry impact analysis. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    productId: z.string().min(1).describe('Product ID to generate changelog for'),
    fromCommit: z.string().min(1).describe('Starting commit SHA or tag'),
    toCommit: z.string().optional().describe('Ending commit SHA (defaults to HEAD)'),
    timeWindow: z
      .object({
        days: z
          .number()
          .int()
          .min(1)
          .max(90)
          .default(7)
          .describe('Look back period in days for telemetry impact'),
      })
      .optional()
      .describe('Time window for telemetry impact analysis'),
    includeTelemetry: z.boolean().default(true).describe('Include telemetry impact analysis'),
    format: z.enum(['markdown', 'json']).default('markdown').describe('Output format'),
  }),
  async execute(args, req) {
    const { productId, fromCommit, toCommit, timeWindow, includeTelemetry, format } = args;
    const runId = `changelog-${Date.now()}`;

    req.log.info(
      { runId, productId, fromCommit, toCommit, includeTelemetry },
      'Generating changelog'
    );

    try {
      // Step 1: Get commit history and PR information
      const commitsResponse = await platformFetch<{
        commits: Array<{
          sha: string;
          message: string;
          author: string;
          date: string;
          prNumber?: number;
          prTitle?: string;
          prBody?: string;
          files: Array<{
            path: string;
            additions: number;
            deletions: number;
          }>;
        }>;
        total: number;
      }>(
        `/api/changelog/commits?productId=${productId}&from=${fromCommit}${toCommit ? `&to=${toCommit}` : ''}`,
        { method: 'GET' },
        { token: req.headers.authorization?.replace('Bearer ', '') || '', requestId: req.id }
      );

      // Step 2: Get telemetry impact if requested
      let telemetryImpact = null;
      if (includeTelemetry) {
        const lookbackDays = timeWindow?.days || 7;
        const telemetryResponse = await platformFetch<{
          before: {
            errorRate: number;
            totalEvents: number;
            uniqueUsers: number;
            topErrors: Array<{ message: string; count: number }>;
          };
          after: {
            errorRate: number;
            totalEvents: number;
            uniqueUsers: number;
            topErrors: Array<{ message: string; count: number }>;
          };
          impact: {
            errorRateChange: number;
            eventsChange: number;
            usersChange: number;
            newErrors: Array<{ message: string; count: number }>;
            resolvedErrors: Array<{ message: string; count: number }>;
          };
        }>(
          `/api/changelog/telemetry-impact?productId=${productId}&days=${lookbackDays}`,
          { method: 'GET' },
          { token: req.headers.authorization?.replace('Bearer ', '') || '', requestId: req.id }
        );
        telemetryImpact = telemetryResponse;
      }

      // Step 3: Analyze and categorize changes
      const categorizeCommit = (message: string, files: Array<{ path: string }>) => {
        const msg = message.toLowerCase();

        if (msg.includes('feat') || msg.includes('feature')) return 'feature';
        if (msg.includes('fix') || msg.includes('bug')) return 'bugfix';
        if (msg.includes('refactor') || msg.includes('cleanup')) return 'refactor';
        if (msg.includes('docs') || msg.includes('readme')) return 'documentation';
        if (msg.includes('test') || msg.includes('spec')) return 'testing';
        if (msg.includes('chore') || msg.includes('build') || msg.includes('deps'))
          return 'maintenance';

        // Categorize by file paths
        const hasBackend = files.some(f => f.path.includes('backend/') || f.path.includes('src/'));
        const hasFrontend = files.some(f => f.path.includes('web/') || f.path.includes('src/app/'));
        const hasTests = files.some(f => f.path.includes('test') || f.path.includes('spec'));

        if (hasTests) return 'testing';
        if (hasBackend && hasFrontend) return 'fullstack';
        if (hasBackend) return 'backend';
        if (hasFrontend) return 'frontend';

        return 'other';
      };

      const categorized = commitsResponse.commits.reduce(
        (acc: Record<string, ChangelogCommit[]>, commit: ChangelogCommit) => {
          const category = categorizeCommit(commit.message, commit.files);
          if (!acc[category]) acc[category] = [];
          acc[category].push(commit);
          return acc;
        },
        {} as Record<string, ChangelogCommit[]>
      );

      // Step 4: Generate changelog content
      const generateMarkdown = () => {
        let md = `# Changelog for ${productId}\n\n`;
        md += `**From:** ${fromCommit}\n`;
        if (toCommit) md += `**To:** ${toCommit}\n`;
        md += `**Generated:** ${new Date().toISOString()}\n`;
        md += `**Total Commits:** ${commitsResponse.total}\n\n`;

        // Telemetry Impact Section
        if (telemetryImpact) {
          md += `## 📊 Telemetry Impact\n\n`;
          md += `| Metric | Before | After | Change |\n`;
          md += `|--------|--------|-------|--------|\n`;
          md += `| Error Rate | ${(telemetryImpact.before.errorRate * 100).toFixed(2)}% | ${(telemetryImpact.after.errorRate * 100).toFixed(2)}% | ${telemetryImpact.impact.errorRateChange > 0 ? '+' : ''}${(telemetryImpact.impact.errorRateChange * 100).toFixed(2)}% |\n`;
          md += `| Total Events | ${telemetryImpact.before.totalEvents.toLocaleString()} | ${telemetryImpact.after.totalEvents.toLocaleString()} | ${telemetryImpact.impact.eventsChange > 0 ? '+' : ''}${telemetryImpact.impact.eventsChange.toLocaleString()} |\n`;
          md += `| Unique Users | ${telemetryImpact.before.uniqueUsers.toLocaleString()} | ${telemetryImpact.after.uniqueUsers.toLocaleString()} | ${telemetryImpact.impact.usersChange > 0 ? '+' : ''}${telemetryImpact.impact.usersChange.toLocaleString()} |\n\n`;

          if (telemetryImpact.impact.newErrors.length > 0) {
            md += `### 🚨 New Errors\n`;
            telemetryImpact.impact.newErrors.forEach(error => {
              md += `- \`${error.message}\` (${error.count} occurrences)\n`;
            });
            md += `\n`;
          }

          if (telemetryImpact.impact.resolvedErrors.length > 0) {
            md += `### ✅ Resolved Errors\n`;
            telemetryImpact.impact.resolvedErrors.forEach(error => {
              md += `- \`${error.message}\` (${error.count} occurrences)\n`;
            });
            md += `\n`;
          }
        }

        // Categorized Changes
        const categoryEmojis: Record<string, string> = {
          feature: '✨',
          bugfix: '🐛',
          refactor: '♻️',
          documentation: '📚',
          testing: '🧪',
          maintenance: '🔧',
          backend: '⚙️',
          frontend: '🎨',
          fullstack: '🔄',
          other: '📝',
        };

        Object.entries(categorized).forEach(([category, commits]) => {
          if (commits.length === 0) return;

          md += `## ${categoryEmojis[category]} ${category.charAt(0).toUpperCase() + category.slice(1)} (${commits.length})\n\n`;

          commits.forEach((commit: ChangelogCommit) => {
            md += `### ${commit.prTitle || commit.message.split('\n')[0]}\n\n`;
            md += `**Commit:** \`${commit.sha.substring(0, 7)}\`\n`;
            md += `**Author:** ${commit.author}\n`;
            md += `**Date:** ${new Date(commit.date).toLocaleDateString()}\n`;

            if (commit.prNumber) {
              md += `**PR:** #${commit.prNumber}\n`;
            }

            const totalChanges = commit.files.reduce(
              (sum: number, file: CommitFile) => sum + file.additions + file.deletions,
              0
            );
            md += `**Changes:** +${commit.files.reduce((sum: number, f: CommitFile) => sum + f.additions, 0)} -${commit.files.reduce((sum: number, f: CommitFile) => sum + f.deletions, 0)} (${totalChanges} total)\n`;

            // List modified files (limit to 10)
            const modifiedFiles = commit.files
              .slice(0, 10)
              .map((f: CommitFile) => f.path)
              .join(', ');
            md += `**Files:** ${modifiedFiles}${commit.files.length > 10 ? '...' : ''}\n`;

            // PR description excerpt
            if (commit.prBody) {
              const excerpt = commit.prBody.split('\n')[0].substring(0, 200);
              if (excerpt) {
                md += `**Description:** ${excerpt}${excerpt.length === 200 ? '...' : ''}\n`;
              }
            }

            md += `\n`;
          });
        });

        return md;
      };

      const content =
        format === 'markdown'
          ? generateMarkdown()
          : JSON.stringify(
              {
                productId,
                fromCommit,
                toCommit,
                generatedAt: new Date().toISOString(),
                totalCommits: commitsResponse.total,
                categorized,
                telemetryImpact,
              },
              null,
              2
            );

      req.log.info(
        {
          runId,
          productId,
          totalCommits: commitsResponse.total,
          categories: Object.keys(categorized),
        },
        'Changelog generated successfully'
      );

      return {
        runId,
        productId,
        fromCommit,
        toCommit,
        format,
        totalCommits: commitsResponse.total,
        categories: Object.keys(categorized),
        hasTelemetryImpact: !!telemetryImpact,
        content,
        summary: `Generated changelog with ${commitsResponse.total} commits across ${Object.keys(categorized).length} categories${includeTelemetry ? ' including telemetry impact' : ''}`,
      };
    } catch (error) {
      req.log.error(
        { runId, productId, error: error instanceof Error ? error.message : String(error) },
        'Failed to generate changelog'
      );
      throw error;
    }
  },
});

registerTool({
  name: 'changelog.compareReleases',
  description:
    'Compare two releases/tags and generate a detailed comparison report. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    productId: z.string().min(1).describe('Product ID'),
    fromTag: z.string().min(1).describe('Source release tag'),
    toTag: z.string().min(1).describe('Target release tag'),
    includeFileChanges: z.boolean().default(true).describe('Include detailed file changes'),
    includeMetrics: z.boolean().default(true).describe('Include performance metrics comparison'),
  }),
  async execute(args, req) {
    const { productId, fromTag, toTag, includeFileChanges, includeMetrics } = args;
    const runId = `compare-${Date.now()}`;

    req.log.info({ runId, productId, fromTag, toTag }, 'Comparing releases');

    try {
      const response = await platformFetch<{
        comparison: {
          commitsAhead: number;
          commitsBehind: number;
          fileChanges: {
            added: number;
            modified: number;
            deleted: number;
            files: Array<{
              path: string;
              change: 'added' | 'modified' | 'deleted';
              additions: number;
              deletions: number;
            }>;
          };
          contributors: Array<{
            name: string;
            commits: number;
            additions: number;
            deletions: number;
          }>;
        };
        metrics?: {
          performance: {
            before: { [key: string]: number };
            after: { [key: string]: number };
            changes: { [key: string]: number };
          };
          reliability: {
            before: { errorRate: number; uptime: number };
            after: { errorRate: number; uptime: number };
            changes: { errorRateChange: number; uptimeChange: number };
          };
        };
      }>(
        `/api/changelog/compare?productId=${productId}&from=${fromTag}&to=${toTag}&includeFiles=${includeFileChanges}&includeMetrics=${includeMetrics}`,
        { method: 'GET' },
        { token: req.headers.authorization?.replace('Bearer ', '') || '', requestId: req.id }
      );

      const { comparison, metrics } = response;

      req.log.info(
        {
          runId,
          productId,
          commitsAhead: comparison.commitsAhead,
          fileChanges: comparison.fileChanges,
        },
        'Release comparison completed'
      );

      return {
        runId,
        productId,
        fromTag,
        toTag,
        comparison,
        metrics,
        summary: `Compared ${fromTag} → ${toTag}: ${comparison.commitsAhead} commits ahead, ${comparison.fileChanges.added + comparison.fileChanges.modified + comparison.fileChanges.deleted} files changed`,
      };
    } catch (error) {
      req.log.error(
        { runId, productId, error: error instanceof Error ? error.message : String(error) },
        'Failed to compare releases'
      );
      throw error;
    }
  },
});
