<?php

namespace App\Console\Commands;

use App\Models\Policy;
use App\Services\Intune\SettingsCatalogDefinitionResolver;
use Illuminate\Console\Command;

class WarmSettingsCatalogDefinitionsCache extends Command
{
    protected $signature = 'intune:warm-definitions-cache 
                            {--policy= : Specific policy ID to warm cache for}
                            {--all : Warm cache for all Settings Catalog policies}
                            {--force : Force re-fetch even if cached}';

    protected $description = 'Warm the Settings Catalog definitions cache by fetching display names from Graph API';

    public function handle(SettingsCatalogDefinitionResolver $resolver): int
    {
        if ($this->option('policy')) {
            return $this->warmForPolicy($this->option('policy'), $resolver);
        }

        if ($this->option('all')) {
            return $this->warmForAllPolicies($resolver);
        }

        $this->error('Please specify either --policy=ID or --all');

        return self::FAILURE;
    }

    private function warmForPolicy(string $policyId, SettingsCatalogDefinitionResolver $resolver): int
    {
        $policy = Policy::find($policyId);

        if (! $policy) {
            $this->error("Policy {$policyId} not found");

            return self::FAILURE;
        }

        if ($policy->policy_type !== 'settingsCatalog' && $policy->policy_type !== 'settingsCatalogPolicy') {
            $this->error("Policy {$policyId} is not a Settings Catalog policy");

            return self::FAILURE;
        }

        $this->info("Warming cache for policy: {$policy->display_name} ({$policy->id})");

        $snapshot = $policy->versions()->latest('version_number')->first()?->snapshot ?? [];
        $definitionIds = $this->extractDefinitionIds($snapshot);

        if (empty($definitionIds)) {
            $this->warn('No definition IDs found in policy snapshot');

            return self::SUCCESS;
        }

        $this->info('Found '.count($definitionIds).' definition IDs');
        $this->newLine();

        $progressBar = $this->output->createProgressBar(count($definitionIds));
        $progressBar->start();

        $success = 0;
        $failed = 0;
        $cached = 0;

        foreach ($definitionIds as $definitionId) {
            try {
                $definition = $resolver->resolveOne($definitionId);

                if ($definition) {
                    if (isset($definition['displayName']) && ! str_contains($definition['displayName'], 'Device Vendor Msft')) {
                        $success++;
                        $this->line("\n✓ {$definitionId} → {$definition['displayName']}", 'info');
                    } else {
                        $cached++;
                        $this->line("\n⚠ {$definitionId} → (fallback: {$definition['displayName']})", 'comment');
                    }
                } else {
                    $failed++;
                    $this->line("\n✗ {$definitionId} → Failed to resolve", 'error');
                }
            } catch (\Exception $e) {
                $failed++;
                $this->line("\n✗ {$definitionId} → Error: {$e->getMessage()}", 'error');
            }

            $progressBar->advance();
        }

        $progressBar->finish();
        $this->newLine(2);

        $this->table(
            ['Status', 'Count'],
            [
                ['✓ Successfully fetched from Graph', $success],
                ['⚠ Using fallback (not in Graph)', $cached],
                ['✗ Failed', $failed],
                ['Total', count($definitionIds)],
            ]
        );

        return self::SUCCESS;
    }

    private function warmForAllPolicies(SettingsCatalogDefinitionResolver $resolver): int
    {
        $policies = Policy::where(function ($query) {
            $query->where('policy_type', 'settingsCatalog')
                ->orWhere('policy_type', 'settingsCatalogPolicy');
        })->get();

        if ($policies->isEmpty()) {
            $this->warn('No Settings Catalog policies found');

            return self::SUCCESS;
        }

        $this->info("Found {$policies->count()} Settings Catalog policies");
        $this->newLine();

        foreach ($policies as $policy) {
            $this->warmForPolicy((string) $policy->id, $resolver);
            $this->newLine();
        }

        return self::SUCCESS;
    }

    private function extractDefinitionIds(array $snapshot): array
    {
        $ids = [];
        $settings = $snapshot['settings'] ?? [];

        $walk = function (array $nodes) use (&$walk, &$ids): void {
            foreach ($nodes as $node) {
                if (! is_array($node)) {
                    continue;
                }

                // Top-level settings have settingInstance wrapper
                if (isset($node['settingInstance']['settingDefinitionId'])) {
                    $ids[] = $node['settingInstance']['settingDefinitionId'];
                    $instance = $node['settingInstance'];
                }
                // Nested children have settingDefinitionId directly
                elseif (isset($node['settingDefinitionId'])) {
                    $ids[] = $node['settingDefinitionId'];
                    $instance = $node;
                } else {
                    continue;
                }

                // Handle nested children in choice setting value
                if (isset($instance['choiceSettingValue']['children']) && is_array($instance['choiceSettingValue']['children'])) {
                    $walk($instance['choiceSettingValue']['children']);
                }

                // Handle nested children in group collections
                if (isset($instance['groupSettingCollectionValue'])) {
                    foreach ($instance['groupSettingCollectionValue'] as $group) {
                        if (isset($group['children']) && is_array($group['children'])) {
                            $walk($group['children']);
                        }
                    }
                }

                // Handle nested children in group setting value
                if (isset($instance['groupSettingValue']['children']) && is_array($instance['groupSettingValue']['children'])) {
                    $walk($instance['groupSettingValue']['children']);
                }
            }
        };

        $walk($settings);

        return array_unique($ids);
    }
}
