<?php

namespace App\Filament\Resources\ProviderConnectionResource\Pages;

use App\Filament\Resources\ProviderConnectionResource;
use App\Jobs\ProviderComplianceSnapshotJob;
use App\Jobs\ProviderConnectionHealthCheckJob;
use App\Jobs\ProviderInventorySyncJob;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Intune\AuditLogger;
use App\Services\Providers\CredentialManager;
use App\Services\Providers\ProviderOperationStartGate;
use App\Support\OperationRunLinks;
use Filament\Actions;
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Gate;

class EditProviderConnection extends EditRecord
{
    protected static string $resource = ProviderConnectionResource::class;

    protected bool $shouldMakeDefault = false;

    protected bool $defaultWasChanged = false;

    protected function mutateFormDataBeforeSave(array $data): array
    {
        $this->shouldMakeDefault = (bool) ($data['is_default'] ?? false);
        unset($data['is_default']);

        return $data;
    }

    protected function afterSave(): void
    {
        $tenant = Tenant::current();
        $record = $this->getRecord();

        $changedFields = array_values(array_diff(array_keys($record->getChanges()), ['updated_at']));

        if ($this->shouldMakeDefault && ! $record->is_default) {
            $record->makeDefault();
            $this->defaultWasChanged = true;
        }

        $hasDefault = $tenant->providerConnections()
            ->where('provider', $record->provider)
            ->where('is_default', true)
            ->exists();

        if (! $hasDefault) {
            $record->makeDefault();
            $this->defaultWasChanged = true;
        }

        $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;

        if ($changedFields !== []) {
            app(AuditLogger::class)->log(
                tenant: $tenant,
                action: 'provider_connection.updated',
                context: [
                    'metadata' => [
                        'provider' => $record->provider,
                        'entra_tenant_id' => $record->entra_tenant_id,
                        'fields' => $changedFields,
                    ],
                ],
                actorId: $actorId,
                actorEmail: $actorEmail,
                actorName: $actorName,
                resourceType: 'provider_connection',
                resourceId: (string) $record->getKey(),
                status: 'success',
            );
        }

        if ($this->defaultWasChanged) {
            app(AuditLogger::class)->log(
                tenant: $tenant,
                action: 'provider_connection.default_set',
                context: [
                    'metadata' => [
                        'provider' => $record->provider,
                        'entra_tenant_id' => $record->entra_tenant_id,
                    ],
                ],
                actorId: $actorId,
                actorEmail: $actorEmail,
                actorName: $actorName,
                resourceType: 'provider_connection',
                resourceId: (string) $record->getKey(),
                status: 'success',
            );
        }
    }

    protected function getHeaderActions(): array
    {
        $tenant = Tenant::current();

        return [
            Actions\DeleteAction::make()
                ->visible(false),

            Actions\ActionGroup::make([
                Action::make('view_last_check_run')
                    ->label('View last check run')
                    ->icon('heroicon-o-eye')
                    ->color('gray')
                    ->visible(fn (ProviderConnection $record): bool => $tenant instanceof Tenant
                        && Gate::allows('provider.view', $tenant)
                        && OperationRun::query()
                            ->where('tenant_id', $tenant->getKey())
                            ->where('type', 'provider.connection.check')
                            ->where('context->provider_connection_id', (int) $record->getKey())
                            ->exists())
                    ->url(function (ProviderConnection $record): ?string {
                        $tenant = Tenant::current();

                        if (! $tenant instanceof Tenant) {
                            return null;
                        }

                        $run = OperationRun::query()
                            ->where('tenant_id', $tenant->getKey())
                            ->where('type', 'provider.connection.check')
                            ->where('context->provider_connection_id', (int) $record->getKey())
                            ->orderByDesc('id')
                            ->first();

                        if (! $run instanceof OperationRun) {
                            return null;
                        }

                        return OperationRunLinks::view($run, $tenant);
                    }),

                Action::make('check_connection')
                    ->label('Check connection')
                    ->icon('heroicon-o-check-badge')
                    ->color('success')
                    ->visible(fn (ProviderConnection $record): bool => $tenant instanceof Tenant
                        && Gate::allows('provider.run', $tenant)
                        && $record->status !== 'disabled')
                    ->action(function (ProviderConnection $record, ProviderOperationStartGate $gate): void {
                        $tenant = Tenant::current();

                        abort_unless($tenant instanceof Tenant && Gate::allows('provider.run', $tenant), 403);

                        $user = auth()->user();
                        abort_unless($user instanceof User, 403);
                        $initiator = $user;

                        $result = $gate->start(
                            tenant: $tenant,
                            connection: $record,
                            operationType: 'provider.connection.check',
                            dispatcher: function (OperationRun $operationRun) use ($tenant, $initiator, $record): void {
                                ProviderConnectionHealthCheckJob::dispatch(
                                    tenantId: (int) $tenant->getKey(),
                                    userId: (int) $initiator->getKey(),
                                    providerConnectionId: (int) $record->getKey(),
                                    operationRun: $operationRun,
                                );
                            },
                            initiator: $initiator,
                        );

                        if ($result->status === 'scope_busy') {
                            Notification::make()
                                ->title('Scope busy')
                                ->body('Another provider operation is already running for this connection.')
                                ->warning()
                                ->actions([
                                    Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();

                            return;
                        }

                        if ($result->status === 'deduped') {
                            Notification::make()
                                ->title('Run already queued')
                                ->body('A connection check is already queued or running.')
                                ->warning()
                                ->actions([
                                    Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();

                            return;
                        }

                        Notification::make()
                            ->title('Connection check queued')
                            ->body('Health check was queued and will run in the background.')
                            ->success()
                            ->actions([
                                Action::make('view_run')
                                    ->label('View run')
                                    ->url(OperationRunLinks::view($result->run, $tenant)),
                            ])
                            ->send();
                    }),

                Action::make('update_credentials')
                    ->label('Update credentials')
                    ->icon('heroicon-o-key')
                    ->color('primary')
                    ->modalDescription('Client secret is stored encrypted and will never be shown again.')
                    ->visible(fn (): bool => $tenant instanceof Tenant && Gate::allows('provider.manage', $tenant))
                    ->form([
                        TextInput::make('client_id')
                            ->label('Client ID')
                            ->required()
                            ->maxLength(255),
                        TextInput::make('client_secret')
                            ->label('Client secret')
                            ->password()
                            ->required()
                            ->maxLength(255),
                    ])
                    ->action(function (array $data, ProviderConnection $record, CredentialManager $credentials, AuditLogger $auditLogger): void {
                        $tenant = Tenant::current();

                        abort_unless($tenant instanceof Tenant && Gate::allows('provider.manage', $tenant), 403);

                        $credentials->upsertClientSecretCredential(
                            connection: $record,
                            clientId: (string) $data['client_id'],
                            clientSecret: (string) $data['client_secret'],
                        );

                        $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;

                        $auditLogger->log(
                            tenant: $tenant,
                            action: 'provider_connection.credentials_updated',
                            context: [
                                'metadata' => [
                                    'provider' => $record->provider,
                                    'entra_tenant_id' => $record->entra_tenant_id,
                                ],
                            ],
                            actorId: $actorId,
                            actorEmail: $actorEmail,
                            actorName: $actorName,
                            resourceType: 'provider_connection',
                            resourceId: (string) $record->getKey(),
                            status: 'success',
                        );

                        Notification::make()
                            ->title('Credentials updated')
                            ->success()
                            ->send();
                    }),

                Action::make('set_default')
                    ->label('Set as default')
                    ->icon('heroicon-o-star')
                    ->color('primary')
                    ->visible(fn (ProviderConnection $record): bool => $tenant instanceof Tenant
                        && Gate::allows('provider.manage', $tenant)
                        && $record->status !== 'disabled'
                        && ! $record->is_default
                        && ProviderConnection::query()
                            ->where('tenant_id', $tenant->getKey())
                            ->where('provider', $record->provider)
                            ->count() > 1)
                    ->action(function (ProviderConnection $record, AuditLogger $auditLogger): void {
                        $tenant = Tenant::current();

                        abort_unless($tenant instanceof Tenant && Gate::allows('provider.manage', $tenant), 403);

                        $record->makeDefault();

                        $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;

                        $auditLogger->log(
                            tenant: $tenant,
                            action: 'provider_connection.default_set',
                            context: [
                                'metadata' => [
                                    'provider' => $record->provider,
                                    'entra_tenant_id' => $record->entra_tenant_id,
                                ],
                            ],
                            actorId: $actorId,
                            actorEmail: $actorEmail,
                            actorName: $actorName,
                            resourceType: 'provider_connection',
                            resourceId: (string) $record->getKey(),
                            status: 'success',
                        );

                        Notification::make()
                            ->title('Default connection updated')
                            ->success()
                            ->send();
                    }),

                Action::make('inventory_sync')
                    ->label('Inventory sync')
                    ->icon('heroicon-o-arrow-path')
                    ->color('info')
                    ->visible(fn (ProviderConnection $record): bool => $tenant instanceof Tenant
                        && Gate::allows('provider.run', $tenant)
                        && $record->status !== 'disabled')
                    ->action(function (ProviderConnection $record, ProviderOperationStartGate $gate): void {
                        $tenant = Tenant::current();

                        abort_unless($tenant instanceof Tenant && Gate::allows('provider.run', $tenant), 403);

                        $user = auth()->user();
                        abort_unless($user instanceof User, 403);
                        $initiator = $user;

                        $result = $gate->start(
                            tenant: $tenant,
                            connection: $record,
                            operationType: 'inventory.sync',
                            dispatcher: function (OperationRun $operationRun) use ($tenant, $initiator, $record): void {
                                ProviderInventorySyncJob::dispatch(
                                    tenantId: (int) $tenant->getKey(),
                                    userId: (int) $initiator->getKey(),
                                    providerConnectionId: (int) $record->getKey(),
                                    operationRun: $operationRun,
                                );
                            },
                            initiator: $initiator,
                        );

                        if ($result->status === 'scope_busy') {
                            Notification::make()
                                ->title('Scope is busy')
                                ->body('Another provider operation is already running for this connection.')
                                ->danger()
                                ->actions([
                                    Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();

                            return;
                        }

                        if ($result->status === 'deduped') {
                            Notification::make()
                                ->title('Run already queued')
                                ->body('An inventory sync is already queued or running.')
                                ->warning()
                                ->actions([
                                    Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();

                            return;
                        }

                        Notification::make()
                            ->title('Inventory sync queued')
                            ->body('Inventory sync was queued and will run in the background.')
                            ->success()
                            ->actions([
                                Action::make('view_run')
                                    ->label('View run')
                                    ->url(OperationRunLinks::view($result->run, $tenant)),
                            ])
                            ->send();
                    }),

                Action::make('compliance_snapshot')
                    ->label('Compliance snapshot')
                    ->icon('heroicon-o-shield-check')
                    ->color('info')
                    ->visible(fn (ProviderConnection $record): bool => $tenant instanceof Tenant
                        && Gate::allows('provider.run', $tenant)
                        && $record->status !== 'disabled')
                    ->action(function (ProviderConnection $record, ProviderOperationStartGate $gate): void {
                        $tenant = Tenant::current();

                        abort_unless($tenant instanceof Tenant && Gate::allows('provider.run', $tenant), 403);

                        $user = auth()->user();
                        abort_unless($user instanceof User, 403);
                        $initiator = $user;

                        $result = $gate->start(
                            tenant: $tenant,
                            connection: $record,
                            operationType: 'compliance.snapshot',
                            dispatcher: function (OperationRun $operationRun) use ($tenant, $initiator, $record): void {
                                ProviderComplianceSnapshotJob::dispatch(
                                    tenantId: (int) $tenant->getKey(),
                                    userId: (int) $initiator->getKey(),
                                    providerConnectionId: (int) $record->getKey(),
                                    operationRun: $operationRun,
                                );
                            },
                            initiator: $initiator,
                        );

                        if ($result->status === 'scope_busy') {
                            Notification::make()
                                ->title('Scope is busy')
                                ->body('Another provider operation is already running for this connection.')
                                ->danger()
                                ->actions([
                                    Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();

                            return;
                        }

                        if ($result->status === 'deduped') {
                            Notification::make()
                                ->title('Run already queued')
                                ->body('A compliance snapshot is already queued or running.')
                                ->warning()
                                ->actions([
                                    Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();

                            return;
                        }

                        Notification::make()
                            ->title('Compliance snapshot queued')
                            ->body('Compliance snapshot was queued and will run in the background.')
                            ->success()
                            ->actions([
                                Action::make('view_run')
                                    ->label('View run')
                                    ->url(OperationRunLinks::view($result->run, $tenant)),
                            ])
                            ->send();
                    }),

                Action::make('enable_connection')
                    ->label('Enable connection')
                    ->icon('heroicon-o-play')
                    ->color('success')
                    ->visible(fn (ProviderConnection $record): bool => $tenant instanceof Tenant
                        && Gate::allows('provider.manage', $tenant)
                        && $record->status === 'disabled')
                    ->action(function (ProviderConnection $record, AuditLogger $auditLogger): void {
                        $tenant = Tenant::current();

                        abort_unless($tenant instanceof Tenant && Gate::allows('provider.manage', $tenant), 403);

                        $hadCredentials = $record->credential()->exists();
                        $status = $hadCredentials ? 'connected' : 'needs_consent';
                        $previousStatus = (string) $record->status;

                        $record->update([
                            'status' => $status,
                            'health_status' => 'unknown',
                            'last_health_check_at' => null,
                            'last_error_reason_code' => null,
                            'last_error_message' => null,
                        ]);

                        $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;

                        $auditLogger->log(
                            tenant: $tenant,
                            action: 'provider_connection.enabled',
                            context: [
                                'metadata' => [
                                    'provider' => $record->provider,
                                    'entra_tenant_id' => $record->entra_tenant_id,
                                    'from_status' => $previousStatus,
                                    'to_status' => $status,
                                    'credentials_present' => $hadCredentials,
                                ],
                            ],
                            actorId: $actorId,
                            actorEmail: $actorEmail,
                            actorName: $actorName,
                            resourceType: 'provider_connection',
                            resourceId: (string) $record->getKey(),
                            status: 'success',
                        );

                        if (! $hadCredentials) {
                            Notification::make()
                                ->title('Connection enabled (credentials missing)')
                                ->body('Add credentials before running checks or operations.')
                                ->warning()
                                ->send();

                            return;
                        }

                        Notification::make()
                            ->title('Provider connection enabled')
                            ->success()
                            ->send();
                    }),

                Action::make('disable_connection')
                    ->label('Disable connection')
                    ->icon('heroicon-o-archive-box-x-mark')
                    ->color('danger')
                    ->requiresConfirmation()
                    ->visible(fn (ProviderConnection $record): bool => $tenant instanceof Tenant
                        && Gate::allows('provider.manage', $tenant)
                        && $record->status !== 'disabled')
                    ->action(function (ProviderConnection $record, AuditLogger $auditLogger): void {
                        $tenant = Tenant::current();

                        abort_unless($tenant instanceof Tenant && Gate::allows('provider.manage', $tenant), 403);

                        $previousStatus = (string) $record->status;

                        $record->update([
                            'status' => 'disabled',
                        ]);

                        $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;

                        $auditLogger->log(
                            tenant: $tenant,
                            action: 'provider_connection.disabled',
                            context: [
                                'metadata' => [
                                    'provider' => $record->provider,
                                    'entra_tenant_id' => $record->entra_tenant_id,
                                    'from_status' => $previousStatus,
                                ],
                            ],
                            actorId: $actorId,
                            actorEmail: $actorEmail,
                            actorName: $actorName,
                            resourceType: 'provider_connection',
                            resourceId: (string) $record->getKey(),
                            status: 'success',
                        );

                        Notification::make()
                            ->title('Provider connection disabled')
                            ->warning()
                            ->send();
                    }),
            ])
                ->label('Actions')
                ->icon('heroicon-o-ellipsis-vertical')
                ->color('gray'),
        ];
    }

    protected function getFormActions(): array
    {
        $tenant = Tenant::current();

        if ($tenant instanceof Tenant && Gate::allows('provider.manage', $tenant)) {
            return parent::getFormActions();
        }

        return [
            $this->getCancelFormAction(),
        ];
    }

    protected function handleRecordUpdate(Model $record, array $data): Model
    {
        $tenant = Tenant::current();

        abort_unless($tenant instanceof Tenant && Gate::allows('provider.manage', $tenant), 403);

        return parent::handleRecordUpdate($record, $data);
    }
}
