<?php

namespace Yuvayana\Acadlix\Common\REST\Front;

use WP_REST_Server;
use WP_Error;

defined('ABSPATH') || exit();

class FrontQuizController
{
  protected $namespace = 'acadlix/v1';
  protected $base = 'front-quiz';

  public function register_routes()
  {

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/(?P<quiz_id>[\d]+)',
      [
        [
          'methods' => WP_REST_Server::READABLE,
          'callback' => [$this, 'get_front_quiz_by_id'],
          'permission_callback' => [$this, 'check_permission'],
          'args' => array(
            'quiz_id' => array(
              'validate_callback' => function ($param, $request, $key) {
                return is_numeric($param);
              }
            ),
          ),
        ],
        [
          'methods' => WP_REST_Server::EDITABLE,
          'callback' => [$this, 'post_save_result_by_id'],
          'permission_callback' => [$this, 'check_permission'],
          'args' => array(
            'quiz_id' => array(
              'validate_callback' => function ($param, $request, $key) {
                return is_numeric($param);
              }
            ),
          ),
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/(?P<quiz_id>[\d]+)' . '/post-upload-assessment-file',
      [
        'methods' => WP_REST_Server::EDITABLE,
        'callback' => [$this, 'post_upload_assessment_file_by_id'],
        'permission_callback' => [$this, 'check_permission'],
        'args' => array(
          'quiz_id' => array(
            'validate_callback' => function ($param, $request, $key) {
              return is_numeric($param);
            }
          ),
        ),
      ],
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/(?P<quiz_id>[\d]+)' . '/post-delete-assessment-file',
      [
        'methods' => WP_REST_Server::EDITABLE,
        'callback' => [$this, 'post_delete_assessment_file_by_id'],
        'permission_callback' => [$this, 'check_permission'],
        'args' => array(
          'quiz_id' => array(
            'validate_callback' => function ($param, $request, $key) {
              return is_numeric($param);
            }
          ),
        ),
      ],
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/(?P<quiz_id>[\d]+)' . '/save-quiz-attempt',
      [
        'methods' => WP_REST_Server::EDITABLE,
        'callback' => [$this, 'post_save_quiz_attempt_by_id'],
        'permission_callback' => [$this, 'check_permission'],
        'args' => array(
          'quiz_id' => array(
            'validate_callback' => function ($param, $request, $key) {
              return is_numeric($param);
            }
          ),
        ),
      ],
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/(?P<quiz_id>[\d]+)' . '/leaderboard',
      [
        'methods' => WP_REST_Server::READABLE,
        'callback' => [$this, 'get_front_quiz_leaderboard_by_id'],
        'permission_callback' => [$this, 'check_permission'],
        'args' => array(
          'quiz_id' => array(
            'validate_callback' => function ($param, $request, $key) {
              return is_numeric($param);
            }
          ),
        ),
      ],
    );



    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/load-more-leaderboard/(?P<quiz_id>[\d]+)',
      [
        [
          'methods' => WP_REST_Server::EDITABLE,
          'callback' => [$this, 'post_load_more_toplist_by_id'],
          'permission_callback' => [$this, 'check_permission'],
          'args' => array(
            'quiz_id' => array(
              'validate_callback' => function ($param, $request, $key) {
                return is_numeric($param);
              }
            ),
          ),
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/(?P<quiz_id>[\d]+)/check-quiz',
      [
        [
          'methods' => WP_REST_Server::EDITABLE,
          'callback' => [$this, 'post_check_quiz_by_id'],
          'permission_callback' => [$this, 'check_permission'],
          'args' => array(
            'quiz_id' => array(
              'validate_callback' => function ($param, $request, $key) {
                return is_numeric($param);
              }
            ),
          ),
        ],
      ]
    );
  }

  public function get_front_quiz_by_id($request)
  {
    // 1) Pre-parameter filter: allow callers to mutate incoming params before validation
    $params = apply_filters('acadlix_pre_get_front_quiz_by_id_params', $request->get_params(), $request);

    $res = [];
    $quiz_id = $params['quiz_id'] ?? $request['quiz_id'] ?? $request->get_param('quiz_id');
    $user_id = get_current_user_id() ?? 0;
    $user_token = $params['user_token'] ?? $request->get_param('user_token') ?? '';

    // Validate required fields
    if (empty($quiz_id)) {
      return new WP_Error(
        'missing_id',
        __('Quiz id is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    // 2) Payload/Data Filter: normalize values before core execution
    $payload = apply_filters('acadlix_get_front_quiz_by_id_payload', [
      'quiz_id' => intval($quiz_id),
      'user_id' => intval($user_id),
      'user_token' => $user_token,
    ], $params);

    $appends = apply_filters('acadlix_get_front_quiz_by_id_quiz_appends', [
      'rendered_post_content',
      'rendered_metas',
      'category',
      'languages',
      'rendered_questions',
    ], $payload['quiz_id'], $params);

    $quiz = acadlix()->model()->quiz()->ofQuiz()->find($payload['quiz_id'])->setAppends($appends);
    $quiz_attempt = null;
    if ($payload['user_id'] > 0) {
      $quiz_attempt = acadlix()->model()->userActivityMeta()->ofQuiz()
        ->ofQuizAttempt()
        ->where('type_id', $payload['quiz_id'])
        ->where('user_id', $payload['user_id'])
        ->first();
    }

    if ($payload['user_id'] == 0 && $payload['user_token'] != '') {
      $quiz_attempt = acadlix()->model()->userActivityMeta()->ofQuiz()
        ->ofQuizAttempt()
        ->where('type_id', $payload['quiz_id'])
        ->where('user_token', $payload['user_token'])
        ->first();
    }

    $custom_logo_id = get_theme_mod('custom_logo');
    $res = [
      'logo' => wp_get_attachment_image_url($custom_logo_id, 'full'),
      'quiz' => $quiz,
      'quiz_attempt' => $quiz_attempt->meta_value ?? 0,
    ];

    // 3) Lifecycle Action: expose retrieval to listeners
    do_action('acadlix_get_front_quiz_by_id', $res, $quiz, $payload, $params);

    // 4) Outbound Response Filter: allow response augmentation
    $res = apply_filters('acadlix_get_front_quiz_by_id_response', $res, $quiz, $payload, $params);

    return rest_ensure_response($res);
  }

  public function post_check_quiz_by_id($request)
  {
    // 1) Pre-parameter filter: allow callers to mutate incoming params before validation
    $params = apply_filters('acadlix_pre_post_check_quiz_by_id_params', $request->get_json_params(), $request);

    $res = [];
    $errors = [];
    $quiz_id = $params['quiz_id'] ?? $request['quiz_id'] ?? $request->get_param('quiz_id');

    if (empty($quiz_id)) {
      return new WP_Error(
        'missing_id',
        __('Quiz id is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    // 2) Payload/Data Filter: normalize values before checking
    $payload = apply_filters('acadlix_post_check_quiz_by_id_payload', [
      'quiz_id' => intval($quiz_id),
      'user_id' => intval($params['user_id'] ?? 0),
      'user_token' => $params['user_token'] ?? '',
    ], $params);

    $quiz = acadlix()->model()->quiz()->ofQuiz()->find($payload['quiz_id']);

    // check quiz attempt
    $per_user_allowed_attempt = $quiz->rendered_metas['quiz_settings']['per_user_allowed_attempt'] ?? 0;
    if ($per_user_allowed_attempt > 0) {
      $user_attempts = acadlix()->model()->userActivityMeta()->ofQuiz()
        ->ofQuizAttempt()
        ->where('type_id', $payload['quiz_id'])
        ->when($payload['user_id'] > 0, fn($query) => $query->where('user_id', $payload['user_id']))
        ->when($payload['user_id'] == 0 && $payload['user_token'] != '', fn($query) => $query->where('user_token', $payload['user_token']))
        ->first();

      if ($user_attempts && $user_attempts->meta_value >= $per_user_allowed_attempt) {
        $errors[] = __('You have reached your maximum limit of attempts.', 'acadlix');
      }
    }

    // Handle error markup
    $html = '';
    if (count($errors) > 0) {
      $html .= "<ul>";
      foreach ($errors as $error) {
        $html .= "<li>{$error}</li>";
      }
      $html .= "</ul>";
    }

    $res['errors'] = $html;

    // 3) Lifecycle Action: allow listeners to react to check result
    do_action('acadlix_post_check_quiz_by_id', $res, $quiz, $payload, $params);

    // 4) Outbound Response Filter: allow other modules to modify the response
    $res = apply_filters('acadlix_post_check_quiz_by_id_response', $res, $quiz, $payload, $params);

    return rest_ensure_response($res);
  }

  public function post_save_quiz_attempt_by_id($request)
  {
    // 1) Pre-parameter filter: allow callers to mutate incoming params before validation
    $params = apply_filters('acadlix_pre_post_save_quiz_attempt_by_id_params', $request->get_json_params(), $request);
    $res = [];
    $quiz_id = $params['quiz_id'] ?? $request['quiz_id'] ?? $request->get_param('quiz_id');

    if (empty($quiz_id)) {
      return new WP_Error(
        'missing_id',
        __('Quiz id is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    // 2) Payload/Data Filter: normalize values before core execution
    $payload = apply_filters('acadlix_post_save_quiz_attempt_by_id_payload', [
      'quiz_id' => intval($quiz_id),
      'user_id' => intval($params['user_id'] ?? 0),
      'user_token' => $params['user_token'] ?? '',
      'quiz_attempt_type' => $params['quiz_attempt_type'] ?? 'shortcode',
      'course_statistic_id' => intval($params['course_statistic_id'] ?? 0),
    ], $params);

    $attempts = acadlix()->model()->userActivityMeta()->ofQuiz()
      ->ofQuizAttempt()
      ->where('type_id', $payload['quiz_id'])
      ->when($payload['user_id'] > 0, fn($query) => $query->where('user_id', $payload['user_id']))
      ->when($payload['user_id'] == 0 && $payload['user_token'] !== '', fn($query) => $query->where('user_token', $payload['user_token']))
      ->first();

    if ($attempts) {
      $attempts->update([
        'meta_value' => (int) $attempts->meta_value + 1, // phpcs:ignore
      ]);
      do_action('acadlix_post_save_quiz_attempt_by_id_updated', $attempts, $payload, $params);
    } else {
      $attempts = acadlix()->model()->userActivityMeta()->create([
        'user_token' => $payload['user_token'],
        'user_id' => $payload['user_id'],
        'type' => 'quiz',
        'type_id' => $payload['quiz_id'],
        'meta_key' => 'quiz_attempt', // phpcs:ignore
        'meta_value' => 1, // phpcs:ignore
      ]);
      do_action('acadlix_post_save_quiz_attempt_by_id_created', $attempts, $payload, $params);
    }

    if ($payload['quiz_attempt_type'] === 'course_statistic' && $payload['course_statistic_id']) {
      acadlix()->model()->userActivityMeta()
        ->create([
          'user_token' => $payload['user_token'],
          'user_id' => $payload['user_id'],
          'type' => $payload['quiz_attempt_type'],
          'type_id' => $payload['course_statistic_id'],
          'meta_key' => 'course_statistic_quiz_attempt_time', // phpcs:ignore
          'meta_value' => time(), // phpcs:ignore
        ]);
    }

    $res['attempts'] = $attempts;

    // 3) Lifecycle Action: allow listeners to react to save attempt
    do_action('acadlix_post_save_quiz_attempt_by_id', $res, $attempts, $payload, $params);

    // 4) Outbound Response Filter: allow other modules to modify the response
    $res = apply_filters('acadlix_post_save_quiz_attempt_by_id_response', $res, $attempts, $payload, $params);

    return rest_ensure_response($res);
  }

  public function post_save_result_by_id($request)
  {
    // 1) Pre-parameter filter: allow callers to mutate incoming params before validation
    $params = apply_filters('acadlix_pre_post_save_result_by_id_params', $request->get_json_params(), $request);
    $quiz_id = $params['quiz_id'] ?? $request['quiz_id'] ?? $request->get_param('quiz_id');

    if (empty($quiz_id)) {
      return new WP_Error(
        'missing_id',
        __('Quiz id is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    $quiz = acadlix()->model()->quiz()->ofQuiz()->find($quiz_id);
    if (!$quiz) {
      return new WP_Error(
        'invalid_quiz',
        __('Quiz not found.', 'acadlix'),
        ['status' => 404]
      );
    }

    // Remote IP extraction securely
    $remote_addr = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
    $ip = filter_var($remote_addr, FILTER_VALIDATE_IP) ?: '';

    // 2) Payload/Data Filter: normalize values before core execution
    $payload = apply_filters('acadlix_post_save_result_by_id_payload', [
      'quiz_id' => intval($quiz_id),
      'user_id' => intval($params['user_id'] ?? 0),
      'user_token' => $params['user_token'] ?? '',
      'quiz_attempt_type' => $params['quiz_attempt_type'] ?? 'shortcode',
      'course_statistic_id' => intval($params['course_statistic_id'] ?? 0),
      'points' => (float) ($params['points'] ?? 0),
      'result' => (float) ($params['result'] ?? 0),
      'quiz_time' => intval($params['time_taken'] ?? 0),
      'accuracy' => (float) ($params['accuracy'] ?? 0),
      'status' => $params['status'] ?? '',
      'ip' => $ip,
    ], $params);

    // Standardized data matrix
    $data = [
      'quiz_id' => $payload['quiz_id'],
      'user_token' => $payload['user_token'],
      'user_id' => $payload['user_id'],
      'points' => $payload['points'],
      'result' => $payload['result'],
      'quiz_time' => $payload['quiz_time'],
      'accuracy' => $payload['accuracy'],
      'status' => $payload['status'],
    ];

    $res = [];
    $settings = $quiz->rendered_metas['quiz_settings'] ?? [];
    $stat_ref = null;
    $top = null;

    // Handle Statistics Saving
    $save_statistic = $settings['save_statistic'] ?? false;
    $statistic_ip_lock = $settings['statistic_ip_lock'] ?? 0;
    $save_statistic_number_of_times = $settings['save_statistic_number_of_times'] ?? 0;

    if ($save_statistic) {
      $statistic_ref = acadlix()->model()->statisticRef()->where('quiz_id', $payload['quiz_id'])
        ->when($payload['user_id'] > 0, fn($query) => $query->where('user_id', $payload['user_id']))
        ->when($payload['user_id'] == 0 && $payload['user_token'] !== '', fn($query) => $query->where('user_token', $payload['user_token']));

      $latest_stat = $statistic_ref->latest()->first();
      $check_ip_lock = $statistic_ip_lock == 0 || !$latest_stat || round((time() - strtotime($latest_stat->created_at)) / 60) > $statistic_ip_lock;
      $check_multiple_entry = $save_statistic_number_of_times == 0 || $save_statistic_number_of_times > $statistic_ref->count();

      if ($statistic_ref->count() == 0 || ($check_ip_lock && $check_multiple_entry)) {
        $stat_ref = acadlix()->model()->statisticRef()->create($data);

        if (!empty($params['questions']) && is_array($params['questions'])) {
          foreach ($params['questions'] as $question) {
            $q_res = $question['result'] ?? [];
            $stat_ref?->statistics()?->create([
              'question_id' => $question['question_id'],
              'correct_count' => $q_res['correct_count'] ?? 0,
              'incorrect_count' => $q_res['incorrect_count'] ?? 0,
              'hint_count' => $q_res['hint_count'] ?? 0,
              'solved_count' => $q_res['solved_count'] ?? 0,
              'points' => $q_res['points'] ?? 0,
              'negative_points' => $q_res['negative_points'] ?? 0,
              'question_time' => $q_res['time'] ?? 0,
              'answer_data' => $q_res['answer_data'] ?? '',
              'attempted_at' => !empty($q_res['attempted_at']) ? (int) $q_res['attempted_at'] : null,
              'is_evaluated' => $q_res['is_evaluated'] ?? 0,
              'evaluated_by' => $q_res['evaluated_by'] ?? null,
              'evaluated_id' => $q_res['evaluated_id'] ?? null,
              'evaluation_remark' => $q_res['evaluation_remark'] ?? '',
            ]);
          }
        }

        // Save course stats correlation
        if ($payload['quiz_attempt_type'] === 'course_statistic' && $payload['course_statistic_id'] && $stat_ref) {
          acadlix()->model()->userActivityMeta()->create([
            'user_token' => $payload['user_token'],
            'user_id' => $payload['user_id'],
            'type' => $payload['quiz_attempt_type'],
            'type_id' => $payload['course_statistic_id'],
            'meta_key' => 'statistic_ref_id',
            'meta_value' => $stat_ref->id,
          ]);
        }

        do_action('acadlix_post_save_result_by_id_statistic_saved', $stat_ref, $payload, $params, $quiz);
      }

      $show_average_score = $settings['show_average_score'] ?? false;
      $global_stat_ref = acadlix()->model()->statisticRef()->where('quiz_id', $payload['quiz_id']);
      $res['average_score'] = $global_stat_ref->count() > 0 && $show_average_score ? (float) $global_stat_ref->avg('points') : 0;
    }

    // Handle Leaderboard / Toplist
    $leaderboard = $settings['leaderboard'] ?? false;
    $leaderboard_user_can_apply_multiple_times = $settings['leaderboard_user_can_apply_multiple_times'] ?? false;
    $leaderboard_apply_multiple_number_of_times = $settings['leaderboard_apply_multiple_number_of_times'] ?? 0;

    if ($leaderboard) {
      $toplist_count = acadlix()->model()->toplist()->where('quiz_id', $payload['quiz_id'])
        ->when($payload['user_id'] > 0, fn($query) => $query->where('user_id', $payload['user_id']))
        ->when($payload['user_id'] == 0 && $payload['user_token'] !== '', fn($query) => $query->where('user_token', $payload['user_token']))
        ->count();

      $check_multiple_leaderboard_entry = $leaderboard_user_can_apply_multiple_times && ($leaderboard_apply_multiple_number_of_times == 0 || $leaderboard_apply_multiple_number_of_times > $toplist_count);

      if ($toplist_count == 0 || $check_multiple_leaderboard_entry) {
        $top = acadlix()->model()->toplist()->create([
          ...$data,
          'name' => $params['name'] ?? '',
          'email' => $params['email'] ?? '',
          'ip' => $payload['ip'],
        ]);
        do_action('acadlix_post_save_result_by_id_toplist_created', $top, $payload, $params, $quiz);
      }

      $res['toplist_id'] = $top?->id ?? 0;
      $toplist = acadlix()->model()->toplist();
      $entries_limit = $settings['leaderboard_total_number_of_entries'] ?? 10;
      $limit = ($entries_limit > 0 && $entries_limit < 10) ? $entries_limit : 10;

      $res['toplist'] = $toplist->getTopList($payload['quiz_id'], 0, $limit);
      $res['toplist_count'] = $toplist->where('quiz_id', $payload['quiz_id'])->count();
    }

    // Handle Email Transmissions
    if ($payload['user_id'] > 0) {
      $email_placeholders = apply_filters('acadlix_post_save_result_by_id_email_placeholders', [
        '$userId' => $payload['user_id'],
        '$username' => $params['name'] ?? __('Anonymous', 'acadlix'),
        '$quizname' => $quiz->post_title ?? '',
        '$result' => number_format($payload['result'], 2) . '%',
        '$points' => $payload['points'],
        '$ip' => $payload['ip'],
        '$categories' => '',
        '$subjects' => '',
      ], $payload, $params, $quiz);

      // Admin Email
      if ($settings['admin_email_notification'] ?? false) {
        $admin_msg = str_replace(array_keys($email_placeholders), $email_placeholders, $settings['admin_message'] ?? '');
        acadlix()->helper()->email()->sendEmail(
          $settings['admin_to'] ?? '',
          $settings['admin_subject'] ?? '',
          $admin_msg,
          $settings['admin_from'] ?? ''
        );
      }

      // Student Email
      if (($settings['student_email_notification'] ?? false) && !empty($params['email'])) {
        $student_msg = str_replace(array_keys($email_placeholders), $email_placeholders, $settings['student_message'] ?? '');
        acadlix()->helper()->email()->sendEmail(
          $params['email'],
          $settings['student_subject'] ?? '',
          $student_msg,
          $settings['student_from'] ?? ''
        );
      }

      do_action('acadlix_post_save_result_by_id_emails_sent', $payload, $params, $quiz);
    }

    // 3) Lifecycle Action: allow listeners to react to result save
    do_action('acadlix_post_save_result_by_id', $res, $payload, $params, $quiz);

    // 4) Outbound Response Filter: allow other modules to modify the response
    $res = apply_filters('acadlix_post_save_result_by_id_response', $res, $payload, $params, $quiz);

    return rest_ensure_response($res);
  }

  public function post_upload_assessment_file_by_id($request)
  {
    // 1) Pre-parameter filter: allow callers to mutate incoming params before validation
    $params = apply_filters('acadlix_pre_post_upload_assessment_file_by_id_params', $request->get_json_params(), $request);
    $quiz_id = $params['quiz_id'] ?? $request['quiz_id'] ?? $request->get_param('quiz_id');

    if (empty($quiz_id)) {
      return new WP_Error(
        'missing_id',
        __('Quiz id is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    $files = $request->get_file_params();

    if (empty($files['files'])) {
      return new WP_Error('no_file', __('No file uploaded', 'acadlix'), ['status' => 400]);
    }

    // 2) Payload/Data Filter: normalize values before core execution
    $payload = apply_filters('acadlix_post_upload_assessment_file_by_id_payload', [
      'quiz_id' => intval($quiz_id),
      'custom_subdir' => '/acadlix-assessment',
      'upload_overrides' => ['test_form' => false],
    ], $params);

    // Include WordPress upload functions
    require_once ABSPATH . 'wp-admin/includes/file.php';

    // Get the base uploads directory
    $upload_dir = wp_upload_dir();
    $custom_dir_path = $upload_dir['basedir'] . $payload['custom_subdir'];

    // Create the folder if it doesn't exist
    if (!file_exists($custom_dir_path)) {
      if (!wp_mkdir_p($custom_dir_path)) {
        return new WP_Error('mkdir_failed', __('Failed to create assessment folder.', 'acadlix'), ['status' => 500]);
      }
    }

    // Loop through each file
    $file_count = count($files['files']['name']);
    $new_file = [];

    for ($i = 0; $i < $file_count; $i++) {
      $file = [
        'name' => $files['files']['name'][$i],
        'type' => $files['files']['type'][$i],
        'tmp_name' => $files['files']['tmp_name'][$i],
        'error' => $files['files']['error'][$i],
        'size' => $files['files']['size'][$i],
      ];

      $original_filename = sanitize_file_name($file['name']);
      $timestamp = time();
      $extension = pathinfo($original_filename, PATHINFO_EXTENSION);
      $filename_wo_ext = pathinfo($original_filename, PATHINFO_FILENAME);
      $file['name'] = "{$filename_wo_ext}_{$timestamp}.{$extension}";

      // Set upload_dir filter for each file
      add_filter('upload_dir', function ($dirs) use ($payload) {
        $dirs['subdir'] = $payload['custom_subdir'];
        $dirs['path'] = $dirs['basedir'] . $payload['custom_subdir'];
        $dirs['url'] = $dirs['baseurl'] . $payload['custom_subdir'];
        return $dirs;
      });

      $result = wp_handle_upload($file, $payload['upload_overrides']);

      // Remove the filter (important when looping)
      remove_filter('upload_dir', '__return_custom_assignment_dir');

      if ($result && !isset($result['error'])) {
        $file_data = [
          'file_name' => $file['name'],
          'file_size' => $file['size'],
          'file_extension' => $extension,
          'file_url' => $result['url'],
          'file_path' => $result['file'],
          'file_type' => $result['type'],
        ];

        $new_file[] = $file_data;

        // 3) Lifecycle Action: allow listeners to react to file upload
        do_action('acadlix_post_upload_assessment_file_by_id_file_uploaded', $file_data, $payload, $params);
      } else {
        return new WP_Error('upload_failed', __('Failed to upload file.', 'acadlix'), ['status' => 500]);
      }
    }

    $res = [
      'success' => true,
      'answer_attachments' => $new_file,
    ];

    // 4) Outbound Response Filter: allow other modules to modify the response
    $res = apply_filters('acadlix_post_upload_assessment_file_by_id_response', $res, $new_file, $payload, $params);

    return rest_ensure_response($res);
  }

  public function post_delete_assessment_file_by_id($request)
  {
    // 1) Pre-parameter filter: allow callers to mutate incoming params before validation
    $params = apply_filters('acadlix_pre_post_delete_assessment_file_by_id_params', $request->get_json_params(), $request);
    $quiz_id = $params['quiz_id'] ?? $request['quiz_id'] ?? $request->get_param('quiz_id');

    if (empty($quiz_id)) {
      return new WP_Error(
        'missing_id',
        __('Quiz id is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    // 2) Payload/Data Filter: normalize values before core execution
    $payload = apply_filters('acadlix_post_delete_assessment_file_by_id_payload', [
      'quiz_id' => intval($quiz_id),
      'delete_file_data' => $params['delete_file_data'] ?? [],
      'answer_attachments' => $params['answer_attachments'] ?? [],
    ], $params);

    $file_path = $payload['delete_file_data']['file_path'] ?? '';

    if (empty($file_path)) {
      return new WP_Error(
        'missing_file_path',
        __('File path is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    $deleted = false;
    if (file_exists($file_path)) {
      $deleted = wp_delete_file($file_path);
      if (!$deleted) {
        return new WP_Error('file_not_deleted', __('File not deleted', 'acadlix'), ['status' => 500]);
      }

      // 3) Lifecycle Action: allow listeners to react to file deletion
      do_action('acadlix_post_delete_assessment_file_by_id_file_deleted', $file_path, $payload, $params);
    }

    $res = [
      'success' => true,
      'answer_attachments' => $payload['answer_attachments'] ?? null,
    ];

    // 4) Outbound Response Filter: allow other modules to modify the response
    $res = apply_filters('acadlix_post_delete_assessment_file_by_id_response', $res, $deleted, $payload, $params);

    return rest_ensure_response($res);
  }

  public function get_front_quiz_leaderboard_by_id($request)
  {
    // 1) Pre-parameter filter: allow callers to mutate incoming params before validation
    $params = apply_filters('acadlix_pre_get_front_quiz_leaderboard_by_id_params', $request->get_params(), $request);

    $quiz_id = $params['quiz_id'] ?? $request['quiz_id'] ?? $request->get_param('quiz_id');

    // Validate required fields
    if (empty($quiz_id)) {
      return new WP_Error(
        'missing_id',
        __('Quiz id is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    // 2) Payload/Data Filter: normalize values before core execution
    $payload = apply_filters('acadlix_get_front_quiz_leaderboard_by_id_payload', [
      'quiz_id' => intval($quiz_id),
    ], $params);

    $appends = apply_filters('acadlix_get_front_quiz_leaderboard_by_id_quiz_appends', [
      'rendered_post_content',
      'rendered_metas',
      'category',
      'languages',
    ], $payload['quiz_id'], $params);

    $quiz = acadlix()->model()->quiz()->ofQuiz()->find($payload['quiz_id'])->setAppends($appends);

    $res = ['quiz' => $quiz];

    $leaderboard = $quiz->rendered_metas['quiz_settings']['leaderboard'] ?? false;
    if ($leaderboard) {
      $leaderboard_total_number_of_entries = $quiz->rendered_metas['quiz_settings']['leaderboard_total_number_of_entries'] ?? 10;
      $toplist = acadlix()->model()->toplist();
      $limit = ($leaderboard_total_number_of_entries > 0 && $leaderboard_total_number_of_entries < 10) ? $leaderboard_total_number_of_entries : 10;

      $toplist_data = apply_filters('acadlix_get_front_quiz_leaderboard_by_id_toplist_data', $toplist->getTopList($payload['quiz_id'], 0, $limit), $payload, $params, $quiz);

      $res['toplist'] = $toplist_data;
      $res['toplist_count'] = $toplist->where('quiz_id', $payload['quiz_id'])->count();
    }

    // 3) Lifecycle Action: expose retrieval to listeners
    do_action('acadlix_get_front_quiz_leaderboard_by_id', $res, $quiz, $payload, $params);

    // 4) Outbound Response Filter: allow response augmentation
    $res = apply_filters('acadlix_get_front_quiz_leaderboard_by_id_response', $res, $quiz, $payload, $params);

    return rest_ensure_response($res);
  }

  public function post_load_more_toplist_by_id($request)
  {
    // 1) Pre-parameter filter: allow callers to mutate incoming params before validation
    $params = apply_filters('acadlix_pre_post_load_more_toplist_by_id_params', $request->get_json_params(), $request);

    $quiz_id = $params['quiz_id'] ?? $request['quiz_id'] ?? $request->get_param('quiz_id');

    if (empty($quiz_id)) {
      return new WP_Error(
        'missing_id',
        __('Quiz id is required.', 'acadlix'),
        ['status' => 400]
      );
    }

    // 2) Payload/Data Filter: normalize values before core execution
    $payload = apply_filters('acadlix_post_load_more_toplist_by_id_payload', [
      'quiz_id' => intval($quiz_id),
      'leaderboard_total_number_of_entries' => intval($params['leaderboard_total_number_of_entries'] ?? 0),
      'toplist_view_count' => intval($params['toplist_view_count'] ?? 0),
    ], $params);

    $toplist = acadlix()->model()->toplist();
    $remaining = $payload['leaderboard_total_number_of_entries'] - $payload['toplist_view_count'];
    $limit = ($remaining < 10) ? $remaining : 10;

    $toplist_data = apply_filters('acadlix_post_load_more_toplist_by_id_toplist_data', $toplist->getTopList($payload['quiz_id'], $payload['toplist_view_count'], $limit), $payload, $params);

    $res = [
      'toplist' => $toplist_data,
      'toplist_count' => $toplist->where('quiz_id', $payload['quiz_id'])->count(),
    ];

    // 3) Lifecycle Action: allow listeners to react to load more
    do_action('acadlix_post_load_more_toplist_by_id', $res, $payload, $params);

    // 4) Outbound Response Filter: allow other modules to modify the response
    $res = apply_filters('acadlix_post_load_more_toplist_by_id_response', $res, $payload, $params);

    return rest_ensure_response($res);
  }

  public function check_permission($request)
  {
    return wp_verify_nonce(
      $request->get_header('X-WP-Nonce'),
      'wp_rest'
    );
  }
}