/**
 * @fileoverview Issues client for the DeepSource API
 * This module provides functionality for working with DeepSource issues.
 */

import { BaseDeepSourceClient } from './base-client.js';
import { DeepSourceIssue, IssueFilterParams } from '../models/issues.js';
import { PaginatedResponse, PageInfo } from '../utils/pagination/types.js';
import { isErrorWithMessage } from '../utils/errors/handlers.js';

/**
 * Client for interacting with DeepSource issues API
 * @class
 * @extends BaseDeepSourceClient
 * @public
 */
export class IssuesClient extends BaseDeepSourceClient {
  /**
   * Fetches issues from a DeepSource project with optional filtering
   * Supports multi-page fetching when max_pages is specified
   * @param projectKey The project key to fetch issues for
   * @param params Optional filter parameters including pagination
   * @returns Promise that resolves to a paginated list of issues
   * @throws {ClassifiedError} When the API request fails
   * @public
   */
  async getIssues(
    projectKey: string,
    params: IssueFilterParams = {}
  ): Promise<PaginatedResponse<DeepSourceIssue>> {
    try {
      this.logger.info('Fetching issues from DeepSource API', {
        projectKey,
        hasFilters: Object.keys(params).length > 0,
        maxPages: params.max_pages,
      });

      const project = await this.findProjectByKey(projectKey);
      if (!project) {
        return BaseDeepSourceClient.createEmptyPaginatedResponse<DeepSourceIssue>();
      }

      // Create a fetcher function for single page
      const singlePageFetcher = async (
        pageParams: IssueFilterParams
      ): Promise<PaginatedResponse<DeepSourceIssue>> => {
        const normalizedParams = BaseDeepSourceClient.normalizePaginationParams(pageParams);

        // For now, use the original query until optimization is fully implemented
        const query = IssuesClient.buildIssuesQuery();

        const response = await this.executeGraphQL(query, {
          login: project.repository.login,
          name: project.repository.name,
          provider: project.repository.provider,
          ...normalizedParams,
        });

        if (!response.data) {
          throw new Error('No data received from GraphQL API');
        }

        const responseData = response.data as { repository?: { issues?: unknown } };
        const issuesData = responseData.repository?.issues as
          | {
              pageInfo?: PageInfo;
              totalCount?: number;
              edges?: unknown[];
            }
          | undefined;
        if (!issuesData) {
          return BaseDeepSourceClient.createEmptyPaginatedResponse<DeepSourceIssue>();
        }

        const issues = this.extractIssuesFromResponse(response.data);
        const pageInfo: PageInfo = issuesData.pageInfo || {
          hasNextPage: false,
          hasPreviousPage: false,
        };
        const totalCount = issuesData.totalCount || issues.length;

        this.logger.debug('Fetched issues page', {
          count: issues.length,
          totalCount,
          hasNextPage: pageInfo.hasNextPage,
        });

        return {
          items: issues,
          pageInfo,
          totalCount,
        };
      };

      // Use fetchWithPagination for automatic multi-page support
      const result = await this.fetchWithPagination(singlePageFetcher, params);

      this.logger.info('Successfully fetched all issues', {
        totalItems: result.items.length,
        totalCount: result.totalCount,
      });

      return result;
    } catch (error) {
      return this.handleIssuesError(error);
    }
  }

  /**
   * Fetches a specific issue by ID
   * @param projectKey The project key
   * @param issueId The issue ID to fetch
   * @returns Promise that resolves to the issue if found, null otherwise
   * @public
   */
  async getIssue(projectKey: string, issueId: string): Promise<DeepSourceIssue | null> {
    try {
      const result = await this.getIssues(projectKey);
      return result.items.find((issue) => issue.id === issueId) || null;
    } catch (error) {
      this.logger.error('Error fetching specific issue', { projectKey, issueId, error });
      throw error;
    }
  }

  /**
   * Builds the GraphQL query for fetching issues
   * @deprecated Use createOptimizedIssuesQuery instead for better performance
   * @private
   */
  private static buildIssuesQuery(): string {
    return `
      query getRepositoryIssues(
        $login: String!
        $name: String!
        $provider: VCSProvider!
        $analyzerIn: [String!]
        $tags: [String!]
        $path: String
        $first: Int
        $after: String
        $last: Int
        $before: String
      ) {
        repository(login: $login, name: $name, vcsProvider: $provider) {
          issues(
            analyzerIn: $analyzerIn, 
            tags: $tags, 
            path: $path, 
            first: $first, 
            after: $after,
            last: $last,
            before: $before
          ) {
            totalCount
            pageInfo {
              hasNextPage
              hasPreviousPage
              startCursor
              endCursor
            }
            edges {
              node {
                id
                title
                shortcode
                category
                severity
                occurrences(first: 1) {
                  edges {
                    node {
                      id
                      status
                      issueText
                      filePath
                      beginLine
                      tags
                    }
                  }
                }
              }
            }
          }
        }
      }
    `;
  }

  /**
   * Extracts issues from GraphQL response
   * @private
   */
  private extractIssuesFromResponse(responseData: unknown): DeepSourceIssue[] {
    const issues: DeepSourceIssue[] = [];

    try {
      const repository = (responseData as Record<string, unknown>)?.repository as Record<
        string,
        unknown
      >;
      const repositoryIssues = repository?.issues as Record<string, unknown>;
      const repoIssues = (repositoryIssues?.edges ?? []) as Array<Record<string, unknown>>;

      for (const { node: repoIssue } of repoIssues) {
        const issueNode = repoIssue as Record<string, unknown>;
        const occurrences = ((issueNode.occurrences as Record<string, unknown>)?.edges ??
          []) as Array<Record<string, unknown>>;

        for (const { node: occurrence } of occurrences) {
          const occurrenceNode = occurrence as Record<string, unknown>;
          issues.push({
            id: String(occurrenceNode.id ?? 'unknown'),
            title: String(issueNode.title ?? 'Unknown Issue'),
            shortcode: String(issueNode.shortcode ?? 'UNKNOWN'),
            category: String(issueNode.category ?? 'UNKNOWN'),
            severity: String(issueNode.severity ?? 'UNKNOWN'),
            status: String(occurrenceNode.status ?? 'UNKNOWN'),
            issue_text: String(occurrenceNode.issueText ?? ''),
            file_path: String(occurrenceNode.filePath ?? ''),
            line_number: Number(occurrenceNode.beginLine ?? 0),
            tags: Array.isArray(occurrenceNode.tags) ? (occurrenceNode.tags as string[]) : [],
          });
        }
      }
    } catch (error) {
      this.logger.error('Error extracting issues from response', { error });
    }

    return issues;
  }

  /**
   * Handles errors during issues fetching
   * @private
   */
  private handleIssuesError(error: unknown): PaginatedResponse<DeepSourceIssue> {
    this.logger.error('Error in getIssues', {
      errorType: typeof error,
      errorMessage: error instanceof Error ? error.message : String(error),
    });

    // Handle special case where no issues exist
    if (isErrorWithMessage(error, 'NoneType')) {
      return {
        items: [],
        pageInfo: {
          hasNextPage: false,
          hasPreviousPage: false,
        },
        totalCount: 0,
      };
    }

    throw error;
  }
}
