<?php

namespace App\Console\Commands;

use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\ManagedEnvironment;
use App\Services\Graph\GraphClientInterface;
use Illuminate\Console\Command;

class ReclassifyEnrollmentConfigurations extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'intune:reclassify-enrollment-configurations {--tenant=} {--write : Write changes (default is dry-run)}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Reclassify enrollment configuration items (e.g. ESP) that were synced under the wrong policy type.';

    /**
     * Execute the console command.
     */
    public function __construct(private readonly GraphClientInterface $graphClient)
    {
        parent::__construct();
    }

    public function handle(): int
    {
        $tenant = $this->resolveTenantOrNull();
        $dryRun = ! (bool) $this->option('write');

        $query = Policy::query()
            ->with(['tenant'])
            ->active()
            ->where('policy_type', 'enrollmentRestriction');

        if ($tenant) {
            $query->where('managed_environment_id', $tenant->id);
        }

        $candidates = $query->get();

        $changedVersions = 0;
        $changedPolicies = 0;
        $providerMissingPolicies = 0;

        foreach ($candidates as $policy) {
            $latestVersion = $policy->versions()->latest('version_number')->first();
            $snapshot = $latestVersion?->snapshot;

            if (! is_array($snapshot)) {
                $snapshot = $this->fetchSnapshotOrNull($policy);
            }

            if (! is_array($snapshot)) {
                continue;
            }

            if (! $this->isEspSnapshot($snapshot)) {
                continue;
            }

            $this->line(sprintf(
                'ESP detected: policy=%s managed_environment_id=%s external_id=%s',
                (string) $policy->getKey(),
                (string) $policy->managed_environment_id,
                (string) $policy->external_id,
            ));

            if ($dryRun) {
                continue;
            }

            $existingTarget = Policy::query()
                ->where('managed_environment_id', $policy->managed_environment_id)
                ->where('external_id', $policy->external_id)
                ->where('policy_type', 'windowsEnrollmentStatusPage')
                ->first();

            if ($existingTarget) {
                $policy->forceFill(['missing_from_provider_at' => now()])->save();
                $providerMissingPolicies++;

                continue;
            }

            $policy->forceFill([
                'policy_type' => 'windowsEnrollmentStatusPage',
                'missing_from_provider_at' => null,
            ])->save();
            $changedPolicies++;

            $changedVersions += PolicyVersion::query()
                ->where('policy_id', $policy->id)
                ->where('policy_type', 'enrollmentRestriction')
                ->update(['policy_type' => 'windowsEnrollmentStatusPage']);
        }

        $this->info('Done.');
        $this->info('PolicyVersions changed: '.$changedVersions);
        $this->info('Policies changed: '.$changedPolicies);
        $this->info('Policies marked provider-missing: '.$providerMissingPolicies);
        $this->info('Mode: '.($dryRun ? 'dry-run' : 'write'));

        return Command::SUCCESS;
    }

    private function isEspSnapshot(array $snapshot): bool
    {
        $odataType = $snapshot['@odata.type'] ?? null;
        $configurationType = $snapshot['deviceEnrollmentConfigurationType'] ?? null;

        return (is_string($odataType) && strcasecmp($odataType, '#microsoft.graph.windows10EnrollmentCompletionPageConfiguration') === 0)
            || (is_string($configurationType) && $configurationType === 'windows10EnrollmentCompletionPageConfiguration');
    }

    private function fetchSnapshotOrNull(Policy $policy): ?array
    {
        $tenant = $policy->tenant;

        if (! $tenant) {
            return null;
        }

        $tenantIdentifier = $tenant->managed_environment_id ?? $tenant->external_id;

        $response = $this->graphClient->getPolicy('enrollmentRestriction', $policy->external_id, [
            'tenant' => $tenantIdentifier,
            'client_id' => $tenant->app_client_id,
            'client_secret' => $tenant->app_client_secret,
            'platform' => $policy->platform,
        ]);

        if ($response->failed()) {
            return null;
        }

        $payload = $response->data['payload'] ?? null;

        return is_array($payload) ? $payload : null;
    }

    private function resolveTenantOrNull(): ?ManagedEnvironment
    {
        $tenantOption = $this->option('tenant');

        if (! $tenantOption) {
            return null;
        }

        return ManagedEnvironment::query()
            ->forTenant($tenantOption)
            ->firstOrFail();
    }
}
