<?php
// components/CampaignFlow/DateRangeBadge.php
namespace Components\CampaignFlow;

use Components\Support\Palette;

class DateRangeBadge extends FlowBadge
{
    /**
     * Render a date-range badge, visually aligned with WaitBadge/ConditionBadge.
     * $cfg keys:
     * - from (int|string|null) unix timestamp or strtotime()-parseable, optional
     * - to (int|string|null) unix timestamp or strtotime()-parseable, optional
     * - label (string) optional explicit label; if present, overrides auto range label
     * - icon (string) optional raw HTML; defaults to Base::SVG_CALENDAR
     * - index (int) optional key for editing
     * - disabled (bool)
     * - href (string) optional link
     * - attrs (array) optional attributes
     * - dropdown (array) optional dropdown config (same shape as WaitBadge)
     * - variant (string) optional layout variant, supports 'pill'
     * - color (string) optional palette token override for 'pill' (defaults to schedule state token)
     */
    public static function render(array $cfg, array $opts = []): string
    {
        $size = $opts['size'] ?? 'md'; // xs|sm|md|lg
        $orientation = $opts['orientation'] ?? 'horizontal';
        $sz = self::getSizes()[$size] ?? self::getSizes()['md'];

        $icon = (string)($cfg['icon'] ?? self::SVG_OUTLINE_CALENDAR);

        // Build labels: upper shows start date, lower shows end date
        // Also build a combined title for accessibility/tooltips
        $explicitLabel = isset($cfg['label']) ? (string)$cfg['label'] : '';
        [$labelUpper, $labelLower, $labelTitle] = self::buildRangeLabels($cfg['from'] ?? null, $cfg['to'] ?? null, $explicitLabel !== '' ? $explicitLabel : null);
        $labelUpperEsc = self::h($labelUpper, false);
        $labelLowerEsc = self::h($labelLower, false);
        $labelTitleEsc = self::h($labelTitle, false);

        // Determine state from the configured date range ONLY (ignore status/completed flags).
        // This ensures the dateRange visual reflects just the range semantics.
        $fromVal = $cfg['from'] ?? null;
        $toVal = $cfg['to'] ?? null;

        $parseTs = function ($v, bool $isEnd = false): ?int {
            if ($v === null || $v === '') { return null; }
            if (is_int($v)) { return $v; }
            if (is_string($v)) {
                $s = trim($v);
                if ($s === '0000-00-00 00:00:00' || $s === '0000-00-00') { return null; }
                if (ctype_digit($s)) { return (int)$s; }
                $ts = strtotime($s);
                if ($ts === false) { return null; }
                // If end boundary provided as date-only, use end-of-day for inclusive range
                if ($isEnd && preg_match('/^\d{4}-\d{2}-\d{2}$/', $s)) {
                    $dt = new \DateTime($s . ' 23:59:59', new \DateTimeZone(date_default_timezone_get()));
                    return (int)$dt->format('U');
                }
                return (int)$ts;
            }
            return null;
        };

        $now = time();
        $startTs = $parseTs($fromVal, false);
        $endTs = $parseTs($toVal, true);

        // Range-only state: scheduled (future start), ended (past end), active otherwise
        $stateKey = 'active';
        if ($startTs !== null && $startTs > $now) {
            $stateKey = 'scheduled';
        } elseif ($endTs !== null && $endTs < $now) {
            $stateKey = 'ended';
        }

        // Use shared palette mapping so the date-range step color matches the list/status badges
        // Support both 'pending' and 'scheduled' as an orange token via Palette
        $token = Palette::newsletterStateToken($stateKey);

        // Determine the visual variant ('pill' or default) to build accurate class strings
        $variant = (string)($cfg['variant'] ?? ($opts['variant'] ?? 'pill'));
        $pal = Palette::map();

        // Classes for the initially computed state (server-side)
        $paletteCls = Palette::classes($token);
        $paletteClsRing = $pal[$token]['ring'] ?? '';
        $isInteractive = self::isInteractive($cfg);

        // Helper to compose the full dynamic class string per token, honoring the variant
        $composeVariantClasses = function (string $tok) use ($variant, $pal, $isInteractive): string {
            if ($variant === 'pill') {
                $bg   = $pal[$tok]['dot'] ?? '';
                if (!$isInteractive) {
                    $bg = preg_replace('/\bhover:[^\s]+\b/', '', $bg);
                }
                $ring = $pal[$tok]['ring'] ?? '';
                $txt  = $pal[$tok]['pill-text'] ?? '';
                return trim($bg . ' ' . $ring . ' ' . $txt);
            }
            // default: non-pill uses bg+text plus ring color
            $cls = Palette::classes($tok);
            $ring = $pal[$tok]['ring'] ?? '';
            return trim($cls . ' ' . $ring);
        };

        // Also pre-compute classes for all possible dynamic states so Alpine.js can switch them client-side
        $tokenActive = Palette::newsletterStateToken('active');
        $tokenScheduled = Palette::newsletterStateToken('scheduled');
        $tokenEnded = Palette::newsletterStateToken('ended');

        $clsActiveFull = $composeVariantClasses($tokenActive);
        $clsScheduledFull = $composeVariantClasses($tokenScheduled);
        $clsEndedFull = $composeVariantClasses($tokenEnded);

        // Build the base icon wrapper classes using the server-side helpers, then strip palette-specific classes
        $iconWrapCls = self::getIconWrapClasses($cfg, array_merge($opts, [
            'color' => $token,
            'palette_classes' => $paletteCls,
            'palette_ring' => $paletteClsRing,
        ]), $sz);

        // Remove color/ring classes so we can apply them dynamically on the client without conflicts.
        // For 'pill', FlowBadge builds classes from dot/pill-text/ring; for default it uses bg+text and ring.
        $strip = [];
        if ($variant === 'pill') {
            $strip = [
                $pal[$token]['dot'] ?? '',
                $pal[$token]['pill-text'] ?? '',
                $pal[$token]['ring'] ?? '',
            ];
        } else {
            $strip = [
                $paletteCls,
                $paletteClsRing,
            ];
        }
        $baseIconWrapCls = $iconWrapCls;
        foreach ($strip as $rem) {
            if ($rem !== '') {
                $baseIconWrapCls = str_replace($rem, '', $baseIconWrapCls);
            }
        }
        // Normalize whitespace
        $baseIconWrapCls = trim(preg_replace('/\s+/', ' ', $baseIconWrapCls));
        $baseIconWrapEsc = self::h($baseIconWrapCls, false);

        // Prepare Alpine datasets (escaped)
        $clsActiveEsc = self::h($clsActiveFull, false);
        $clsScheduledEsc = self::h($clsScheduledFull, false);
        $clsEndedEsc = self::h($clsEndedFull, false);
        $dataStart = self::h($startTs !== null ? (string)$startTs : '', false);
        $dataEnd = self::h($endTs !== null ? (string)$endTs : '', false);
        $titlePrefixEsc = self::h($labelTitle . ' - ', false);
        $stateTextEsc = self::h($stateKey, false);

        $labelBaseCls = 'rounded bg-surface px-1 ' . $sz['labelText'] . ' ';
        $upperPosCls = 'absolute -top-4';
        $lowerPosCls = 'absolute -bottom-4';

        $upperLabelHtml = '';
        if ($labelUpperEsc !== '') {
            $clsEsc = self::h(trim($upperPosCls . ' ' . $labelBaseCls), false);
            $upperLabelHtml = <<<HTML
<div class="{$clsEsc}">{$labelUpperEsc}</div>
HTML;
        }
        $lowerLabelHtml = '';
        if ($labelLowerEsc !== '') {
            $clsEsc = self::h(trim($lowerPosCls . ' ' . $labelBaseCls), false);
            $lowerLabelHtml = <<<HTML
<div class="{$clsEsc}">{$labelLowerEsc}</div>
HTML;
        }

        // The icon wrapper uses Alpine.js to adapt its palette classes to the current time without a reload.
        // We keep only structural classes static and bind palette classes dynamically based on computed state.
        $badgeInner = <<<HTML
  $upperLabelHtml
  <div
    class="$baseIconWrapEsc"
    title="$labelTitleEsc - $stateTextEsc"
    x-data="{
      state: '$stateKey',
      tick() {
        const now = Math.floor(Date.now() / 1000);
        const s = Number(this.\$el.dataset.startTs) || 0;
        const e = Number(this.\$el.dataset.endTs) || 0;
        if (s && now < s) this.state = 'scheduled';
        else if (e && now > e) this.state = 'ended';
        else this.state = 'active';
      }
    }"
    x-init="tick(); setInterval(() => tick(), 60000)"
    :class="state === 'scheduled' ? \$el.dataset.clsScheduled : (state === 'ended' ? \$el.dataset.clsEnded : \$el.dataset.clsActive)"
    :title="\$el.dataset.titlePrefix + state"
    data-start-ts="$dataStart"
    data-end-ts="$dataEnd"
    data-cls-active="$clsActiveEsc"
    data-cls-scheduled="$clsScheduledEsc"
    data-cls-ended="$clsEndedEsc"
    data-title-prefix="$titlePrefixEsc"
  >$icon</div>
  $lowerLabelHtml
HTML;

        return self::renderFlowBadge($cfg, array_merge($opts, [
            'badge_type_class' => 'daterange-badge',
            'badge_inner_html' => $badgeInner,
        ]));
    }

    /**
     * Build a concise label for a date range.
     */
    public static function buildRangeLabel($from, $to): string
    {

        $fmt = function ($v): ?\DateTime {
            if ($v === null || $v === '') return null;
            if (is_int($v)) {
                $dt = (new \DateTime('@' . $v));
                $dt->setTimezone(new \DateTimeZone(date_default_timezone_get()));
                return $dt;
            }
            if (is_string($v)) {
                $ts = strtotime($v);
                if ($ts !== false) {
                    $dt = (new \DateTime('@' . $ts));
                    $dt->setTimezone(new \DateTimeZone(date_default_timezone_get()));
                    return $dt;
                }
            }
            return null;
        };

        $df = $fmt($from);
        $dt = $fmt($to);

        if ($df && $dt) {
            // If same month/year, compact form: 1–12 Dec 2025
            if ($df->format('Ym') === $dt->format('Ym')) {
                return $df->format('j') . '-' . $dt->format('j M Y');
            }
            return $df->format('j M Y') . ' .. ' . $dt->format('j M Y');
        }
        if ($df) {
            return 'From ' . $df->format('j M Y');
        }
        if ($dt) {
            return 'Until ' . $dt->format('j M Y');
        }
        return 'Date range';
    }

    /**
     * Build split labels suitable for rendering above/below the icon.
     * Returns [upper, lower, title]
     * - upper: formatted start date (e.g., 1 Dec 2025) or '' if not set
     * - lower: formatted end date (e.g., 12 Dec 2025) or '' if not set
     * - title: combined readable label (using buildRangeLabel)
     * If $overrideLabel is provided, it will be used as the title and placed as the lower label,
     * leaving the upper empty to avoid double text. This preserves previous API behavior where
     * a custom label replaces autogenerated content.
     */
    public static function buildRangeLabels($from, $to, ?string $overrideLabel = null): array
    {
        $fmt = function ($v): ?\DateTime {
            if ($v === null || $v === '') return null;
            if (is_int($v)) {
                $dt = (new \DateTime('@' . $v));
                $dt->setTimezone(new \DateTimeZone(date_default_timezone_get()));
                return $dt;
            }
            if (is_string($v)) {
                $ts = strtotime($v);
                if ($ts !== false) {
                    $dt = (new \DateTime('@' . $ts));
                    $dt->setTimezone(new \DateTimeZone(date_default_timezone_get()));
                    return $dt;
                }
            }
            return null;
        };

        if ($overrideLabel !== null && $overrideLabel !== '') {
            $title = $overrideLabel;
            return ['', $overrideLabel, $title];
        }

        $df = $fmt($from);
        $dt = $fmt($to);

        $upper = $df ? $df->format('j M Y') : '';
        $lower = $dt ? $dt->format('j M Y') : '';
        $title = self::buildRangeLabel($from, $to);
        // Fallback: if both dates are empty, show the combined title as the lower label for visibility
        if ($upper === '' && $lower === '' && $title !== '') {
            $lower = $title;
        }

        return [$upper, $lower, $title];
    }
}
