/**
 * @fileoverview GraphQL response processor for run checks queries
 * @packageDocumentation
 */

import { z } from 'zod';
import { DeepSourceIssue } from '../../../models/issues.js';
import { createLogger } from '../../logging/logger.js';

const logger = createLogger('DeepSource:RunChecksProcessor');

/**
 * Schema for issue data in GraphQL response
 */
const IssueSchema = z.object({
  shortcode: z.string().optional(),
  title: z.string().optional(),
  category: z.string().optional(),
  severity: z.string().optional(),
  description: z.string().optional(),
  tags: z.array(z.string()).optional(),
});

/**
 * Schema for occurrence node in GraphQL response
 */
const OccurrenceNodeSchema = z.object({
  id: z.string().optional(),
  issue: IssueSchema.optional(),
  path: z.string().optional(),
  beginLine: z.number().optional(),
});

/**
 * Schema for page info
 */
const PageInfoSchema = z.object({
  hasNextPage: z.boolean(),
  hasPreviousPage: z.boolean(),
  startCursor: z.string().optional(),
  endCursor: z.string().optional(),
});

/**
 * Schema for occurrences in check node
 */
const OccurrencesSchema = z.object({
  pageInfo: PageInfoSchema.optional(),
  totalCount: z.number().optional(),
  edges: z
    .array(
      z.object({
        node: OccurrenceNodeSchema.optional(),
      })
    )
    .optional(),
});

/**
 * Schema for check node
 */
const CheckNodeSchema = z.object({
  occurrences: OccurrencesSchema.optional(),
});

/**
 * Schema for run checks GraphQL response
 */
const RunChecksResponseSchema = z.object({
  data: z.object({
    data: z
      .object({
        run: z
          .object({
            checks: z
              .object({
                edges: z
                  .array(
                    z.object({
                      node: CheckNodeSchema,
                    })
                  )
                  .optional(),
              })
              .optional(),
          })
          .optional(),
      })
      .optional(),
  }),
});

/**
 * Result type for processed run checks
 */
export interface ProcessedRunChecks {
  issues: DeepSourceIssue[];
  pageInfo: {
    hasNextPage: boolean;
    hasPreviousPage: boolean;
    startCursor: string | undefined;
    endCursor: string | undefined;
  };
  totalCount: number;
}

/**
 * Create a DeepSourceIssue from an occurrence node
 * @param occurrence - Occurrence node from GraphQL response
 * @returns DeepSourceIssue object
 * @private
 */
function createIssueFromOccurrence(
  occurrence: z.infer<typeof OccurrenceNodeSchema>
): DeepSourceIssue {
  return {
    id: occurrence.id ?? 'unknown',
    shortcode: occurrence.issue?.shortcode ?? '',
    title: occurrence.issue?.title ?? 'Untitled Issue',
    category: occurrence.issue?.category ?? 'UNKNOWN',
    severity: occurrence.issue?.severity ?? 'UNKNOWN',
    status: 'OPEN',
    issue_text: occurrence.issue?.description ?? '',
    file_path: occurrence.path ?? 'N/A',
    line_number: occurrence.beginLine ?? 0,
    tags: occurrence.issue?.tags ?? [],
  };
}

/**
 * Extract issues from validated response data
 * @param validated - Validated response data
 * @returns Processed run checks data
 * @private
 */
function extractIssues(validated: z.infer<typeof RunChecksResponseSchema>): ProcessedRunChecks {
  const issues: DeepSourceIssue[] = [];
  let pageInfo = {
    hasNextPage: false,
    hasPreviousPage: false,
    startCursor: undefined as string | undefined,
    endCursor: undefined as string | undefined,
  };
  let totalCount = 0;

  const checks = validated.data.data?.run?.checks?.edges ?? [];

  for (const { node: check } of checks) {
    const occurrences = check.occurrences?.edges ?? [];
    const occurrencesPageInfo = check.occurrences?.pageInfo;
    const occurrencesTotalCount = check.occurrences?.totalCount ?? 0;

    // Aggregate page info (using the first check's pagination info for simplicity)
    if (occurrencesPageInfo) {
      pageInfo = {
        hasNextPage: occurrencesPageInfo.hasNextPage,
        hasPreviousPage: occurrencesPageInfo.hasPreviousPage,
        startCursor: occurrencesPageInfo.startCursor,
        endCursor: occurrencesPageInfo.endCursor,
      };
    }

    // Always aggregate the total count
    totalCount += occurrencesTotalCount;

    for (const { node: occurrence } of occurrences) {
      if (!occurrence || !occurrence.issue) continue;

      issues.push(createIssueFromOccurrence(occurrence));
    }
  }

  return { issues, pageInfo, totalCount };
}

/**
 * Processor for run checks GraphQL responses
 */
export const RunChecksProcessor = {
  /**
   * Process a run checks GraphQL response
   * @param response - Raw GraphQL response
   * @returns Processed run checks data
   */
  process(response: unknown): ProcessedRunChecks {
    try {
      // Validate response structure
      const validated = RunChecksResponseSchema.parse(response);

      // Extract and process data
      return extractIssues(validated);
    } catch (error) {
      logger.warn('Failed to parse run checks response', {
        error: error instanceof Error ? error.message : String(error),
      });

      // Return empty result on parse failure
      return {
        issues: [],
        pageInfo: {
          hasNextPage: false,
          hasPreviousPage: false,
          startCursor: undefined,
          endCursor: undefined,
        },
        totalCount: 0,
      };
    }
  },
};
