<?php

defined("ABSPATH") || exit();

class Chit_Redeem
{
    // Guest redemptions are unauthenticated, so capping failed lookups by IP
    private const THROTTLE_LIMIT = 10;
    private const THROTTLE_WINDOW = 10 * MINUTE_IN_SECONDS;

    private const SUCCESS_TTL = 15 * MINUTE_IN_SECONDS;

    private bool $guest = false;

    public function __construct()
    {
        add_shortcode("chit_redeem", [$this, "render_shortcode"]);
        add_action("admin_post_chit_redeem", [$this, "handle_post"]);
        add_action("admin_post_nopriv_chit_redeem", [$this, "handle_post"]);
    }

    public static function guests_allowed(): bool
    {
        return (bool) apply_filters(
            "chit_allow_guest_redemption",
            (bool) get_option("chit_allow_guest_redemption")
        );
    }

    public function render_shortcode($atts = []): string
    {
        ob_start();
        $this->render();
        return ob_get_clean();
    }

    private function render(): void
    {
        $logged_in = is_user_logged_in();
        $guest = !$logged_in && self::guests_allowed();
        ?>
        <div class="chit-redeem">
            <?php if (isset($_GET["chit_redeemed"])): ?>
                <?php $this->render_success(
                    $this->redeemed_order((string) wp_unslash($_GET["chit_redeemed"]))
                ); ?>
            <?php elseif (isset($_GET["chit_error"])): ?>
                <div class="chit-redeem-error">
                    <p><?php echo esc_html(
                        $this->error_message(
                            sanitize_key($_GET["chit_error"])
                        )
                    ); ?></p>
                </div>
            <?php endif; ?>

            <?php if (!$logged_in && !$guest): ?>
                <p>
                    <?php
                    $login_url = wp_login_url(get_permalink());
                    printf(
                        wp_kses(
                            /* translators: %s = login URL */
                            __(
                                'Please <a href="%s">log in</a> to redeem a code.',
                                "chit-download-codes-for-woocommerce"
                            ),
                            ["a" => ["href" => []]]
                        ),
                        esc_url($login_url)
                    );
                    ?>
                </p>
            <?php else: ?>
                <form method="post" action="<?php echo esc_url(
                    admin_url("admin-post.php")
                ); ?>">
                    <input type="hidden" name="action" value="chit_redeem">
                    <?php wp_nonce_field("chit_redeem"); ?>
                    <?php if ($guest): ?>
                        <p>
                            <label for="chit_email"><?php esc_html_e(
                                "Email address",
                                "chit-download-codes-for-woocommerce"
                            ); ?></label><br>
                            <input
                                type="email"
                                id="chit_email"
                                name="email"
                                required
                                autocomplete="email"
                            />
                            <br><small><?php esc_html_e(
                                "Your download links will be sent here.",
                                "chit-download-codes-for-woocommerce"
                            ); ?></small>
                        </p>
                    <?php endif; ?>
                    <p>
                        <label for="chit_code"><?php esc_html_e(
                            "Code",
                            "chit-download-codes-for-woocommerce"
                        ); ?></label><br>
                        <input
                            type="text"
                            id="chit_code"
                            name="code"
                            required
                            placeholder="<?php echo esc_attr(
                                Chit_Codes::placeholder()
                            ); ?>"
                            autocomplete="off"
                            style="text-transform: uppercase;"
                        />
                    </p>
                    <p>
                        <button type="submit" class="button button-primary">
                            <?php esc_html_e("Redeem", "chit-download-codes-for-woocommerce"); ?>
                        </button>
                    </p>
                    <?php if ($guest): ?>
                        <p><small>
                            <?php
                            printf(
                                wp_kses(
                                    /* translators: %s = login URL */
                                    __(
                                        'Have an account? <a href="%s">Log in</a> to keep your downloads under My Account.',
                                        "chit-download-codes-for-woocommerce"
                                    ),
                                    ["a" => ["href" => []]]
                                ),
                                esc_url(wp_login_url(get_permalink()))
                            );
                            ?>
                        </small></p>
                    <?php endif; ?>
                </form>
            <?php endif; ?>
        </div>
        <?php
    }

    private function render_success(?WC_Order $order): void
    {
        $downloads = $order ? $order->get_downloadable_items() : [];
        ?>
        <div class="chit-redeem-success">
            <p><?php esc_html_e(
                "Code redeemed successfully.",
                "chit-download-codes-for-woocommerce"
            ); ?></p>
            <?php if ($downloads): ?>
                <ul class="chit-redeem-downloads">
                    <?php foreach ($downloads as $download): ?>
                        <li>
                            <a href="<?php echo esc_url(
                                $download["download_url"]
                            ); ?>">
                                <?php echo esc_html(
                                    $download["download_name"] ?: $download["product_name"]
                                ); ?>
                            </a>
                        </li>
                    <?php endforeach; ?>
                </ul>
                <?php if ($order->get_billing_email()): ?>
                    <p><?php printf(
                        /* translators: %s = email address */
                        esc_html__(
                            "We've also emailed these links to %s.",
                            "chit-download-codes-for-woocommerce"
                        ),
                        esc_html($order->get_billing_email())
                    ); ?></p>
                <?php endif; ?>
            <?php endif; ?>
            <?php if (!$order || $order->get_customer_id()): ?>
                <p>
                    <a class="button" href="<?php echo esc_url(
                        wc_get_account_endpoint_url("downloads")
                    ); ?>">
                        <?php esc_html_e("View your downloads", "chit-download-codes-for-woocommerce"); ?>
                    </a>
                </p>
            <?php endif; ?>
        </div>
        <?php
    }

    private function redeemed_order(string $token): ?WC_Order
    {
        if (!preg_match('/^[A-Za-z0-9]{32}$/', $token)) {
            return null;
        }
        $order_id = (int) get_transient("chit_redeemed_" . $token);
        if (!$order_id) {
            return null;
        }
        $order = wc_get_order($order_id);
        return $order instanceof WC_Order ? $order : null;
    }

    private function error_message(string $code): string
    {
        $messages = [
            "missing" => __("Please enter a code.", "chit-download-codes-for-woocommerce"),
            "email" => __("Please enter a valid email address.", "chit-download-codes-for-woocommerce"),
            "invalid" => __("That code was not recognised.", "chit-download-codes-for-woocommerce"),
            "spent" => __("That code has already been used.", "chit-download-codes-for-woocommerce"),
            "revoked" => __("That code has been revoked.", "chit-download-codes-for-woocommerce"),
            "throttled" => __(
                "Too many attempts. Please try again in a few minutes.",
                "chit-download-codes-for-woocommerce"
            ),
            "product" => __(
                "The product linked to this code is no longer available.",
                "chit-download-codes-for-woocommerce"
            ),
            "downloads" => __(
                "This product has no downloads available.",
                "chit-download-codes-for-woocommerce"
            ),
            "failed" => __("Something went wrong. Please try again.", "chit-download-codes-for-woocommerce"),
        ];
        return $messages[$code] ?? $messages["failed"];
    }

    public function handle_post(): void
    {
        check_admin_referer("chit_redeem");

        $logged_in = is_user_logged_in();
        if (!$logged_in && !self::guests_allowed()) {
            wp_safe_redirect(wp_login_url($this->get_redeem_url()));
            exit();
        }
        $this->guest = !$logged_in;

        if ($this->guest) {
            if ($this->is_throttled()) {
                $this->redirect_with_error("throttled");
            }
            $user_id = null;
            $email = sanitize_email(wp_unslash($_POST["email"] ?? ""));
            if (!is_email($email)) {
                $this->redirect_with_error("email");
            }
        } else {
            $user_id = get_current_user_id();
            $email = (string) wp_get_current_user()->user_email;
        }

        $raw = sanitize_text_field(wp_unslash($_POST["code"] ?? ""));
        $code = $this->normalize_code((string) $raw);
        if ($code === "") {
            $this->redirect_with_error("missing");
        }

        $row = Chit_Codes::find_by_code($code);
        if (!$row) {
            $this->fail_lookup("invalid");
        }

        if (
            $row["status"] === "activated" ||
            $row["status"] === "expired"
        ) {
            $this->fail_lookup("spent");
        }
        if ($row["status"] === "revoked") {
            $this->fail_lookup("revoked");
        }
        if ($row["status"] !== "unused") {
            $this->redirect_with_error("failed");
        }

        $product = wc_get_product((int) $row["product_id"]);
        if (!$product) {
            $this->redirect_with_error("product");
        }
        if (
            !$product->is_downloadable() ||
            empty($product->get_downloads())
        ) {
            $this->redirect_with_error("downloads");
        }

        $claimed = Chit_Codes::activate((int) $row["id"], $user_id);
        if (!$claimed) {
            // Race condition: someone else claimed it after find_by_code.
            $this->fail_lookup("spent");
        }

        try {
            $order_id = $this->create_redemption_order(
                $product,
                (int) $user_id,
                $email
            );
            Chit_Codes::set_order_id((int) $row["id"], $order_id);
        } catch (Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                wp_trigger_error(__METHOD__, 'chit redeem failed: ' . $e->getMessage());
            }
            Chit_Codes::revert_activation((int) $row["id"]);
            $this->redirect_with_error("failed");
        }

        // The success screen renders after a redirect, so hand it the order
        // via a short-lived token rather than exposing the order id.
        $token = wp_generate_password(32, false);
        set_transient("chit_redeemed_" . $token, $order_id, self::SUCCESS_TTL);

        wp_safe_redirect(
            add_query_arg(["chit_redeemed" => $token], $this->get_redeem_url())
        );
        exit();
    }

    private function create_redemption_order(
        WC_Product $product,
        int $user_id,
        string $email
    ): int {
        $user = $user_id ? get_userdata($user_id) : null;

        // customer_id 0 is a guest order
        $order = wc_create_order(["customer_id" => $user_id]);
        $zero_total = apply_filters(
            "chit_redemption_zero_total",
            !get_option("chit_redemption_catalog_price")
        );
        if ($zero_total) {
            $order->add_product($product, 1, ["subtotal" => 0, "total" => 0]);
        } else {
            $order->add_product($product, 1);
        }

        $order->set_billing_email($email);
        if ($user) {
            if (!empty($user->first_name)) {
                $order->set_billing_first_name($user->first_name);
            }
            if (!empty($user->last_name)) {
                $order->set_billing_last_name($user->last_name);
            }
        }

        $order->calculate_totals();
        $order->update_status(
            "completed",
            __("Chit code redeemed.", "chit-download-codes-for-woocommerce")
        );

        return (int) $order->get_id();
    }

    private function normalize_code(string $raw): string
    {
        return strtoupper(trim($raw));
    }

    private function fail_lookup(string $code): void
    {
        if ($this->guest) {
            $key = $this->throttle_key();
            $count = (int) get_transient($key);
            set_transient($key, $count + 1, self::THROTTLE_WINDOW);
        }
        $this->redirect_with_error($code);
    }

    private function is_throttled(): bool
    {
        return (int) get_transient($this->throttle_key()) >= self::THROTTLE_LIMIT;
    }

    private function throttle_key(): string
    {
        $ip = isset($_SERVER["REMOTE_ADDR"])
            ? sanitize_text_field(wp_unslash($_SERVER["REMOTE_ADDR"]))
            : "";
        return "chit_throttle_" . md5($ip);
    }

    private function redirect_with_error(string $code): void
    {
        wp_safe_redirect(
            add_query_arg(["chit_error" => $code], $this->get_redeem_url())
        );
        exit();
    }

    private function get_redeem_url(): string
    {
        $referer = wp_get_referer();
        if ($referer) {
            return $referer;
        }
        $page_id = (int) get_option("chit_redeem_page_id", 0);
        if ($page_id) {
            $url = get_permalink($page_id);
            if ($url) {
                return $url;
            }
        }
        return home_url("/");
    }
}
