<?php

namespace Mtc\Plugins\BlockManager\Services;

use Mtc\Plugins\BlockManager\Models\BlockManagerVariantDraft;

/**
 * Emits the LESS stylesheet for a Designer-published variant.
 *
 * Differs from VariantPreviewService in one critical way: token references
 * are **preserved as @varname** (literal LESS variables) instead of being
 * resolved to their hex/length values. The LESS compiler at site-build time
 * substitutes them — keeping the design system token-aware end-to-end.
 *
 * Output structure:
 *   /\* @designer-meta { ...JSON snapshot... } *\/
 *   .bm-{element}--{slug} {
 *     color: @primary_color;   // token ref preserved
 *     font-size: 16px;         // raw value passes through
 *     ...
 *   }
 *
 * The `@designer-meta` header lets Phase 14's round-trip parser reopen the
 * file directly into the Designer's property tree without re-deriving from CSS.
 */
class VariantLessGenerator
{
    public function generate(BlockManagerVariantDraft $draft): string
    {
        $meta = [
            'version'      => 1,
            'element_type' => $draft->element_type,
            'variant_slug' => $draft->variant_slug,
            'properties'   => $draft->properties_json ?? [],
        ];
        $header = '/* @designer-meta ' . json_encode($meta, JSON_UNESCAPED_SLASHES) . ' */';

        $selector = ".bm-{$draft->element_type}--{$draft->variant_slug}";
        $properties = $draft->properties_json ?? [];

        $baseRules = $this->buildRulesFromCell($this->cell($properties, 'default'));

        $stateBlocks = [];
        foreach (self::STATES as $state) {
            $cell = $this->cell($properties, $state);
            $rules = $this->buildRulesFromCell($cell);
            if ($rules === []) {
                continue;
            }
            $stateBlocks[] = ['state' => $state, 'rules' => $rules];
        }

        $breakpointBlocks = [];
        foreach (self::BREAKPOINTS as $breakpoint) {
            $cell = $this->cell($properties, 'default', $breakpoint);
            $rules = $this->buildRulesFromCell($cell);
            if ($rules === []) {
                continue;
            }
            $breakpointBlocks[] = ['breakpoint' => $breakpoint, 'rules' => $rules];
        }

        $body = $selector . " {\n";
        foreach ($baseRules as $rule) {
            $body .= "  {$rule}\n";
        }
        foreach ($stateBlocks as $block) {
            $body .= "\n  &:{$block['state']} {\n";
            foreach ($block['rules'] as $rule) {
                $body .= "    {$rule}\n";
            }
            $body .= "  }\n";
        }
        foreach ($breakpointBlocks as $block) {
            $mediaVar = self::BREAKPOINT_MEDIA[$block['breakpoint']];
            $body .= "\n  @media (max-width: {$mediaVar}) {\n";
            foreach ($block['rules'] as $rule) {
                $body .= "    {$rule}\n";
            }
            $body .= "  }\n";
        }
        $customCss = $this->customCss($properties);
        if ($customCss !== null) {
            $body .= "\n  {$customCss}\n";
        }
        $body .= "}\n";

        return $header . "\n" . $body;
    }

    /**
     * Read one (state, breakpoint) cell from the property tree.
     *
     * @param array<string, mixed> $properties
     * @return array<string, mixed>
     */
    private function cell(array $properties, string $state, string $breakpoint = 'desktop'): array
    {
        $cell = $properties['base'][$state][$breakpoint] ?? null;
        return is_array($cell) ? $cell : [];
    }

    /**
     * @param array<string, mixed> $cell  panel name => property name => value wrap
     * @return array<int, string>
     */
    private function buildRulesFromCell(array $cell): array
    {
        $rules = [];
        foreach (self::PANEL_TO_CSS as $panel => $propertyMap) {
            $panelValues = is_array($cell[$panel] ?? null) ? $cell[$panel] : [];
            foreach ($panelValues as $propertyName => $valueWrap) {
                $cssTargets = $propertyMap[$propertyName] ?? null;
                if ($cssTargets === null) {
                    continue;
                }

                $resolved = $this->resolveValue($valueWrap);
                if ($resolved === null) {
                    continue;
                }

                $resolved = $this->postProcess($panel, (string) $propertyName, $resolved);
                foreach ((array) $cssTargets as $cssProperty) {
                    $rules[] = "{$cssProperty}: {$resolved};";
                }
            }
        }
        return $rules;
    }

    /**
     * For LESS publish: token references survive as `@varname` (LESS variables).
     * Raw values pass through unchanged. Empty/null wrappers are skipped.
     *
     * @param mixed $value
     */
    private function resolveValue($value): ?string
    {
        if (!is_array($value)) {
            return null;
        }
        if (isset($value['token']) && is_string($value['token']) && $value['token'] !== '') {
            return $value['token'];
        }
        if (array_key_exists('raw', $value) && $value['raw'] !== '' && $value['raw'] !== null) {
            return (string) $value['raw'];
        }
        return null;
    }

    private function postProcess(string $panel, string $property, string $value): string
    {
        if ($panel === 'background' && $property === 'image') {
            if (
                !str_starts_with($value, 'url(') &&
                !str_starts_with($value, 'linear-gradient') &&
                !str_starts_with($value, '@')
            ) {
                return "url({$value})";
            }
        }
        if ($panel === 'effects' && $property === 'blur') {
            return "blur({$value})";
        }
        return $value;
    }

    private function customCss(array $properties): ?string
    {
        $raw = $this->cell($properties, 'default')['custom_css']['css']['raw'] ?? null;
        if (!is_string($raw) || trim($raw) === '') {
            return null;
        }
        return trim($raw);
    }

    private const STATES = ['hover', 'focus', 'active', 'disabled'];

    private const BREAKPOINTS = ['tablet', 'mobile'];

    private const BREAKPOINT_MEDIA = [
        'tablet' => '@medium_width',
        'mobile' => '@mobile_width',
    ];

    private const PANEL_TO_CSS = [
        'typography' => [
            'color'           => 'color',
            'font_family'     => 'font-family',
            'font_size'       => 'font-size',
            'font_weight'     => 'font-weight',
            'line_height'     => 'line-height',
            'letter_spacing'  => 'letter-spacing',
            'text_transform'  => 'text-transform',
            'text_decoration' => 'text-decoration',
            'text_align'      => 'text-align',
        ],
        'box' => [
            'width'     => 'width',
            'height'    => 'height',
            'padding_x' => ['padding-left', 'padding-right'],
            'padding_y' => ['padding-top',  'padding-bottom'],
            'margin_x'  => ['margin-left',  'margin-right'],
            'margin_y'  => ['margin-top',   'margin-bottom'],
        ],
        'border' => [
            'border_width'  => 'border-width',
            'border_style'  => 'border-style',
            'border_color'  => 'border-color',
            'border_radius' => 'border-radius',
        ],
        'background' => [
            'color'    => 'background-color',
            'image'    => 'background-image',
            'gradient' => 'background-image',
        ],
        'layout' => [
            'display'         => 'display',
            'flex_direction'  => 'flex-direction',
            'justify_content' => 'justify-content',
            'align_items'     => 'align-items',
            'gap'             => 'gap',
        ],
        'effects' => [
            'box_shadow' => 'box-shadow',
            'opacity'    => 'opacity',
            'blur'       => 'filter',
        ],
    ];
}
