<?php

/**
 * Copyright 2024-2025 Simply.IN Sp. z o.o.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the EUPL-1.2 or later.
 * You may not use this work except in compliance with the Licence.
 *
 * Copy of the Licence is available at:
 * https://joinup.ec.europa.eu/software/page/eupl
 * It is bundled with this package in the file LICENSE.txt
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Licence is distributed on an as is basis,
 * without warranties or conditions of any kind, either express or implied.
 * Check the Licence for the specific language governing permissions
 * and limitations under the License.
 *
 * @author   Simply.IN Sp. z o.o.
 * @copyright 2024-2025 Simply.IN Sp. z o.o.
 * @license   https://joinup.ec.europa.eu/software/page/eupl
 */

declare(strict_types=1);

namespace SimplyIn\Ajax\Handler;

use Context;
use SimplyIn\Ajax\AjaxActionInterface;
use SimplyIn\Ajax\AjaxRequest;
use SimplyIn\Ajax\Service\CheckoutSessionService;
use SimplyIn\Ajax\Service\DeliveryFormRenderer;
use SimplyIn\Api\SimplyInApiClient;

if (!defined('_PS_VERSION_')) {
    exit;
}

/**
 * Shop-login email-OTP challenge — step 1. Asks the backend to e-mail a code to the shopper and
 * returns the 6-digit challenge popup. Two modes:
 *
 * EMAIL MODE (email-first, resend): the challenge email is read from the session key pinned by
 * SubmitEmailHandler at challenge start, never from the request, so this action cannot be pointed
 * at an arbitrary inbox.
 *
 * TOKEN MODE (post-SMS pivot): no pinned email, but a checkout authToken saved after
 * submitCheckoutCode; the backend targets the authenticated user's own email — no email input at all.
 */
final class SendShopLoginEmailOtpHandler implements AjaxActionInterface
{
    public function __construct(
        private readonly SimplyInApiClient $api,
        private readonly Context $context,
        private readonly CheckoutSessionService $checkoutSession,
        private readonly DeliveryFormRenderer $renderer,
    ) {
    }

    public function name(): string
    {
        return 'sendShopLoginEmailOtp';
    }

    /**
     * @return array<string, mixed>
     */
    public function execute(AjaxRequest $request): array
    {
        // This action serves both the initial email-OTP send (triggered right after the checkout
        // code verify reports challenge_available) and the popup's resend button — the same
        // 'isResend' contract as checkout/submitEmail, so the backend can pick "sent" vs "resent".
        $isResend = $request->getBool('isResend');

        // EMAIL MODE wins over a stray checkout authToken: a pinned challenge email means the code
        // was e-mailed to that address, so the resend must ride the by-email path. The pinned
        // session email is the only accepted source; a request-supplied email is ignored. The
        // 'language' param mirrors the initial send so the resent OTP e-mail is localized.
        $challengeEmail = $this->checkoutSession->getShopLoginChallengeEmail();
        if (is_string($challengeEmail) && $challengeEmail !== '') {
            $body = ['email' => $challengeEmail, 'language' => $this->languageCode()];
            if ($isResend) {
                $body['isResend'] = true;
            }

            return $this->mapSendResponse(
                $this->api->apiCall($body, 'POST', 'checkout/sendShopLoginEmailOtpCommerce'),
            );
        }

        // TOKEN MODE (post-SMS pivot): a token-mode challenge is starting — clear any stale
        // email-first pin so it can't later hijack the verify step into the wrong mode.
        $authToken = $this->checkoutSession->getAuthToken();
        if (is_string($authToken) && $authToken !== '') {
            $this->checkoutSession->clearShopLoginChallengeEmail();

            $body = ['language' => $this->languageCode()];
            if ($isResend) {
                $body['isResend'] = true;
            }

            return $this->mapSendResponse(
                $this->api->apiCall($body, 'POST', 'checkout/sendShopLoginEmailOtpCommerce', $authToken),
            );
        }

        // No pending challenge → same UNAUTHORIZED shape as before (this action is never a challenge
        // entry point on its own).
        return ['success' => false, 'error' => 'UNAUTHORIZED'];
    }

    private function languageCode(): string
    {
        return strtoupper((string) $this->context->language->iso_code);
    }

    /**
     * @param null|array<string, mixed>|false $raw
     *
     * @return array<string, mixed>
     */
    private function mapSendResponse(null|array|false $raw): array
    {
        $response = is_array($raw) ? $raw : [];

        if (empty($response['ok'])) {
            return ['success' => false, 'error' => (string) ($response['code'] ?? 'ERROR')];
        }

        return [
            'success' => true,
            'show_popup' => true,
            'html' => $this->renderer->renderShopLoginChallenge($response),
        ];
    }
}
