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 { UpdateUserProgressParams, UpdateUserProgressResult, ProgressDetails, ProgressUpdateSummary, ProgressUpdateResult } from '../interfaces';

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

/** Shape returned by the enrollment / progress GET endpoints */
interface LWEnrollmentProgress {
  progress_percentage?: number;
  completed_units?: number;
  total_units?: number;
  total_time_spent?: number;
  last_accessed_at?: string;
  completed?: boolean;
  completed_at?: string;
}

/** Request body sent to the lesson-level progress PUT endpoint */
interface LWLessonProgressRequest {
  completed: boolean;
  progress_percentage?: number;
  time_spent?: number;
  score?: number;
  notes?: string;
}

/** Request body sent to the course-level progress PUT endpoint */
interface LWCourseProgressRequest {
  progress_percentage?: number;
  completed?: boolean;
  total_time_spent?: number;
}


/**
 * Action to update a user's course progress in LearnWorlds
 */
@RegisterClass(BaseAction, 'UpdateUserProgressAction')
export class UpdateUserProgressAction extends LearnWorldsBaseAction {
  // ----------------------------------------------------------------
  // Typed public method – can be called directly from code
  // ----------------------------------------------------------------

  /**
   * Update user progress for a course or lesson.
   * Throws on any error.
   */
  public async UpdateProgress(params: UpdateUserProgressParams, contextUser: UserInfo): Promise<UpdateUserProgressResult> {
    this.SetCompanyContext(params.CompanyID);

    const { UserID, CourseID, LessonID, ProgressPercentage, Completed, TimeSpent, Score, Notes } = params;

    // Validate required fields
    if (!UserID) {
      throw new Error('UserID is required');
    }
    if (!CourseID) {
      throw new Error('CourseID is required');
    }

    this.validatePathSegment(UserID, 'UserID');
    this.validatePathSegment(CourseID, 'CourseID');
    if (LessonID) {
      this.validatePathSegment(LessonID, 'LessonID');
    }

    if (ProgressPercentage !== undefined && (ProgressPercentage < 0 || ProgressPercentage > 100)) {
      throw new Error('ProgressPercentage must be between 0 and 100');
    }

    // Get current enrollment first
    const currentEnrollment = await this.fetchCurrentEnrollment(UserID, CourseID, contextUser);

    const updateResult: ProgressUpdateResult = {};

    // Update lesson progress if lessonId is provided
    if (LessonID) {
      updateResult.lessonProgress = await this.updateLessonProgress(
        UserID,
        CourseID,
        LessonID,
        { Completed, ProgressPercentage, TimeSpent, Score, Notes },
        contextUser,
      );
    }

    // Update overall course progress if progressPercentage is provided at course level
    if (ProgressPercentage !== undefined && !LessonID) {
      updateResult.courseProgress = await this.updateCourseProgress(
        UserID,
        CourseID,
        currentEnrollment,
        { Completed, ProgressPercentage, TimeSpent },
        contextUser,
      );
    }

    // Get updated enrollment details
    const updatedProgress = await this.fetchUpdatedEnrollment(UserID, CourseID, currentEnrollment, contextUser);

    // Build typed result
    const updateType: 'lesson' | 'course' = LessonID ? 'lesson' : 'course';

    const progressDetails = this.buildProgressDetails(UserID, CourseID, LessonID, currentEnrollment, updatedProgress, updateType, updateResult);

    const summary = this.buildProgressSummary(UserID, CourseID, LessonID, currentEnrollment, updatedProgress, TimeSpent, updateType);

    return { ProgressDetails: progressDetails, 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.extractUpdateProgressParams(Params);
      const result = await this.UpdateProgress(typedParams, ContextUser);

      this.setOutputParam(Params, 'ProgressDetails', result.ProgressDetails);
      this.setOutputParam(Params, 'Summary', result.Summary);

      return this.buildSuccessResult(`Successfully updated ${result.ProgressDetails.updateType} progress`, Params);
    } catch (error) {
      const msg = error instanceof Error ? error.message : 'Unknown error';
      return this.buildErrorResult('ERROR', `Error updating progress: ${msg}`, Params);
    }
  }

  // ----------------------------------------------------------------
  // Private helpers
  // ----------------------------------------------------------------

  private extractUpdateProgressParams(params: ActionParam[]): UpdateUserProgressParams {
    return {
      CompanyID: this.getRequiredStringParam(params, 'CompanyID'),
      UserID: this.getRequiredStringParam(params, 'UserID'),
      CourseID: this.getRequiredStringParam(params, 'CourseID'),
      LessonID: this.getOptionalStringParam(params, 'LessonID'),
      ProgressPercentage: this.getOptionalNumberParam(params, 'ProgressPercentage', undefined),
      Completed: this.getOptionalBooleanParam(params, 'Completed', undefined),
      TimeSpent: this.getOptionalNumberParam(params, 'TimeSpent', undefined),
      Score: this.getOptionalNumberParam(params, 'Score', undefined),
      Notes: this.getOptionalStringParam(params, 'Notes'),
    };
  }

  private async fetchCurrentEnrollment(userId: string, courseId: string, contextUser: UserInfo): Promise<LWEnrollmentProgress> {
    try {
      return await this.makeLearnWorldsRequest<LWEnrollmentProgress>(`users/${userId}/enrollments/${courseId}`, 'GET', undefined, contextUser);
    } catch (error) {
      throw new Error('User is not enrolled in this course: ' + (error instanceof Error ? error.message : String(error)));
    }
  }

  private async updateLessonProgress(
    userId: string,
    courseId: string,
    lessonId: string,
    data: { Completed?: boolean; ProgressPercentage?: number; TimeSpent?: number; Score?: number; Notes?: string },
    contextUser: UserInfo,
  ): Promise<Record<string, unknown>> {
    const body: LWLessonProgressRequest = {
      completed: data.Completed !== undefined ? data.Completed : false,
      progress_percentage: data.ProgressPercentage,
    };

    if (data.TimeSpent !== undefined) {
      body.time_spent = data.TimeSpent;
    }
    if (data.Score !== undefined) {
      body.score = data.Score;
    }
    if (data.Notes) {
      body.notes = data.Notes;
    }

    return await this.makeLearnWorldsRequest<Record<string, unknown>>(
      `users/${userId}/courses/${courseId}/lessons/${lessonId}/progress`,
      'PUT',
      body,
      contextUser,
    );
  }

  private async updateCourseProgress(
    userId: string,
    courseId: string,
    currentEnrollment: LWEnrollmentProgress,
    data: { Completed?: boolean; ProgressPercentage?: number; TimeSpent?: number },
    contextUser: UserInfo,
  ): Promise<Record<string, unknown>> {
    const body: LWCourseProgressRequest = {
      progress_percentage: data.ProgressPercentage,
    };

    if (data.Completed !== undefined) {
      body.completed = data.Completed;
    }
    if (data.TimeSpent !== undefined) {
      body.total_time_spent = (currentEnrollment.total_time_spent || 0) + data.TimeSpent;
    }

    return await this.makeLearnWorldsRequest<Record<string, unknown>>(
      `users/${userId}/enrollments/${courseId}/progress`,
      'PUT',
      body,
      contextUser,
    );
  }

  private async fetchUpdatedEnrollment(userId: string, courseId: string, fallback: LWEnrollmentProgress, contextUser: UserInfo): Promise<LWEnrollmentProgress> {
    try {
      return await this.makeLearnWorldsRequest<LWEnrollmentProgress>(`users/${userId}/enrollments/${courseId}`, 'GET', undefined, contextUser);
    } catch (error) {
      // If we can't get updated details, use previous enrollment
      console.warn('Failed to get updated enrollment details:', error);
      return fallback;
    }
  }

  private buildProgressDetails(
    userId: string,
    courseId: string,
    lessonId: string | undefined,
    currentEnrollment: LWEnrollmentProgress,
    updatedProgress: LWEnrollmentProgress,
    updateType: 'lesson' | 'course',
    updateResult: ProgressUpdateResult,
  ): ProgressDetails {
    return {
      userId,
      courseId,
      lessonId,
      previousProgress: {
        percentage: currentEnrollment.progress_percentage || 0,
        completedUnits: currentEnrollment.completed_units || 0,
        totalTimeSpent: currentEnrollment.total_time_spent || 0,
      },
      updatedProgress: {
        percentage: updatedProgress.progress_percentage || 0,
        completedUnits: updatedProgress.completed_units || 0,
        totalUnits: updatedProgress.total_units || 0,
        totalTimeSpent: updatedProgress.total_time_spent || 0,
        totalTimeSpentText: this.formatDuration(updatedProgress.total_time_spent || 0),
        lastAccessedAt: updatedProgress.last_accessed_at || new Date().toISOString(),
        completed: updatedProgress.completed || false,
        completedAt: updatedProgress.completed_at,
      },
      updateType,
      updateResult,
    };
  }

  private buildProgressSummary(
    userId: string,
    courseId: string,
    lessonId: string | undefined,
    currentEnrollment: LWEnrollmentProgress,
    updatedProgress: LWEnrollmentProgress,
    timeSpent: number | undefined,
    updateType: 'lesson' | 'course',
  ): ProgressUpdateSummary {
    return {
      userId,
      courseId,
      lessonId,
      progressIncreased: (updatedProgress.progress_percentage || 0) > (currentEnrollment.progress_percentage || 0),
      previousPercentage: currentEnrollment.progress_percentage || 0,
      newPercentage: updatedProgress.progress_percentage || 0,
      timeAdded: timeSpent || 0,
      totalTimeSpent: updatedProgress.total_time_spent || 0,
      isCompleted: updatedProgress.completed || false,
      updateType,
    };
  }

  // ----------------------------------------------------------------
  // 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: 'LessonID', Type: 'Input', Value: null },
      { Name: 'ProgressPercentage', Type: 'Input', Value: null },
      { Name: 'Completed', Type: 'Input', Value: null },
      { Name: 'TimeSpent', Type: 'Input', Value: null },
      { Name: 'Score', Type: 'Input', Value: null },
      { Name: 'Notes', Type: 'Input', Value: null },
      { Name: 'ProgressDetails', Type: 'Output', Value: null },
      { Name: 'Summary', Type: 'Output', Value: null },
    ];
    return [...baseParams, ...specificParams];
  }

  /**
   * Metadata about this action
   */
  public get Description(): string {
    return "Updates a user's progress for a course or specific lesson in LearnWorlds";
  }
}
