import { RegisterClass } from '@memberjunction/global';
import { LearnWorldsBaseAction } from '../learnworlds-base.action';
import { ActionParam, ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
import { BaseAction } from '@memberjunction/actions';
import { UserInfo } from '@memberjunction/core';
import {
  GetQuizResultsParams,
  GetQuizResultsResult,
  FormattedQuizResult,
  FormattedQuestion,
  FormattedAnswer,
  QuizAnswerValue,
  QuizMetrics,
  QuizResultsSummary,
} from '../interfaces';

// ----------------------------------------------------------------
// File-local interfaces for raw LearnWorlds API shapes
// ----------------------------------------------------------------

/** Query parameters sent to quiz results endpoints */
interface LWQuizResultQueryParams {
  limit: number;
  sort: string;
  order: string;
  passed?: boolean;
  completed_after?: string;
  completed_before?: string;
  user_id?: string;
  [key: string]: string | number | boolean | undefined;
}

/** Raw quiz result from the API */
interface LWRawQuizResult {
  id?: string;
  result_id?: string;
  user_id?: string;
  course_id?: string;
  quiz_id?: string;
  attempt_number?: number;
  attempt?: number;
  score?: number;
  max_score?: number;
  total_points?: number;
  percentage?: number;
  passed?: boolean;
  is_passing?: boolean;
  passing_score?: number;
  passing_percentage?: number;
  started_at?: string;
  completed_at?: string;
  submitted_at?: string;
  duration?: number;
  time_spent?: number;
  user?: {
    id?: string;
    email?: string;
    name?: string;
    first_name?: string;
    last_name?: string;
  };
  quiz?: {
    id?: string;
    title?: string;
    name?: string;
    type?: string;
    question_count?: number;
    total_questions?: number;
  };
}

/** Raw quiz detail response */
interface LWQuizDetailResponse {
  success?: boolean;
  data?: {
    questions?: LWRawQuestion[];
    answers?: LWRawAnswer[];
  };
}

/** Raw question from the API */
interface LWRawQuestion {
  question_number?: number;
  question_id?: string;
  id?: string;
  question_text?: string;
  question?: string;
  text?: string;
  question_type?: string;
  type?: string;
  points?: number;
  score?: number;
  difficulty?: string;
}

/** Raw answer from the API */
interface LWRawAnswer {
  question_number?: number;
  question_id?: string;
  user_answer?: unknown;
  answer?: unknown;
  selected_answer?: unknown;
  correct_answer?: unknown;
  is_correct?: boolean;
  correct?: boolean;
  points_earned?: number;
  points_possible?: number;
  points?: number;
  feedback?: string;
  time_spent?: number;
  answered_at?: string;
}

/** Results API response wrapper */
interface LWQuizResultsApiResponse {
  success?: boolean;
  message?: string;
  data?: LWRawQuizResult[] | { data?: LWRawQuizResult[] };
}

/** Quiz info lookup response */
interface LWQuizLookup {
  success?: boolean;
  data?: {
    id?: string;
    title?: string;
    name?: string;
    type?: string;
    question_count?: number;
    total_questions?: number;
  };
}

/** Stats tracked per quiz when building the breakdown */
interface QuizBreakdownEntry {
  results: FormattedQuizResult[];
  stats: {
    attempts: number;
    averageScore: number;
    passRate: number;
  };
}

/**
 * Action to retrieve quiz/assessment results from LearnWorlds
 */
@RegisterClass(BaseAction, 'GetQuizResultsAction')
export class GetQuizResultsAction extends LearnWorldsBaseAction {
  // ----------------------------------------------------------------
  // Typed public method – can be called directly from code
  // ----------------------------------------------------------------

  /**
   * Get quiz results for a user, course, or specific quiz.
   * Throws on any error.
   */
  public async GetQuizResults(params: GetQuizResultsParams, contextUser: UserInfo): Promise<GetQuizResultsResult> {
    this.SetCompanyContext(params.CompanyID);

    const {
      UserID: userId,
      CourseID: courseId,
      QuizID: quizId,
      IncludeQuestions: includeQuestionsRaw,
      IncludeAnswers: includeAnswersRaw,
      PassingOnly: passingOnly = false,
      DateFrom: dateFrom,
      DateTo: dateTo,
      SortBy: sortBy = 'completed_at',
      SortOrder: sortOrder = 'desc',
      MaxResults: maxResults = 100,
    } = params;

    const includeQuestions = includeQuestionsRaw !== false;
    const includeAnswers = includeAnswersRaw !== false;

    // Require at least one identifier
    if (!userId && !courseId && !quizId) {
      throw new Error('At least one of UserID, CourseID, or QuizID is required');
    }

    // Build query parameters
    const queryParams = this.buildQuizQueryParams(userId, quizId, passingOnly, dateFrom, dateTo, sortBy, sortOrder, maxResults);

    // Validate path segments before URL interpolation
    if (userId) this.validatePathSegment(userId, 'UserID');
    if (courseId) this.validatePathSegment(courseId, 'CourseID');
    if (quizId) this.validatePathSegment(quizId, 'QuizID');

    // Determine endpoint
    const endpoint = this.buildQuizEndpoint(userId, courseId, quizId);

    // Build query string
    const queryString = this.buildQueryString(queryParams);

    // Fetch quiz results
    const resultsResponse = await this.makeLearnWorldsRequest<LWQuizResultsApiResponse>(endpoint + queryString, 'GET', null, contextUser);

    if (resultsResponse.success === false) {
      throw new Error(resultsResponse.message || 'Failed to retrieve quiz results');
    }

    // Normalize results
    const rawResults = this.extractQuizResultsArray(resultsResponse);

    // Process each quiz result
    const formattedResults = await this.processQuizResults(rawResults, userId, courseId, quizId, includeQuestions, includeAnswers, contextUser);

    // Calculate summary
    const summary = this.buildQuizResultsSummary(formattedResults, userId, courseId, quizId, dateFrom, dateTo);

    return {
      QuizResults: formattedResults,
      TotalCount: formattedResults.length,
      Summary: summary,
    };
  }

  // ----------------------------------------------------------------
  // Framework wrapper – thin delegation to the public method
  // ----------------------------------------------------------------

  protected async InternalRunAction(params: RunActionParams): Promise<ActionResultSimple> {
    const { Params, ContextUser } = params;
    this.params = Params;

    try {
      const typedParams = this.extractQuizResultsParams(Params);
      const result = await this.GetQuizResults(typedParams, ContextUser);

      this.setOutputParam(Params, 'QuizResults', result.QuizResults);
      this.setOutputParam(Params, 'TotalCount', result.TotalCount);
      this.setOutputParam(Params, 'Summary', result.Summary);

      return this.buildSuccessResult(`Retrieved ${result.TotalCount} quiz result(s)`, Params);
    } catch (error) {
      const msg = error instanceof Error ? error.message : 'Unknown error';
      return this.buildErrorResult('ERROR', `Error retrieving quiz results: ${msg}`, Params);
    }
  }

  // ----------------------------------------------------------------
  // Private helpers – parameter extraction
  // ----------------------------------------------------------------

  private extractQuizResultsParams(params: ActionParam[]): GetQuizResultsParams {
    return {
      CompanyID: this.getRequiredStringParam(params, 'CompanyID'),
      UserID: this.getOptionalStringParam(params, 'UserID'),
      CourseID: this.getOptionalStringParam(params, 'CourseID'),
      QuizID: this.getOptionalStringParam(params, 'QuizID'),
      IncludeQuestions: this.getOptionalBooleanParam(params, 'IncludeQuestions', true),
      IncludeAnswers: this.getOptionalBooleanParam(params, 'IncludeAnswers', true),
      PassingOnly: this.getOptionalBooleanParam(params, 'PassingOnly', false),
      DateFrom: this.getOptionalStringParam(params, 'DateFrom'),
      DateTo: this.getOptionalStringParam(params, 'DateTo'),
      SortBy: this.getOptionalStringParam(params, 'SortBy'),
      SortOrder: (this.getOptionalStringParam(params, 'SortOrder') || 'desc') as 'asc' | 'desc',
      MaxResults: this.getOptionalNumberParam(params, 'MaxResults', LearnWorldsBaseAction.LW_MAX_PAGE_SIZE),
    };
  }

  // ----------------------------------------------------------------
  // Private helpers – query building
  // ----------------------------------------------------------------

  private buildQuizQueryParams(
    userId: string | undefined,
    quizId: string | undefined,
    passingOnly: boolean,
    dateFrom: string | undefined,
    dateTo: string | undefined,
    sortBy: string,
    sortOrder: string,
    maxResults: number,
  ): LWQuizResultQueryParams {
    const queryParams: LWQuizResultQueryParams = {
      limit: Math.min(maxResults, LearnWorldsBaseAction.LW_MAX_PAGE_SIZE),
      sort: sortBy,
      order: sortOrder,
    };

    if (passingOnly) {
      queryParams.passed = true;
    }
    const parsedFrom = this.safeParseDateToISO(dateFrom);
    if (parsedFrom) {
      queryParams.completed_after = parsedFrom;
    }
    const parsedTo = this.safeParseDateToISO(dateTo);
    if (parsedTo) {
      queryParams.completed_before = parsedTo;
    }
    if (quizId && userId) {
      queryParams.user_id = userId;
    }

    return queryParams;
  }

  private buildQuizEndpoint(userId: string | undefined, courseId: string | undefined, quizId: string | undefined): string {
    if (quizId) {
      return `/quizzes/${quizId}/results`;
    } else if (userId && courseId) {
      return `/users/${userId}/courses/${courseId}/quiz-results`;
    } else if (userId) {
      return `/users/${userId}/quiz-results`;
    } else if (courseId) {
      return `/courses/${courseId}/quiz-results`;
    }
    return '/quiz-results';
  }

  private buildQueryString(queryParams: Record<string, string | number | boolean | undefined>): string {
    const filtered: Record<string, string> = {};
    for (const [key, value] of Object.entries(queryParams)) {
      if (value !== undefined) {
        filtered[key] = String(value);
      }
    }
    const keys = Object.keys(filtered);
    if (keys.length === 0) return '';
    return '?' + new URLSearchParams(filtered).toString();
  }

  private extractQuizResultsArray(response: LWQuizResultsApiResponse): LWRawQuizResult[] {
    const data = response.data;
    if (Array.isArray(data)) {
      return data;
    }
    if (data && typeof data === 'object' && 'data' in data && Array.isArray(data.data)) {
      return data.data;
    }
    return [];
  }

  // ----------------------------------------------------------------
  // Private helpers – processing
  // ----------------------------------------------------------------

  private async processQuizResults(
    rawResults: LWRawQuizResult[],
    userId: string | undefined,
    courseId: string | undefined,
    quizId: string | undefined,
    includeQuestions: boolean,
    includeAnswers: boolean,
    contextUser: UserInfo,
  ): Promise<FormattedQuizResult[]> {
    const formatted: FormattedQuizResult[] = [];

    for (const result of rawResults) {
      const formattedResult = this.buildBaseQuizResult(result, userId, courseId, quizId);

      // Attach user info if available
      if (result.user) {
        formattedResult.user = {
          id: result.user.id || '',
          email: result.user.email || '',
          name: result.user.name || `${result.user.first_name || ''} ${result.user.last_name || ''}`.trim(),
        };
      }

      // Attach quiz info
      formattedResult.quiz = await this.resolveQuizInfo(result, quizId, contextUser);

      // Attach questions & answers if requested
      if (includeQuestions || includeAnswers) {
        await this.attachDetailedResults(formattedResult, includeQuestions, includeAnswers, contextUser);
      }

      formatted.push(formattedResult);
    }

    return formatted;
  }

  private buildBaseQuizResult(
    result: LWRawQuizResult,
    userId: string | undefined,
    courseId: string | undefined,
    quizId: string | undefined,
  ): FormattedQuizResult {
    const score = result.score || 0;
    const maxScore = result.max_score || result.total_points || 100;

    return {
      id: result.id || result.result_id || '',
      userId: result.user_id || userId || '',
      courseId: result.course_id || courseId || '',
      quizId: result.quiz_id || quizId || '',
      attemptNumber: result.attempt_number || result.attempt || 1,
      score,
      maxScore,
      percentage: result.percentage || (score / (maxScore || 100)) * 100,
      passed: result.passed || result.is_passing || false,
      passingScore: result.passing_score || result.passing_percentage || 70,
      startedAt: result.started_at,
      completedAt: result.completed_at || result.submitted_at,
      duration: result.duration || result.time_spent || 0,
      durationText: this.formatDuration(result.duration || result.time_spent || 0),
    };
  }

  private async resolveQuizInfo(
    result: LWRawQuizResult,
    filterQuizId: string | undefined,
    contextUser: UserInfo,
  ): Promise<{ id: string; title: string; type: string; questionCount?: number } | undefined> {
    if (result.quiz) {
      return {
        id: result.quiz.id || '',
        title: result.quiz.title || result.quiz.name || '',
        type: result.quiz.type || 'quiz',
        questionCount: result.quiz.question_count || result.quiz.total_questions,
      };
    }

    if (!filterQuizId && result.quiz_id) {
      const quizResponse = await this.makeLearnWorldsRequest<LWQuizLookup>(`/quizzes/${result.quiz_id}`, 'GET', null, contextUser);

      if (quizResponse.success !== false && quizResponse.data) {
        const quiz = quizResponse.data;
        return {
          id: quiz.id || '',
          title: quiz.title || quiz.name || '',
          type: quiz.type || 'quiz',
          questionCount: quiz.question_count || quiz.total_questions,
        };
      }
    }

    return undefined;
  }

  private async attachDetailedResults(
    formattedResult: FormattedQuizResult,
    includeQuestions: boolean,
    includeAnswers: boolean,
    contextUser: UserInfo,
  ): Promise<void> {
    const detailsResponse = await this.makeLearnWorldsRequest<LWQuizDetailResponse>(`/quiz-results/${formattedResult.id}/details`, 'GET', null, contextUser);

    if (detailsResponse.success === false || !detailsResponse.data) {
      return;
    }

    const details = detailsResponse.data;

    if (includeQuestions) {
      formattedResult.questions = this.formatQuestions(details.questions || details.answers || []);
    }

    if (includeAnswers) {
      formattedResult.answers = this.formatAnswers(details.answers || details.questions || []);

      // Calculate additional metrics
      const answers = formattedResult.answers;
      formattedResult.metrics = this.calculateQuizMetrics(answers);
    }
  }

  // ----------------------------------------------------------------
  // Private helpers – formatting
  // ----------------------------------------------------------------

  /**
   * Format quiz questions
   */
  private formatQuestions(questions: LWRawQuestion[]): FormattedQuestion[] {
    return questions.map((q, index) => ({
      questionNumber: q.question_number || index + 1,
      questionId: q.question_id || q.id || '',
      questionText: q.question_text || q.question || q.text || '',
      questionType: q.question_type || q.type || 'multiple-choice',
      points: q.points || q.score || 1,
      difficulty: q.difficulty || 'medium',
    }));
  }

  /**
   * Format user answers
   */
  private formatAnswers(answers: LWRawAnswer[]): FormattedAnswer[] {
    return answers.map((a, index) => ({
      questionNumber: a.question_number || index + 1,
      questionId: a.question_id,
      userAnswer: this.coerceAnswerValue(a.user_answer || a.answer || a.selected_answer),
      correctAnswer: this.coerceAnswerValue(a.correct_answer),
      isCorrect: a.is_correct || a.correct || false,
      pointsEarned: a.points_earned || (a.is_correct ? a.points || 1 : 0),
      pointsPossible: a.points_possible || a.points || 1,
      feedback: a.feedback,
      timeSpent: a.time_spent,
      answeredAt: a.answered_at,
    }));
  }

  /**
   * Coerce a raw answer value from the API to a typed QuizAnswerValue.
   */
  private coerceAnswerValue(value: unknown): QuizAnswerValue {
    if (value === undefined || value === null) return undefined;
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value;
    if (Array.isArray(value)) return value.map(String);
    return String(value);
  }

  private calculateQuizMetrics(answers: FormattedAnswer[]): QuizMetrics {
    const correctCount = answers.filter((a) => a.isCorrect).length;
    return {
      correctAnswers: correctCount,
      incorrectAnswers: answers.filter((a) => !a.isCorrect).length,
      totalQuestions: answers.length,
      accuracyRate: answers.length > 0 ? Number(((correctCount / answers.length) * 100).toFixed(1)) : 0,
    };
  }

  // ----------------------------------------------------------------
  // Private helpers – summary
  // ----------------------------------------------------------------

  private buildQuizResultsSummary(
    formattedResults: FormattedQuizResult[],
    userId: string | undefined,
    courseId: string | undefined,
    quizId: string | undefined,
    dateFrom: string | undefined,
    dateTo: string | undefined,
  ): QuizResultsSummary {
    const totalResults = formattedResults.length;
    const passedResults = formattedResults.filter((r) => r.passed).length;

    const averageScore = totalResults > 0 ? formattedResults.reduce((sum, r) => sum + r.percentage, 0) / totalResults : 0;

    const averageDuration = totalResults > 0 ? formattedResults.reduce((sum, r) => sum + (r.duration || 0), 0) / totalResults : 0;

    // Group results by quiz if multiple quizzes
    const quizBreakdown = !quizId ? this.buildQuizBreakdown(formattedResults) : null;

    const filterType = userId ? (courseId ? 'user-course' : 'user') : courseId ? 'course' : 'all';

    return {
      totalResults,
      passedResults,
      failedResults: totalResults - passedResults,
      passRate: totalResults > 0 ? Number(((passedResults / totalResults) * 100).toFixed(1)) : 0,
      averageScore: Number(averageScore.toFixed(1)),
      averageDuration,
      averageDurationText: this.formatDuration(Math.round(averageDuration)),
      dateRange: {
        from: dateFrom || 'all-time',
        to: dateTo || 'current',
      },
      filterType,
      quizBreakdown,
    };
  }

  private buildQuizBreakdown(formattedResults: FormattedQuizResult[]): Record<string, QuizBreakdownEntry> {
    const resultsByQuiz: Record<string, QuizBreakdownEntry> = {};

    for (const result of formattedResults) {
      const quizTitle = result.quiz?.title || 'Unknown Quiz';
      if (!resultsByQuiz[quizTitle]) {
        resultsByQuiz[quizTitle] = {
          results: [],
          stats: { attempts: 0, averageScore: 0, passRate: 0 },
        };
      }
      resultsByQuiz[quizTitle].results.push(result);
    }

    // Calculate stats for each quiz
    for (const quizTitle of Object.keys(resultsByQuiz)) {
      const quizResults = resultsByQuiz[quizTitle].results;
      const qTotal = quizResults.length;
      const qPassed = quizResults.filter((r) => r.passed).length;
      const qAvgScore = quizResults.reduce((sum, r) => sum + r.percentage, 0) / qTotal;

      resultsByQuiz[quizTitle].stats = {
        attempts: qTotal,
        averageScore: Number(qAvgScore.toFixed(1)),
        passRate: Number(((qPassed / qTotal) * 100).toFixed(1)),
      };
    }

    return resultsByQuiz;
  }

  // ----------------------------------------------------------------
  // Params & Description metadata
  // ----------------------------------------------------------------

  /**
   * Define the parameters this action expects
   */
  public get Params(): ActionParam[] {
    const baseParams = this.getCommonLMSParams();
    const specificParams: ActionParam[] = [
      { Name: 'UserID', Type: 'Input', Value: null },
      { Name: 'CourseID', Type: 'Input', Value: null },
      { Name: 'QuizID', Type: 'Input', Value: null },
      { Name: 'IncludeQuestions', Type: 'Input', Value: true },
      { Name: 'IncludeAnswers', Type: 'Input', Value: true },
      { Name: 'PassingOnly', Type: 'Input', Value: false },
      { Name: 'DateFrom', Type: 'Input', Value: null },
      { Name: 'DateTo', Type: 'Input', Value: null },
      { Name: 'SortBy', Type: 'Input', Value: 'completed_at' },
      { Name: 'SortOrder', Type: 'Input', Value: 'desc' },
      { Name: 'MaxResults', Type: 'Input', Value: 100 },
      { Name: 'QuizResults', Type: 'Output', Value: null },
      { Name: 'TotalCount', Type: 'Output', Value: null },
      { Name: 'Summary', Type: 'Output', Value: null },
    ];
    return [...baseParams, ...specificParams];
  }

  /**
   * Metadata about this action
   */
  public get Description(): string {
    return 'Retrieves quiz and assessment results from LearnWorlds with detailed question/answer information';
  }
}
