<?php

namespace App\Services\Intune;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;

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

    public function supports(string $policyType): bool
    {
        return in_array($policyType, [
            'windowsAutopilotDeploymentProfile',
            'windowsEnrollmentStatusPage',
            'enrollmentRestriction',
            'deviceEnrollmentLimitConfiguration',
            'deviceEnrollmentPlatformRestrictionsConfiguration',
            'deviceEnrollmentNotificationConfiguration',
        ], true);
    }

    /**
     * @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 = is_array($snapshot) ? $snapshot : [];

        $displayName = Arr::get($snapshot, 'displayName') ?? Arr::get($snapshot, 'name');
        $description = Arr::get($snapshot, 'description');

        $warnings = [];

        if ($policyType === 'enrollmentRestriction') {
            $warnings[] = 'Restore is preview-only for Enrollment Restrictions.';
        }

        if ($policyType === 'deviceEnrollmentLimitConfiguration') {
            $warnings[] = 'Restore is preview-only for Enrollment Limits.';
        }

        if ($policyType === 'deviceEnrollmentPlatformRestrictionsConfiguration') {
            $warnings[] = 'Restore is preview-only for Platform Restrictions.';
        }

        if ($policyType === 'deviceEnrollmentNotificationConfiguration') {
            $warnings[] = 'Restore is preview-only for Enrollment Notifications.';
        }

        $generalEntries = [
            ['key' => 'Type', 'value' => $policyType],
        ];

        if (is_string($displayName) && $displayName !== '') {
            $generalEntries[] = ['key' => 'Display name', 'value' => $displayName];
        }

        if (is_string($description) && $description !== '') {
            $generalEntries[] = ['key' => 'Description', 'value' => $description];
        }

        $odataType = Arr::get($snapshot, '@odata.type');
        if (is_string($odataType) && $odataType !== '') {
            $generalEntries[] = ['key' => '@odata.type', 'value' => $odataType];
        }

        $roleScopeTagIds = Arr::get($snapshot, 'roleScopeTagIds');
        if (is_array($roleScopeTagIds) && $roleScopeTagIds !== []) {
            $generalEntries[] = ['key' => 'Scope tag IDs', 'value' => array_values($roleScopeTagIds)];
        }

        $settings = [
            [
                'type' => 'keyValue',
                'title' => 'General',
                'entries' => $generalEntries,
            ],
        ];

        $typeBlock = match ($policyType) {
            'windowsAutopilotDeploymentProfile' => $this->buildAutopilotBlock($snapshot),
            'windowsEnrollmentStatusPage' => $this->buildEnrollmentStatusPageBlock($snapshot),
            'enrollmentRestriction' => $this->buildEnrollmentRestrictionBlock($snapshot),
            'deviceEnrollmentLimitConfiguration' => $this->buildEnrollmentLimitBlock($snapshot),
            'deviceEnrollmentPlatformRestrictionsConfiguration' => $this->buildEnrollmentPlatformRestrictionsBlock($snapshot),
            'deviceEnrollmentNotificationConfiguration' => $this->buildEnrollmentNotificationBlock($snapshot),
            default => null,
        };

        if ($typeBlock !== null) {
            $settings[] = $typeBlock;
        }

        $settings = array_values(array_filter($settings));

        return [
            'status' => 'ok',
            'settings' => $settings,
            'warnings' => $warnings,
        ];
    }

    /**
     * @return array{type: string, title: string, entries: array<int, array{key: string, value: mixed}>}|null
     */
    private function buildAutopilotBlock(array $snapshot): ?array
    {
        $entries = [];

        foreach ([
            'deviceNameTemplate' => 'Device name template',
            'language' => 'Language',
            'locale' => 'Locale',
            'deploymentMode' => 'Deployment mode',
            'deviceType' => 'Device type',
            'enableWhiteGlove' => 'Pre-provisioning (White Glove)',
            'hybridAzureADJoinSkipConnectivityCheck' => 'Skip Hybrid AAD connectivity check',
        ] as $key => $label) {
            $value = Arr::get($snapshot, $key);

            if (is_string($value) && $value !== '') {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_bool($value)) {
                $entries[] = ['key' => $label, 'value' => $value ? 'Enabled' : 'Disabled'];
            }
        }

        $oobe = Arr::get($snapshot, 'outOfBoxExperienceSettings');
        if (is_array($oobe) && $oobe !== []) {
            $oobe = Arr::except($oobe, ['@odata.type']);

            foreach ($this->expandOutOfBoxExperienceEntries($oobe) as $entry) {
                $entries[] = $entry;
            }
        }

        $assignments = Arr::get($snapshot, 'assignments');
        if (is_array($assignments) && $assignments !== []) {
            $entries[] = ['key' => 'Assignments (snapshot)', 'value' => '[present]'];
        }

        if ($entries === []) {
            return null;
        }

        return [
            'type' => 'keyValue',
            'title' => 'Autopilot profile',
            'entries' => $entries,
        ];
    }

    /**
     * @return array<int, array{key: string, value: mixed}>
     */
    private function expandOutOfBoxExperienceEntries(array $oobe): array
    {
        $knownKeys = [
            'hideEULA' => 'Hide EULA',
            'userType' => 'User type',
            'hideEscapeLink' => 'Hide escape link',
            'deviceUsageType' => 'Device usage type',
            'hidePrivacySettings' => 'Hide privacy settings',
            'skipKeyboardSelectionPage' => 'Skip keyboard selection page',
            'skipExpressSettings' => 'Skip express settings',
        ];

        $entries = [];

        foreach ($knownKeys as $key => $label) {
            if (! array_key_exists($key, $oobe)) {
                continue;
            }

            $value = $oobe[$key];

            if (is_bool($value)) {
                $entries[] = ['key' => "OOBE: {$label}", 'value' => $value ? 'Enabled' : 'Disabled'];
            } elseif (is_string($value) && $value !== '') {
                $entries[] = ['key' => "OOBE: {$label}", 'value' => $value];
            } elseif (is_int($value) || is_float($value)) {
                $entries[] = ['key' => "OOBE: {$label}", 'value' => $value];
            }

            unset($oobe[$key]);
        }

        foreach ($oobe as $key => $value) {
            $label = Str::headline((string) $key);

            if (is_bool($value)) {
                $entries[] = ['key' => "OOBE: {$label}", 'value' => $value ? 'Enabled' : 'Disabled'];
            } elseif (is_string($value) && $value !== '') {
                $entries[] = ['key' => "OOBE: {$label}", 'value' => $value];
            } elseif (is_int($value) || is_float($value)) {
                $entries[] = ['key' => "OOBE: {$label}", 'value' => $value];
            } elseif (is_array($value) && $value !== []) {
                $entries[] = ['key' => "OOBE: {$label}", 'value' => $value];
            }
        }

        return $entries;
    }

    /**
     * @return array{type: string, title: string, entries: array<int, array{key: string, value: mixed}>}|null
     */
    private function buildEnrollmentStatusPageBlock(array $snapshot): ?array
    {
        $entries = [];

        foreach ([
            'priority' => 'Priority',
            'showInstallationProgress' => 'Show installation progress',
            'blockDeviceSetupRetryByUser' => 'Block retry by user',
            'allowDeviceResetOnInstallFailure' => 'Allow device reset on install failure',
            'installProgressTimeoutInMinutes' => 'Install progress timeout (minutes)',
            'allowLogCollectionOnInstallFailure' => 'Allow log collection on failure',
        ] as $key => $label) {
            $value = Arr::get($snapshot, $key);

            if (is_int($value) || is_float($value)) {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_string($value) && $value !== '') {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_bool($value)) {
                $entries[] = ['key' => $label, 'value' => $value ? 'Enabled' : 'Disabled'];
            }
        }

        $selected = Arr::get($snapshot, 'selectedMobileAppIds');
        if (is_array($selected) && $selected !== []) {
            $entries[] = ['key' => 'Selected mobile app IDs', 'value' => array_values($selected)];
        }

        $assigned = Arr::get($snapshot, 'assignments');
        if (is_array($assigned) && $assigned !== []) {
            $entries[] = ['key' => 'Assignments (snapshot)', 'value' => '[present]'];
        }

        if ($entries === []) {
            return null;
        }

        return [
            'type' => 'keyValue',
            'title' => 'Enrollment Status Page (ESP)',
            'entries' => $entries,
        ];
    }

    /**
     * @return array{type: string, title: string, entries: array<int, array{key: string, value: mixed}>}|null
     */
    private function buildEnrollmentRestrictionBlock(array $snapshot): ?array
    {
        $entries = [];

        foreach ([
            'priority' => 'Priority',
            'version' => 'Version',
            'deviceEnrollmentConfigurationType' => 'Configuration type',
        ] as $key => $label) {
            $value = Arr::get($snapshot, $key);

            if (is_int($value) || is_float($value)) {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_string($value) && $value !== '') {
                $entries[] = ['key' => $label, 'value' => $value];
            }
        }

        $platformRestrictions = Arr::get($snapshot, 'platformRestrictions');
        $platformRestriction = Arr::get($snapshot, 'platformRestriction');

        $platformPayload = is_array($platformRestrictions) && $platformRestrictions !== []
            ? $platformRestrictions
            : (is_array($platformRestriction) ? $platformRestriction : null);

        if (is_array($platformPayload) && $platformPayload !== []) {
            $platformPayload = Arr::except($platformPayload, ['@odata.type']);

            $platformBlocked = Arr::get($platformPayload, 'platformBlocked');
            if (is_bool($platformBlocked)) {
                $entries[] = ['key' => 'Platform blocked', 'value' => $platformBlocked ? 'Enabled' : 'Disabled'];
            }

            $personalBlocked = Arr::get($platformPayload, 'personalDeviceEnrollmentBlocked');
            if (is_bool($personalBlocked)) {
                $entries[] = ['key' => 'Personal device enrollment blocked', 'value' => $personalBlocked ? 'Enabled' : 'Disabled'];
            }

            $osMin = Arr::get($platformPayload, 'osMinimumVersion');
            $entries[] = [
                'key' => 'OS minimum version',
                'value' => (is_string($osMin) && $osMin !== '') ? $osMin : 'None',
            ];

            $osMax = Arr::get($platformPayload, 'osMaximumVersion');
            $entries[] = [
                'key' => 'OS maximum version',
                'value' => (is_string($osMax) && $osMax !== '') ? $osMax : 'None',
            ];

            $blockedManufacturers = Arr::get($platformPayload, 'blockedManufacturers');
            $entries[] = [
                'key' => 'Blocked manufacturers',
                'value' => (is_array($blockedManufacturers) && $blockedManufacturers !== [])
                    ? array_values($blockedManufacturers)
                    : ['None'],
            ];

            $blockedSkus = Arr::get($platformPayload, 'blockedSkus');
            $entries[] = [
                'key' => 'Blocked SKUs',
                'value' => (is_array($blockedSkus) && $blockedSkus !== [])
                    ? array_values($blockedSkus)
                    : ['None'],
            ];
        }

        $assigned = Arr::get($snapshot, 'assignments');
        if (is_array($assigned) && $assigned !== []) {
            $entries[] = ['key' => 'Assignments (snapshot)', 'value' => '[present]'];
        }

        if ($entries === []) {
            return null;
        }

        return [
            'type' => 'keyValue',
            'title' => 'Enrollment restrictions',
            'entries' => $entries,
        ];
    }

    /**
     * @return array{type: string, title: string, entries: array<int, array{key: string, value: mixed}>}|null
     */
    private function buildEnrollmentLimitBlock(array $snapshot): ?array
    {
        $entries = [];

        foreach ([
            'priority' => 'Priority',
            'version' => 'Version',
            'deviceEnrollmentConfigurationType' => 'Configuration type',
            'limit' => 'Enrollment limit',
            'limitType' => 'Limit type',
        ] as $key => $label) {
            $value = Arr::get($snapshot, $key);

            if (is_int($value) || is_float($value)) {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_string($value) && $value !== '') {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_bool($value)) {
                $entries[] = ['key' => $label, 'value' => $value ? 'Enabled' : 'Disabled'];
            }
        }

        $assigned = Arr::get($snapshot, 'assignments');
        if (is_array($assigned) && $assigned !== []) {
            $entries[] = ['key' => 'Assignments (snapshot)', 'value' => '[present]'];
        }

        if ($entries === []) {
            return null;
        }

        return [
            'type' => 'keyValue',
            'title' => 'Enrollment limits',
            'entries' => $entries,
        ];
    }

    /**
     * @return array{type: string, title: string, entries: array<int, array{key: string, value: mixed}>}|null
     */
    private function buildEnrollmentPlatformRestrictionsBlock(array $snapshot): ?array
    {
        $entries = [];

        foreach ([
            'priority' => 'Priority',
            'version' => 'Version',
            'platformType' => 'Platform type',
            'deviceEnrollmentConfigurationType' => 'Configuration type',
        ] as $key => $label) {
            $value = Arr::get($snapshot, $key);

            if (is_int($value) || is_float($value)) {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_string($value) && $value !== '') {
                $entries[] = ['key' => $label, 'value' => $value];
            }
        }

        $platformPayload = Arr::get($snapshot, 'platformRestrictions') ?? Arr::get($snapshot, 'platformRestriction');
        if (is_array($platformPayload) && $platformPayload !== []) {
            $prefix = (string) (Arr::get($snapshot, 'platformType') ?: 'Platform');
            $this->appendPlatformRestrictionEntries($entries, $prefix, $platformPayload);
        }

        $typedRestrictions = [
            'androidForWorkRestriction' => 'Android work profile',
            'androidRestriction' => 'Android',
            'iosRestriction' => 'iOS/iPadOS',
            'macRestriction' => 'macOS',
            'windowsRestriction' => 'Windows',
        ];

        foreach ($typedRestrictions as $key => $prefix) {
            $restriction = Arr::get($snapshot, $key);

            if (! is_array($restriction) || $restriction === []) {
                continue;
            }

            $this->appendPlatformRestrictionEntries($entries, $prefix, $restriction);
        }

        $assigned = Arr::get($snapshot, 'assignments');
        if (is_array($assigned) && $assigned !== []) {
            $entries[] = ['key' => 'Assignments (snapshot)', 'value' => '[present]'];
        }

        if ($entries === []) {
            return null;
        }

        return [
            'type' => 'keyValue',
            'title' => 'Platform restrictions (enrollment)',
            'entries' => $entries,
        ];
    }

    /**
     * @param  array<int, array{key: string, value: mixed}>  $entries
     */
    private function appendPlatformRestrictionEntries(array &$entries, string $prefix, array $payload): void
    {
        $payload = Arr::except($payload, ['@odata.type']);

        $platformBlocked = Arr::get($payload, 'platformBlocked');
        if (is_bool($platformBlocked)) {
            $entries[] = ['key' => "{$prefix}: Platform blocked", 'value' => $platformBlocked ? 'Enabled' : 'Disabled'];
        }

        $personalBlocked = Arr::get($payload, 'personalDeviceEnrollmentBlocked');
        if (is_bool($personalBlocked)) {
            $entries[] = ['key' => "{$prefix}: Personal device enrollment blocked", 'value' => $personalBlocked ? 'Enabled' : 'Disabled'];
        }

        $osMin = Arr::get($payload, 'osMinimumVersion');
        $entries[] = [
            'key' => "{$prefix}: OS minimum version",
            'value' => (is_string($osMin) && $osMin !== '') ? $osMin : 'None',
        ];

        $osMax = Arr::get($payload, 'osMaximumVersion');
        $entries[] = [
            'key' => "{$prefix}: OS maximum version",
            'value' => (is_string($osMax) && $osMax !== '') ? $osMax : 'None',
        ];

        $blockedManufacturers = Arr::get($payload, 'blockedManufacturers');
        $entries[] = [
            'key' => "{$prefix}: Blocked manufacturers",
            'value' => (is_array($blockedManufacturers) && $blockedManufacturers !== [])
                ? array_values($blockedManufacturers)
                : ['None'],
        ];

        $blockedSkus = Arr::get($payload, 'blockedSkus');
        $entries[] = [
            'key' => "{$prefix}: Blocked SKUs",
            'value' => (is_array($blockedSkus) && $blockedSkus !== [])
                ? array_values($blockedSkus)
                : ['None'],
        ];
    }

    /**
     * @return array{type: string, title: string, entries: array<int, array{key: string, value: mixed}>}|null
     */
    private function buildEnrollmentNotificationBlock(array $snapshot): ?array
    {
        $entries = [];

        foreach ([
            'priority' => 'Priority',
            'version' => 'Version',
            'platformType' => 'Platform type',
            'deviceEnrollmentConfigurationType' => 'Configuration type',
            'brandingOptions' => 'Branding options',
            'templateType' => 'Template type',
            'defaultLocale' => 'Default locale',
            'notificationMessageTemplateId' => 'Notification message template ID',
        ] as $key => $label) {
            $value = Arr::get($snapshot, $key);

            if (is_int($value) || is_float($value)) {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_string($value) && $value !== '') {
                $entries[] = ['key' => $label, 'value' => $value];
            } elseif (is_bool($value)) {
                $entries[] = ['key' => $label, 'value' => $value ? 'Enabled' : 'Disabled'];
            }
        }

        $notificationTemplates = Arr::get($snapshot, 'notificationTemplates');
        if (is_array($notificationTemplates) && $notificationTemplates !== []) {
            $entries[] = ['key' => 'Notification templates', 'value' => array_values($notificationTemplates)];
        }

        $templateSnapshots = Arr::get($snapshot, 'notificationTemplateSnapshots');
        if (is_array($templateSnapshots) && $templateSnapshots !== []) {
            foreach ($templateSnapshots as $templateSnapshot) {
                if (! is_array($templateSnapshot)) {
                    continue;
                }

                $channel = Arr::get($templateSnapshot, 'channel');
                $channelLabel = is_string($channel) && $channel !== '' ? $channel : 'Template';

                $templateId = Arr::get($templateSnapshot, 'template_id');
                if (is_string($templateId) && $templateId !== '') {
                    $entries[] = ['key' => "{$channelLabel} template ID", 'value' => $templateId];
                }

                $template = Arr::get($templateSnapshot, 'template');
                if (is_array($template) && $template !== []) {
                    $displayName = Arr::get($template, 'displayName');
                    if (is_string($displayName) && $displayName !== '') {
                        $entries[] = ['key' => "{$channelLabel} template name", 'value' => $displayName];
                    }

                    $brandingOptions = Arr::get($template, 'brandingOptions');
                    if (is_string($brandingOptions) && $brandingOptions !== '') {
                        $entries[] = ['key' => "{$channelLabel} branding options", 'value' => $brandingOptions];
                    }

                    $defaultLocale = Arr::get($template, 'defaultLocale');
                    if (is_string($defaultLocale) && $defaultLocale !== '') {
                        $entries[] = ['key' => "{$channelLabel} default locale", 'value' => $defaultLocale];
                    }
                }

                $localizedMessages = Arr::get($templateSnapshot, 'localized_notification_messages');
                if (is_array($localizedMessages) && $localizedMessages !== []) {
                    foreach ($localizedMessages as $localizedMessage) {
                        if (! is_array($localizedMessage)) {
                            continue;
                        }

                        $locale = Arr::get($localizedMessage, 'locale');
                        $localeLabel = is_string($locale) && $locale !== '' ? $locale : 'locale';

                        $subject = Arr::get($localizedMessage, 'subject');
                        if (is_string($subject) && $subject !== '') {
                            $entries[] = ['key' => "{$channelLabel} ({$localeLabel}) Subject", 'value' => $subject];
                        }

                        $messageTemplate = Arr::get($localizedMessage, 'messageTemplate');
                        if (is_string($messageTemplate) && $messageTemplate !== '') {
                            $entries[] = ['key' => "{$channelLabel} ({$localeLabel}) Message", 'value' => $messageTemplate];
                        }

                        $isDefault = Arr::get($localizedMessage, 'isDefault');
                        if (is_bool($isDefault)) {
                            $entries[] = ['key' => "{$channelLabel} ({$localeLabel}) Default", 'value' => $isDefault ? 'Enabled' : 'Disabled'];
                        }
                    }
                }
            }
        }

        $assigned = Arr::get($snapshot, 'assignments');
        if (is_array($assigned) && $assigned !== []) {
            $entries[] = ['key' => 'Assignments (snapshot)', 'value' => '[present]'];
        }

        if ($entries === []) {
            return null;
        }

        return [
            'type' => 'keyValue',
            'title' => 'Enrollment notifications',
            'entries' => $entries,
        ];
    }

    /**
     * @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);
    }
}
