<?php

namespace Drupal\miniorange_2fa\Plugin;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\OpenDialogCommand;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Url;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\miniorange_2fa\MiniorangeUser;
use Drupal\miniorange_2fa\UsersAPIHandler;
use Drupal\miniorange_2fa\MoAuthUtilities;
use Drupal\user\Entity\User;
use Drupal\Core\Ajax\MessageCommand;
use Drupal\miniorange_2fa\AuthenticationType;
/**
 * Base class for TOTP-based authentication types.
 * 
 * This class provides common functionality for all TOTP authenticator apps
 */
abstract class TotpAuthenticationTypePluginBase extends AuthenticationTypePluginBase {

  use StringTranslationTrait;
  use DependencySerializationTrait;
  private const FORM_CONTAINER_ID = 'modal_support_form';
  private const SECRET_CHUNK_SIZE = 4;

  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {

    $input = $form_state->getUserInput();
    $auth_method_from_url = \Drupal::request()->query->get('authMethod', '') ?: \Drupal::routeMatch()->getParameter('authMethod') ?: '';
    $totp_method = !empty($auth_method_from_url) ? (AuthenticationType::getAuthType($auth_method_from_url) ?: $this->toArray()) : $this->toArray();
    
    $this->setFormTitle($form, self::FORM_CONTAINER_ID, $auth_method_from_url);

    if (array_key_exists('secret', $input) === false) {
      $qr_secret_data = $this->generateQrCodeAndSecret();
      if (!$qr_secret_data) {
        return $form;
      }
      $qr_code = $qr_secret_data['qrCode'];
      $secret = $qr_secret_data['secret'];
    } else {
      $secret = $input['secret'];
      $qr_code = $input['qrCode'];
    }

    $form['mo_scan_qr_code_google_authenticator'] = $this->buildTotpForm($totp_method, $qr_code, $secret, $auth_method_from_url);
    
    $this->addFormSuffix($form);
    return $form;
  }

  /**
   * Generate QR code and secret for TOTP setup.
   *
   * @return array|null
   *   Array with 'qrCode' and 'secret' keys, or null on error.
   */
  public function generateQrCodeAndSecret(): ?array {
    if (!$this->validateUserSession()) {
      return null;
    }

    $user = $this->getCurrentUser();
    
    if (!$this->validateUserEmail($user->id())) {
      return null;
    }

    $auth_api_handler = $this->createAuthApiHandler();
    $miniorange_user = $this->createMiniorangeUser($this->getCode());
    $response = $auth_api_handler->getGoogleAuthSecret($miniorange_user);
    
    if (!$this->validateApiResponse($response, 'TOTP secret generation')) {
      $utilities = new MoAuthUtilities();
      $utilities->redirectUserToSetupTwoFactor('An error occurred while processing your request. Please try again after sometime.');
      return null;
    }

    return [
      'qrCode' => $response->qrCodeData ?? '',
      'secret' => $response->secret ?? ''
    ];
  }

  /**
   * Build the TOTP configuration form.
   *
   * @param array $totp_method
   *   The TOTP method configuration.
   * @param string $qr_code
   *   The QR code data.
   * @param string $secret
   *   The secret key.
   *
   * @return array
   *   The form array.
   */
  protected function buildTotpForm(array $totp_method, string $qr_code, string $secret, string $actual_auth_method = ''): array {
    $form = [];

    if ($this->shouldShowDownloadStep()) {
      $form['step_one'] = [
        '#markup' => $this->buildDownloadStepMarkup($totp_method),
      ];
      $qr_step_number = 2;
    } else {
      $qr_step_number = 1;
    }

    $form['step_two'] = [
      '#markup' => '<h5>' . $this->t('Step @step: Scan this QR code with the app', ['@step' => $qr_step_number]) . '</h5>'
    ];

    $form['actions_qrcode'] = [
      '#markup' => new FormattableMarkup('<img src="data:image/jpg;base64, @qr_code"/>', ['@qr_code' => $qr_code]),
      '#prefix' => '<div class="form-item--editor-format">',
      '#suffix' => '&nbsp;&nbsp;',
    ];

    $formatted_secret = $this->formatSecret($secret);
    $form['secret'] = [
      '#type' => 'hidden',
      '#value' => $formatted_secret
    ];

    $form['qrCode'] = [
      '#type' => 'hidden',
      '#value' => $qr_code
    ];

    $form['actions_secret_key'] = [
      '#markup' => $this->buildManualEntryMarkup($totp_method, $formatted_secret),
    ];

    $final_step = $this->shouldShowDownloadStep() ? 3 : 2;
    $form['actions_3'] = [
      '#markup' => '<h5>' . $this->t('Step @step: Enter the passcode generated by the app', ['@step' => $final_step]) . '</h5>'
    ];

    $form['mo_auth_google_auth_token'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Passcode:'),
      '#maxlength' => 8,
      '#id' => 'passcode',
      '#attributes' => [
        'placeholder' => $this->t('Enter passcode'),
        'class' => ['mo2f-textbox'],
      ],
      '#required' => TRUE,
      '#prefix' => '<div class="container-inline">',
      '#suffix' => '&nbsp;&nbsp;',
    ];

    $form['action'] = ['#type' => 'actions'];
    $form['validate_code'] = [
      '#type' => 'submit',
      '#button_type' => 'primary',
      '#value' => $this->t('Verify'),
      '#suffix' => '</div>',
      '#attributes' => ['class' => ['use-ajax']],
      '#ajax' => [
        'callback' => [$this, 'submitTotpForm'],
        'wrapper' => self::FORM_CONTAINER_ID,
        'progress' => [
          'type' => 'throbber',
        ],
      ],
    ];

    $form['methodToConfigure'] = [
      '#type' => 'hidden',
      '#value' => $this->getCode()
    ];

    $form['actual_auth_method'] = [
      '#type' => 'hidden',
      '#value' => !empty($actual_auth_method) ? $actual_auth_method : $this->getCode()
    ];

    return $form;
  }

  /**
   * Build the download step markup.
   *
   * @param array $totp_method
   *   The TOTP method configuration.
   *
   * @return string
   *   The markup for the download step.
   */
  protected function buildDownloadStepMarkup(array $totp_method): string {
    $ios_link = $totp_method['ios_link'] ?? '';
    $android_link = $totp_method['android_link'] ?? '';
    $method_name = $totp_method['name'] ?? 'Authenticator';

    return '
      <h5>' . $this->t('Step 1: Download the @method_name app', ['@method_name' => $method_name]) . '</h5>
      <ul>
        <li class="two-step-verification--app-item--ios">iPhone, iPod Touch, or iPad: <a target="_blank" href="' . $ios_link . '">' . $this->t('Download') . '</a></li>
        <li class="two-step-verification--app-item--android">Android devices: <a target="_blank" href="' . $android_link . '">' . $this->t('Download') . '</a></li>
      </ul><hr>';
  }

  /**
   * Build the manual entry markup.
   *
   * @param array $totp_method
   *   The TOTP method configuration.
   * @param string $formatted_secret
   *   The formatted secret key.
   *
   * @return string
   *   The markup for manual entry instructions.
   */
  protected function buildManualEntryMarkup(array $totp_method, string $formatted_secret): string {
    $method_name = $totp_method['name'] ?? 'Authenticator';
    $markup = '<div class="qr_code_text">
      <p><b>@cant_scan</b> @add_code</p>
      <p id="googleAuthSecret"><b>Key: </b> <code class="mo_2fa_highlight_background_note">@secret</code> (Spaces don\'t matter)</p>
      <p><b>@time_based</b> @yes</p>
    </div></div><hr>';

    return $this->t($markup, [
      '@cant_scan' => $this->t("Can't scan the code?"),
      '@add_code' => $this->t('Add the code to @method_name app manually:', ['@method_name' => $method_name]),
      '@secret' => $formatted_secret,
      '@time_based' => $this->t('Time based:'),
      '@yes' => $this->t('Yes'),
    ]);
  }

  /**
   * Format secret key with spaces for readability.
   *
   * @param string $secret
   *   The raw secret key.
   *
   * @return string
   *   The formatted secret key with spaces.
   */
   protected function formatSecret(string $secret): string {
    return trim(chunk_split($secret, self::SECRET_CHUNK_SIZE, ' '));
  }

  /**
   * AJAX callback for TOTP form submission.
   *
   * @param array $form
   *   The form array.
   * @param FormStateInterface $form_state
   *   The form state.
   *
   * @return AjaxResponse
   *   The AJAX response.
   */
  public function submitTotpForm(array &$form, FormStateInterface $form_state): AjaxResponse {
    
    $ajax_response = new AjaxResponse();
   
    if ($form_state->hasAnyErrors()) {
      $ajax_response->addCommand(new ReplaceCommand('#' . self::FORM_CONTAINER_ID, $form));
      return $ajax_response;
    }

    if (!$this->validateUserSession()) {
      return $this->createRedirectResponse('miniorange_2fa.setup_twofactor');
    }

    $input = $form_state->getUserInput();
    $user = $this->getCurrentUser();
    $utilities = new MoAuthUtilities();
    $custom_attribute = $utilities::get_users_custom_attribute($user->id());
    $auth_api_handler = $this->createAuthApiHandler();
    $auth_method = !empty($input['actual_auth_method']) ? $input['actual_auth_method'] : $this->getCode();
    
    $customer = $this->createCustomerProfile();
    $user_api_handler = new UsersAPIHandler($customer->getCustomerID(), $customer->getAPIKey());
    $miniorange_user = $this->createMiniorangeUser($auth_method);
    
    $secret = preg_replace('/\s+/', '', $input['secret']);
    $otp_token = $this->getFormValue($form_state, 'mo_auth_google_auth_token');
   
    $response = $auth_api_handler->register($miniorange_user, AuthenticationType::$GOOGLE_AUTHENTICATOR['code'], $secret, $otp_token, null);
    
    \Drupal::messenger()->deleteAll();

    if (is_object($response) && $response->status == 'SUCCESS') {
      return $this->handleSuccessfulTotpRegistration($ajax_response, $auth_method, $user_api_handler, $miniorange_user, $input);
    } elseif (is_object($response) && $response->status == 'FAILED') {
      $message = $this->t('The passcode you have entered is incorrect. Please try again.');
      $has_registered_email = !empty($custom_attribute) && !empty($custom_attribute[0]->miniorange_registered_email);
      if ($has_registered_email) {
        $ajax_response->addCommand(new OpenDialogCommand('#error', 'Error', $message, ['minWidth' => 500], []));
      }
      else{
        $ajax_response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
      }
      
      return $ajax_response;
    }

    $this->logError('TOTP registration failed: ' . print_r($response, TRUE), 'debug');
    $this->addErrorMessage('An error occurred while processing your request. Please try again.');
    $ajax_response->addCommand(new RedirectCommand(Url::fromRoute('miniorange_2fa.setup_twofactor')->toString()));
    return $ajax_response;
  }
  
  /**
   * Handle successful TOTP registration.
   *
   * @param AjaxResponse $ajax_response
   *   The AJAX response (unused now).
   * @param array $configured_methods
   *   Array of configured methods (unused now).
   * @param string $auth_method
   *   The authentication method.
   * @param UsersAPIHandler $user_api_handler
   *   The user API handler.
   * @param MiniorangeUser $miniorange_user
   *   The MiniorangeUser instance.
   * @param object $user
   *   The user object (unused now).
   * @param array $input
   *   The form input.
   *
   * @return AjaxResponse
   *   The AJAX response.
   */
  protected function handleSuccessfulTotpRegistration( AjaxResponse $ajax_response,
    string $auth_method,
    UsersAPIHandler $user_api_handler,
    MiniorangeUser $miniorange_user,
    array $input
  ): AjaxResponse {
    
    $additional_fields = [];
    if (isset($input['qrCode'])) {
      $additional_fields['qr_code_string'] = $input['qrCode'];
    }
    $result = $this->updateUserAuthenticationMethod($auth_method, $miniorange_user, $user_api_handler, $additional_fields, TRUE);
   
    return $this->handlePostAuthenticationRedirection(
      $result['success'],
      $result['message'],
      $result['message']
    );
  }
  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void {
   
    $passcode = $this->getFormValue($form_state, 'mo_auth_google_auth_token');
    if (empty($passcode)) {
      $form_state->setErrorByName('mo_auth_google_auth_token', $this->t('Please enter the passcode.'));
    } elseif (!$this->validatePasscode($passcode)) {
      $form_state->setErrorByName('mo_auth_google_auth_token', $this->t('Please enter a valid 6-digit passcode.'));
    }
  }

 /**
 * {@inheritdoc}
 */
public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void {
 
}
  /**
   * Determine whether to show the download step.
   *
   * @return bool
   *   TRUE to show the download step, FALSE to skip it.
   */
  protected function shouldShowDownloadStep(): bool {
    return TRUE;
  }
 
} 