<?php

declare(strict_types=1);

namespace App\Observers;

use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Intune\AuditLogger;

class ProviderCredentialObserver
{
    public function created(ProviderCredential $credential): void
    {
        $connection = $this->resolveConnection($credential);

        if (! $connection instanceof ProviderConnection) {
            return;
        }

        $tenant = $connection->tenant;

        if (! $tenant instanceof ManagedEnvironment) {
            return;
        }

        $this->audit(
            tenant: $tenant,
            connection: $connection,
            action: 'provider_connection.credentials_created',
            credential: $credential,
            changedFields: ['type', 'client_id', 'client_secret'],
        );
    }

    public function updated(ProviderCredential $credential): void
    {
        $connection = $this->resolveConnection($credential);

        if (! $connection instanceof ProviderConnection) {
            return;
        }

        $tenant = $connection->tenant;

        if (! $tenant instanceof ManagedEnvironment) {
            return;
        }

        $changedFields = $this->changedFields($credential);

        if ($changedFields === []) {
            return;
        }

        $action = in_array('client_secret', $changedFields, true)
            ? 'provider_connection.credentials_rotated'
            : 'provider_connection.credentials_updated';

        $this->audit(
            tenant: $tenant,
            connection: $connection,
            action: $action,
            credential: $credential,
            changedFields: $changedFields,
        );
    }

    public function deleted(ProviderCredential $credential): void
    {
        $connection = $this->resolveConnection($credential);

        if (! $connection instanceof ProviderConnection) {
            return;
        }

        $tenant = $connection->tenant;

        if (! $tenant instanceof ManagedEnvironment) {
            return;
        }

        $this->audit(
            tenant: $tenant,
            connection: $connection,
            action: 'provider_connection.credentials_deleted',
            credential: $credential,
            changedFields: ['client_id', 'client_secret'],
        );
    }

    private function resolveConnection(ProviderCredential $credential): ?ProviderConnection
    {
        $credential->loadMissing('providerConnection.tenant');

        return $credential->providerConnection;
    }

    /**
     * @return array<int, string>
     */
    private function changedFields(ProviderCredential $credential): array
    {
        $fields = [];

        if ($credential->isDirty('type') || $credential->wasChanged('type')) {
            $fields[] = 'type';
        }

        $previousPayload = $credential->getOriginal('payload');
        $currentPayload = $credential->payload;

        $previousPayload = is_array($previousPayload) ? $previousPayload : [];
        $currentPayload = is_array($currentPayload) ? $currentPayload : [];

        $previousClientId = trim((string) ($previousPayload['client_id'] ?? ''));
        $currentClientId = trim((string) ($currentPayload['client_id'] ?? ''));

        if ($previousClientId !== $currentClientId) {
            $fields[] = 'client_id';
        }

        $previousClientSecret = trim((string) ($previousPayload['client_secret'] ?? ''));
        $currentClientSecret = trim((string) ($currentPayload['client_secret'] ?? ''));

        if ($previousClientSecret !== $currentClientSecret) {
            $fields[] = 'client_secret';
        }

        return array_values(array_unique($fields));
    }

    /**
     * @param  array<int, string>  $changedFields
     */
    private function audit(
        ManagedEnvironment $tenant,
        ProviderConnection $connection,
        string $action,
        ProviderCredential $credential,
        array $changedFields,
    ): void {
        $user = auth()->user();
        $actorId = $user instanceof User ? (int) $user->getKey() : null;
        $actorEmail = $user instanceof User ? $user->email : null;
        $actorName = $user instanceof User ? $user->name : null;
        $source = $credential->source;
        $kind = $credential->credential_kind;
        $connectionType = $connection->connection_type;

        app(AuditLogger::class)->log(
            tenant: $tenant,
            action: $action,
            context: [
                'metadata' => [
                    'workspace_id' => (int) $connection->workspace_id,
                    'provider_connection_id' => (int) $connection->getKey(),
                    'provider' => (string) $connection->provider,
                    'entra_tenant_id' => (string) $connection->entra_tenant_id,
                    'connection_type' => is_string($connectionType)
                        ? $connectionType
                        : $connectionType?->value,
                    'credential_type' => (string) $credential->type,
                    'credential_kind' => is_string($kind) ? $kind : $kind?->value,
                    'credential_source' => is_string($source) ? $source : $source?->value,
                    'last_rotated_at' => $credential->last_rotated_at?->toJSON(),
                    'expires_at' => $credential->expires_at?->toJSON(),
                    'changed_fields' => $changedFields,
                    'redacted_fields' => ['client_secret'],
                ],
            ],
            actorId: $actorId,
            actorEmail: $actorEmail,
            actorName: $actorName,
            resourceType: 'provider_connection',
            resourceId: (string) $connection->getKey(),
            status: 'success',
        );
    }
}
