/**
 * @fileoverview Recent run issues handler for the DeepSource MCP server
 * This module provides MCP tool handlers for fetching issues from the most recent run.
 */

import { DeepSourceClient } from '../deepsource.js';
import { ApiResponse } from '../models/common.js';
import { DeepSourceIssue } from '../models/issues.js';
import { createLogger, Logger } from '../utils/logging/logger.js';
import { PaginationParams } from '../utils/pagination/types.js';
import { BranchName, asProjectKey, asBranchName } from '../types/branded.js';
import { BaseHandlerDeps } from './base/handler.interface.js';
import {
  createBaseHandlerFactory,
  wrapInApiResponse,
  createDefaultHandlerDeps,
} from './base/handler.factory.js';
import { IAnalysisRunRepository } from '../domain/aggregates/analysis-run/analysis-run.repository.js';
import { RepositoryFactory } from '../infrastructure/factories/repository.factory.js';

// Logger for the recent run issues handler
const logger = createLogger('RecentRunIssuesHandler');

/**
 * Interface for parameters for fetching issues from the most recent run
 * @public
 */
export interface DeepsourceRecentRunIssuesParams extends PaginationParams {
  /** DeepSource project key to fetch issues for */
  projectKey: string;
  /** Branch name to fetch the most recent run from */
  branchName: string;
}

/**
 * Extended dependencies interface for recent run issues handler
 */
interface RecentRunIssuesHandlerDeps {
  analysisRunRepository: IAnalysisRunRepository;
  client: DeepSourceClient;
  logger: Logger;
}

/**
 * Creates a recent run issues handler with injected dependencies using domain aggregates
 * @param deps - The dependencies for the handler
 * @returns The configured handler function
 */
export function createRecentRunIssuesHandlerWithRepo(deps: RecentRunIssuesHandlerDeps) {
  return async function handleRecentRunIssues(params: DeepsourceRecentRunIssuesParams) {
    try {
      const { projectKey, branchName } = params;
      const projectKeyBranded = asProjectKey(projectKey);
      const branchNameBranded = asBranchName(branchName);

      deps.logger.info('Fetching recent run from repository and issues from client', {
        projectKey,
        branchName,
      });

      // Get the most recent run using domain repository
      const recentRun = await deps.analysisRunRepository.findMostRecent(
        projectKeyBranded,
        branchNameBranded
      );

      if (!recentRun) {
        deps.logger.error('No recent run found for branch', { projectKey, branchName });
        throw new Error(
          `No recent analysis run found for branch "${branchName}" in project "${projectKey}"`
        );
      }

      deps.logger.info('Found recent run, fetching issues via client', {
        runId: recentRun.runId,
        commitOid: recentRun.commitInfo.oid,
        branchName: recentRun.commitInfo.branch,
      });

      // Get issues for this run using the client (since we don't have an issue domain aggregate yet)
      const result = await deps.client.getRecentRunIssues(projectKey, branchName as BranchName);

      // Verify we got the same run (sanity check)
      if (!result.run || result.run.runUid !== recentRun.runId) {
        deps.logger.warn('Mismatch between domain run and client run', {
          domainRunId: recentRun.runId,
          clientRunUid: result.run?.runUid || 'null',
        });
      }

      deps.logger.info('Successfully fetched recent run issues', {
        runId: recentRun.runId,
        commitOid: recentRun.commitInfo.oid,
        issueCount: result.items.length,
        totalCount: result.totalCount,
        hasNextPage: result.pageInfo?.hasNextPage,
        hasPreviousPage: result.pageInfo?.hasPreviousPage,
      });

      const recentRunData = {
        run: {
          id: recentRun.runId,
          runUid: recentRun.runId, // Domain aggregate uses runId as unique identifier
          commitOid: recentRun.commitInfo.oid,
          branchName: recentRun.commitInfo.branch,
          baseOid: recentRun.commitInfo.baseOid,
          status: recentRun.status,
          createdAt: recentRun.timestamps.createdAt,
          updatedAt: recentRun.timestamps.startedAt || recentRun.timestamps.createdAt,
          finishedAt: recentRun.timestamps.finishedAt,
          summary: {
            occurrencesIntroduced: recentRun.summary.totalIntroduced.count,
            occurrencesResolved: recentRun.summary.totalResolved.count,
            occurrencesSuppressed: recentRun.summary.totalSuppressed.count,
            occurrenceDistributionByAnalyzer: recentRun.summary.byAnalyzer.map((dist) => ({
              analyzerShortcode: dist.analyzerShortcode,
              introduced: dist.introduced.count,
            })),
            occurrenceDistributionByCategory: recentRun.summary.byCategory.map((dist) => ({
              category: dist.category,
              introduced: dist.introduced.count,
            })),
          },
          repository: {
            name: 'Repository', // Domain aggregate doesn't store repository name directly
            id: recentRun.repositoryId,
          },
        },
        issues: result.items.map((issue: DeepSourceIssue) => ({
          id: issue.id,
          title: issue.title,
          shortcode: issue.shortcode,
          category: issue.category,
          severity: issue.severity,
          status: issue.status,
          issue_text: issue.issue_text,
          file_path: issue.file_path,
          line_number: issue.line_number,
          tags: issue.tags,
        })),
        pageInfo: {
          hasNextPage: result.pageInfo?.hasNextPage || false,
          hasPreviousPage: result.pageInfo?.hasPreviousPage || false,
          startCursor: result.pageInfo?.startCursor || null,
          endCursor: result.pageInfo?.endCursor || null,
        },
        totalCount: result.totalCount,
        // Provide helpful information and guidance
        usage_examples: {
          pagination: {
            next_page: 'For forward pagination, use first and after parameters',
            previous_page: 'For backward pagination, use last and before parameters',
          },
          related_tools: {
            run_details: 'Use the run tool to get detailed information about a specific run',
            all_issues: 'Use the project_issues tool to get all issues in the project',
            other_runs: 'Use the runs tool to list all runs for the project',
          },
        },
      };

      return {
        content: [
          {
            type: 'text' as const,
            text: JSON.stringify(recentRunData),
          },
        ],
      };
    } catch (error) {
      deps.logger.error('Error in handleRecentRunIssues', {
        errorType: typeof error,
        errorName: error instanceof Error ? error.name : 'Unknown',
        errorMessage: error instanceof Error ? error.message : String(error),
        errorStack: error instanceof Error ? error.stack : 'No stack available',
      });

      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      deps.logger.debug('Returning error response', { errorMessage });

      return {
        isError: true,
        content: [
          {
            type: 'text' as const,
            text: JSON.stringify({
              error: errorMessage,
              details: 'Failed to retrieve recent run issues',
            }),
          },
        ],
      };
    }
  };
}

/**
 * Creates a recent run issues handler with injected dependencies
 * @param deps - The dependencies for the handler
 * @returns The configured handler factory
 */
export const createRecentRunIssuesHandler = createBaseHandlerFactory(
  'recent_run_issues',
  async (deps: BaseHandlerDeps, { projectKey, branchName }: DeepsourceRecentRunIssuesParams) => {
    const apiKey = deps.getApiKey();
    deps.logger.debug('API key retrieved from config', {
      length: apiKey.length,
      prefix: `${apiKey.substring(0, 5)}...`,
    });

    const client = new DeepSourceClient(apiKey);

    deps.logger.info('Fetching recent run issues', {
      projectKey,
      branchName,
    });

    const result = await client.getRecentRunIssues(projectKey, branchName as BranchName);

    if (!result.run) {
      deps.logger.error('No recent run found for branch', { projectKey, branchName });
      throw new Error(
        `No recent analysis run found for branch "${branchName}" in project "${projectKey}"`
      );
    }

    deps.logger.info('Successfully fetched recent run issues', {
      runUid: result.run.runUid,
      commitOid: result.run.commitOid,
      issueCount: result.items.length,
      totalCount: result.totalCount,
      hasNextPage: result.pageInfo?.hasNextPage,
      hasPreviousPage: result.pageInfo?.hasPreviousPage,
    });

    const recentRunData = {
      run: {
        id: result.run.id,
        runUid: result.run.runUid,
        commitOid: result.run.commitOid,
        branchName: result.run.branchName,
        baseOid: result.run.baseOid,
        status: result.run.status,
        createdAt: result.run.createdAt,
        updatedAt: result.run.updatedAt,
        finishedAt: result.run.finishedAt,
        summary: {
          occurrencesIntroduced: result.run.summary.occurrencesIntroduced,
          occurrencesResolved: result.run.summary.occurrencesResolved,
          occurrencesSuppressed: result.run.summary.occurrencesSuppressed,
          occurrenceDistributionByAnalyzer: result.run.summary.occurrenceDistributionByAnalyzer,
          occurrenceDistributionByCategory: result.run.summary.occurrenceDistributionByCategory,
        },
        repository: {
          name: result.run.repository.name,
          id: result.run.repository.id,
        },
      },
      issues: result.items.map((issue: DeepSourceIssue) => ({
        id: issue.id,
        title: issue.title,
        shortcode: issue.shortcode,
        category: issue.category,
        severity: issue.severity,
        status: issue.status,
        issue_text: issue.issue_text,
        file_path: issue.file_path,
        line_number: issue.line_number,
        tags: issue.tags,
      })),
      pageInfo: {
        hasNextPage: result.pageInfo?.hasNextPage || false,
        hasPreviousPage: result.pageInfo?.hasPreviousPage || false,
        startCursor: result.pageInfo?.startCursor || null,
        endCursor: result.pageInfo?.endCursor || null,
      },
      totalCount: result.totalCount,
      // Provide helpful information and guidance
      usage_examples: {
        pagination: {
          next_page: 'For forward pagination, use first and after parameters',
          previous_page: 'For backward pagination, use last and before parameters',
        },
        related_tools: {
          run_details: 'Use the run tool to get detailed information about a specific run',
          all_issues: 'Use the project_issues tool to get all issues in the project',
          other_runs: 'Use the runs tool to list all runs for the project',
        },
      },
    };

    return wrapInApiResponse(recentRunData);
  }
);

/**
 * Fetches and returns issues from the most recent analysis run on a specific branch using domain aggregates
 * @param params - Parameters for fetching the issues, including project key, branch name, and pagination
 * @returns A response containing the issues data and run information
 * @throws Error if the DEEPSOURCE_API_KEY environment variable is not set
 * @public
 */
export async function handleDeepsourceRecentRunIssues(
  params: DeepsourceRecentRunIssuesParams
): Promise<ApiResponse> {
  const baseDeps = createDefaultHandlerDeps({ logger });
  const apiKey = baseDeps.getApiKey();
  const repositoryFactory = new RepositoryFactory({ apiKey });
  const analysisRunRepository = repositoryFactory.createAnalysisRunRepository();
  const client = new DeepSourceClient(apiKey);

  const deps: RecentRunIssuesHandlerDeps = {
    analysisRunRepository,
    client,
    logger,
  };

  const handler = createRecentRunIssuesHandlerWithRepo(deps);
  const result = await handler(params);

  // If the domain handler returned an error response, throw an error for backward compatibility
  if (result.isError) {
    const firstContent = result.content[0];
    if (firstContent) {
      const errorData = JSON.parse(firstContent.text);
      throw new Error(errorData.error);
    } else {
      throw new Error('Unknown recent run issues error');
    }
  }

  return result;
}
