<?php

namespace App\Services\Intune;

use Illuminate\Support\Arr;

class GroupPolicyConfigurationNormalizer implements PolicyTypeNormalizer
{
    public function __construct(
        private readonly DefaultPolicyNormalizer $defaultNormalizer,
    ) {}

    public function supports(string $policyType): bool
    {
        return $policyType === 'groupPolicyConfiguration';
    }

    /**
     * @return array{status: string, settings: array<int, array<string, mixed>>, settings_table?: array<string, mixed>, warnings: array<int, string>}
     */
    public function normalize(?array $snapshot, string $policyType, ?string $platform = null): array
    {
        $snapshot = $snapshot ?? [];
        $definitionValues = $snapshot['definitionValues'] ?? null;
        $snapshot = Arr::except($snapshot, ['definitionValues']);

        $normalized = $this->defaultNormalizer->normalize($snapshot, $policyType, $platform);

        if (! is_array($definitionValues) || $definitionValues === []) {
            $normalized['warnings'] = array_values(array_unique(array_merge(
                $normalized['warnings'] ?? [],
                ['Administrative Template settings not hydrated for this policy.']
            )));

            return $normalized;
        }

        $rows = [];

        foreach ($definitionValues as $index => $definitionValue) {
            if (! is_array($definitionValue)) {
                continue;
            }

            $definition = $definitionValue['#Definition_displayName'] ?? null;
            $definitionId = $definitionValue['#Definition_Id'] ?? null;
            $category = $definitionValue['#Definition_categoryPath'] ?? '-';
            $enabled = (bool) ($definitionValue['enabled'] ?? false);
            $path = $this->buildDiffPath(
                definition: $definition,
                definitionId: $definitionId,
                categoryPath: $category,
                index: $index,
            );

            $value = $this->formatGroupPolicyValue($definitionValue, $enabled);
            $dataType = $this->inferGroupPolicyDataType($definitionValue);

            $rows[] = [
                'definition' => is_string($definition) && $definition !== '' ? $definition : 'Definition',
                'definition_id' => is_string($definitionId) ? $definitionId : null,
                'category' => is_string($category) && $category !== '' ? $category : '-',
                'data_type' => $dataType,
                'value' => $value,
                'description' => '-',
                'path' => $path,
                'raw' => $definitionValue,
            ];
        }

        if ($rows !== []) {
            $normalized['settings_table'] = [
                'title' => 'Administrative Template settings',
                'rows' => $rows,
            ];
        }

        return $normalized;
    }

    /**
     * @return array<string, mixed>
     */
    public function flattenForDiff(?array $snapshot, string $policyType, ?string $platform = null): array
    {
        $normalized = $this->normalize($snapshot ?? [], $policyType, $platform);

        return $this->defaultNormalizer->flattenNormalizedForDiff($normalized);
    }

    private function inferGroupPolicyDataType(array $definitionValue): string
    {
        $presentationValues = $definitionValue['presentationValues'] ?? null;

        if (! is_array($presentationValues) || $presentationValues === []) {
            return 'Boolean';
        }

        foreach ($presentationValues as $presentationValue) {
            if (! is_array($presentationValue)) {
                continue;
            }

            if (array_key_exists('values', $presentationValue)) {
                return 'Choice';
            }

            if (array_key_exists('value', $presentationValue)) {
                $value = $presentationValue['value'];

                if (is_bool($value)) {
                    return 'Boolean';
                }

                if (is_int($value) || is_float($value) || is_numeric($value)) {
                    return 'Number';
                }

                return 'Text';
            }
        }

        return 'Text';
    }

    private function formatGroupPolicyValue(array $definitionValue, bool $enabled): string
    {
        $presentationValues = $definitionValue['presentationValues'] ?? null;

        if (! is_array($presentationValues) || $presentationValues === []) {
            return $enabled ? 'Enabled' : 'Disabled';
        }

        $parts = [];

        foreach ($presentationValues as $presentationValue) {
            if (! is_array($presentationValue)) {
                continue;
            }

            $label = $presentationValue['#Presentation_Label'] ?? null;
            $value = $presentationValue['value'] ?? null;
            $values = $presentationValue['values'] ?? null;

            $valueString = match (true) {
                is_array($values) => json_encode($values),
                is_bool($value) => $value ? 'true' : 'false',
                is_scalar($value) => (string) $value,
                default => null,
            };

            if ($valueString === null) {
                $clean = Arr::except($presentationValue, ['presentation@odata.bind', '#Presentation_Label', '#Presentation_Id']);
                $valueString = $clean !== [] ? json_encode($clean) : null;
            }

            if (is_string($label) && $label !== '') {
                $parts[] = $label.': '.($valueString ?? '-');
            } else {
                $parts[] = $valueString ?? '-';
            }
        }

        return implode(' | ', array_values(array_filter($parts, static fn ($part) => $part !== '')));
    }

    private function buildDiffPath(mixed $definition, mixed $definitionId, mixed $categoryPath, int $index): string
    {
        $label = is_string($definition) && $definition !== '' ? $definition : "definitionValues[{$index}]";

        if (is_string($definitionId) && $definitionId !== '') {
            $label .= " ({$definitionId})";
        }

        if (is_string($categoryPath) && $categoryPath !== '' && $categoryPath !== '-') {
            return $categoryPath.' > '.$label;
        }

        return $label;
    }
}
