<?php

namespace App\Services\Providers;

use App\Support\Auth\Capabilities;
use InvalidArgumentException;

final class ProviderOperationRegistry
{
    public const string BINDING_ACTIVE = 'active';

    public const string BINDING_UNSUPPORTED = 'unsupported';

    /**
     * @return array<string, array{operation_type: string, module: string, label: string, required_capability: string, provider_capability_keys: array<int, string>}>
     */
    public function definitions(): array
    {
        return [
            'provider.connection.check' => [
                'operation_type' => 'provider.connection.check',
                'module' => 'health_check',
                'label' => 'Provider connection check',
                'required_capability' => Capabilities::PROVIDER_RUN,
                'provider_capability_keys' => ['provider_connection_check'],
            ],
            'inventory.sync' => [
                'operation_type' => 'inventory.sync',
                'module' => 'inventory',
                'label' => 'Inventory sync',
                'required_capability' => Capabilities::PROVIDER_RUN,
                'provider_capability_keys' => ['inventory_read'],
            ],
            'compliance.snapshot' => [
                'operation_type' => 'compliance.snapshot',
                'module' => 'compliance',
                'label' => 'Compliance snapshot',
                'required_capability' => Capabilities::PROVIDER_RUN,
                'provider_capability_keys' => ['configuration_read'],
            ],
            'restore.execute' => [
                'operation_type' => 'restore.execute',
                'module' => 'restore',
                'label' => 'Restore execution',
                'required_capability' => Capabilities::TENANT_MANAGE,
                'provider_capability_keys' => ['restore_execute'],
            ],
            'directory.groups.sync' => [
                'operation_type' => 'directory.groups.sync',
                'module' => 'directory_groups',
                'label' => 'Directory groups sync',
                'required_capability' => Capabilities::TENANT_SYNC,
                'provider_capability_keys' => ['directory_groups_read'],
            ],
            'directory.role_definitions.sync' => [
                'operation_type' => 'directory.role_definitions.sync',
                'module' => 'directory_role_definitions',
                'label' => 'Role definitions sync',
                'required_capability' => Capabilities::TENANT_MANAGE,
                'provider_capability_keys' => ['directory_role_definitions_read'],
            ],
        ];
    }

    /**
     * @return array<string, array{operation_type: string, module: string, label: string, required_capability: string, provider_capability_keys: array<int, string>}>
     */
    public function all(): array
    {
        return $this->definitions();
    }

    /**
     * @return array<string, array<string, array{operation_type: string, provider: string, binding_status: string, handler_notes: string, exception_notes: string}>>
     */
    public function providerBindings(): array
    {
        return [
            'provider.connection.check' => [
                'microsoft' => $this->activeMicrosoftBinding(
                    operationType: 'provider.connection.check',
                    handlerNotes: 'Uses the current Microsoft Graph provider connection health-check workflow.',
                    exceptionNotes: 'Current-release provider binding remains Microsoft-only until a real second provider case exists.',
                ),
            ],
            'inventory.sync' => [
                'microsoft' => $this->activeMicrosoftBinding(
                    operationType: 'inventory.sync',
                    handlerNotes: 'Uses the current Microsoft Intune inventory sync workflow.',
                    exceptionNotes: 'Inventory collection is currently Microsoft Intune-specific provider behavior.',
                ),
            ],
            'compliance.snapshot' => [
                'microsoft' => $this->activeMicrosoftBinding(
                    operationType: 'compliance.snapshot',
                    handlerNotes: 'Uses the current Microsoft compliance snapshot workflow.',
                    exceptionNotes: 'Compliance snapshot runtime remains bounded to the Microsoft provider.',
                ),
            ],
            'restore.execute' => [
                'microsoft' => $this->activeMicrosoftBinding(
                    operationType: 'restore.execute',
                    handlerNotes: 'Uses the current Microsoft restore execution workflow.',
                    exceptionNotes: 'Restore execution remains Microsoft-only and must preserve dry-run and audit safeguards.',
                ),
            ],
            'directory.groups.sync' => [
                'microsoft' => $this->activeMicrosoftBinding(
                    operationType: 'directory.groups.sync',
                    handlerNotes: 'Uses the current Microsoft Entra group synchronization workflow.',
                    exceptionNotes: 'The operation type keeps current Entra vocabulary until the identity-neutrality follow-up.',
                ),
            ],
            'directory.role_definitions.sync' => [
                'microsoft' => $this->activeMicrosoftBinding(
                    operationType: 'directory.role_definitions.sync',
                    handlerNotes: 'Uses the current Microsoft directory role definition synchronization workflow.',
                    exceptionNotes: 'Directory role definitions are Microsoft-owned provider semantics.',
                ),
            ],
        ];
    }

    public function isAllowed(string $operationType): bool
    {
        return array_key_exists(trim($operationType), $this->definitions());
    }

    /**
     * @return array{operation_type: string, module: string, label: string, required_capability: string, provider_capability_keys: array<int, string>}
     */
    public function get(string $operationType): array
    {
        $operationType = trim($operationType);

        $definition = $this->definitions()[$operationType] ?? null;

        if (! is_array($definition)) {
            throw new InvalidArgumentException("Unknown provider operation type: {$operationType}");
        }

        return $definition;
    }

    /**
     * @return array<int, string>
     */
    public function providerCapabilityKeysForOperation(string $operationType): array
    {
        $definition = $this->get($operationType);
        $keys = $definition['provider_capability_keys'] ?? [];

        return array_values(array_filter(
            array_map('strval', is_array($keys) ? $keys : []),
            static fn (string $key): bool => trim($key) !== '',
        ));
    }

    /**
     * @return array{operation_type: string, provider: string, binding_status: string, handler_notes: string, exception_notes: string}|null
     */
    public function bindingFor(string $operationType, string $provider): ?array
    {
        $operationType = trim($operationType);
        $provider = trim($provider);

        if ($operationType === '' || $provider === '') {
            return null;
        }

        $bindings = $this->providerBindings()[$operationType] ?? [];

        return $bindings[$provider] ?? null;
    }

    /**
     * @return array{operation_type: string, provider: string, binding_status: string, handler_notes: string, exception_notes: string}|null
     */
    public function activeBindingFor(string $operationType): ?array
    {
        $operationType = trim($operationType);
        $bindings = $this->providerBindings()[$operationType] ?? [];

        foreach ($bindings as $binding) {
            if (($binding['binding_status'] ?? null) === self::BINDING_ACTIVE) {
                return $binding;
            }
        }

        return null;
    }

    /**
     * @return array{
     *     definition: array{operation_type: string, module: string, label: string, required_capability: string, provider_capability_keys: array<int, string>},
     *     binding: array{operation_type: string, provider: string, binding_status: string, handler_notes: string, exception_notes: string}
     * }
     */
    public function boundaryOperation(string $operationType, ?string $provider = null): array
    {
        $definition = $this->get($operationType);
        $binding = is_string($provider) && trim($provider) !== ''
            ? $this->bindingFor($operationType, $provider)
            : $this->activeBindingFor($operationType);

        return [
            'definition' => $definition,
            'binding' => $binding ?? $this->unsupportedBinding($operationType, $provider ?? 'unknown'),
        ];
    }

    /**
     * @return array{operation_type: string, provider: string, binding_status: string, handler_notes: string, exception_notes: string}
     */
    public function unsupportedBinding(string $operationType, string $provider): array
    {
        return [
            'operation_type' => trim($operationType),
            'provider' => trim($provider) !== '' ? trim($provider) : 'unknown',
            'binding_status' => self::BINDING_UNSUPPORTED,
            'handler_notes' => 'No explicit provider binding exists for this operation/provider combination.',
            'exception_notes' => 'Unsupported combinations must block explicitly instead of inheriting Microsoft behavior.',
        ];
    }

    /**
     * @return array{operation_type: string, provider: string, binding_status: string, handler_notes: string, exception_notes: string}
     */
    private function activeMicrosoftBinding(string $operationType, string $handlerNotes, string $exceptionNotes): array
    {
        return [
            'operation_type' => $operationType,
            'provider' => 'microsoft',
            'binding_status' => self::BINDING_ACTIVE,
            'handler_notes' => $handlerNotes,
            'exception_notes' => $exceptionNotes,
        ];
    }
}
