<?php

namespace Components\Ui;

use Components\Base;

/**
 * ScaleControl UI component.
 *
 * Renders a zoom button group (plus, percentage, minus) and provides
 * the corresponding Alpine.js logic.
 */
class ScaleControl
{
    /**
     * Render the scale control button group.
     *
     * @return string
     */
    public static function render(): string
    {
        ob_start();
        ?>
        <div class="flex items-center rounded-full bg-white inset-ring-1 inset-ring-divider p-0.5 dark:bg-gray-800 dark:ring-white/10 shrink-0">
            <button type="button"
                    @click="decScale()"
                    class="group flex items-center rounded-full p-1.5 pb-1 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
                    aria-label="Decrease scale">
                <span class="size-5 text-gray-500 group-hover:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-200">
                    <?= str_replace('size-6', 'size-5', Base::SVG_MAGNIFYING_MINUS) ?>
                </span>
            </button>
            <div class="border-l border-gray-200 h-4 mx-0.5 dark:border-white/10"></div>
            <span class="px-1 text-[11px] font-bold text-gray-500 tabular-nums min-w-[2.5rem] text-center cursor-pointer hover:text-gray-900 select-none"
                  @click="scale = 1.0; userScaled = true"
                  x-text="scalePercent"></span>
            <div class="border-l border-gray-200 h-4 mx-0.5 dark:border-white/10"></div>
            <button type="button"
                    @click="incScale()"
                    class="group flex items-center rounded-full p-1.5 pb-1 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
                    aria-label="Increase scale">
                <span class="size-5 text-gray-500 group-hover:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-200">
                    <?= str_replace('size-6', 'size-5', Base::SVG_MAGNIFYING_PLUS) ?>
                </span>
            </button>
        </div>
        <?php
        return ob_get_clean();
    }

    /**
     * Returns the Alpine.js data properties and methods for scaling.
     * Can be spread into an Alpine x-data object.
     *
     * @param float $initialScale
     * @return string
     */
    public static function jsData(float $initialScale = 1.0): string
    {
        // We use a Heredoc to provide the JS logic. 
        // Note: The consuming Alpine.js component must handle the actual 
        // visual scaling (e.g. via a :style binding using the `scale` property).
        return "
            scale: $initialScale,
            userScaled: false,
            scaleOptions: [
                {value: 0.5, label: '50%'},
                {value: 0.7, label: '70%'},
                {value: 0.9, label: '90%'},
                {value: 1, label: '100%'},
                {value: 1.1, label: '110%'},
                {value: 1.5, label: '150%'},
            ],
            get scalePercent() {
                const s = Number(this.scale);
                if (!Number.isFinite(s) || s <= 0) return '100%';
                return Math.round(s * 100) + '%';
            },
            updateScale() {
                const s = Number(this.scale);
                if (!Number.isFinite(s) || s <= 0) this.scale = 1;
            },
            incScale() {
                this.userScaled = true;
                try {
                    const opts = this.scaleOptions.map(o => o.value).sort((a, b) => a - b);
                    const curr = Number(this.scale);
                    let next = opts[opts.length - 1];
                    for (let i = 0; i < opts.length; i++) {
                        if (opts[i] > curr + 1e-6) {
                            next = opts[i];
                            break;
                        }
                    }
                    this.scale = next;
                    this.updateScale();
                } catch (e) {
                    this.scale = 1;
                }
            },
            decScale() {
                this.userScaled = true;
                try {
                    const opts = this.scaleOptions.map(o => o.value).sort((a, b) => a - b);
                    const curr = Number(this.scale);
                    let prev = opts[0];
                    for (let i = opts.length - 1; i >= 0; i--) {
                        if (opts[i] < curr - 1e-6) {
                            prev = opts[i];
                            break;
                        }
                    }
                    this.scale = prev;
                    this.updateScale();
                } catch (e) {
                    this.scale = 1;
                }
            }
        ";
    }
}
