<?php

namespace App\Filament\Resources;

use App\Filament\Resources\ProviderConnectionResource\Pages;
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\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\OperationRunLinks;
use BackedEnum;
use Filament\Actions;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Gate;
use UnitEnum;

class ProviderConnectionResource extends Resource
{
    protected static bool $isScopedToTenant = false;

    protected static ?string $model = ProviderConnection::class;

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-link';

    protected static string|UnitEnum|null $navigationGroup = 'Providers';

    protected static ?string $navigationLabel = 'Connections';

    protected static ?string $recordTitleAttribute = 'display_name';

    public static function form(Schema $schema): Schema
    {
        return $schema
            ->schema([
                TextInput::make('display_name')
                    ->label('Display name')
                    ->required()
                    ->disabled(fn (): bool => ! Gate::allows('provider.manage', Tenant::current()))
                    ->maxLength(255),
                TextInput::make('entra_tenant_id')
                    ->label('Entra tenant ID')
                    ->required()
                    ->maxLength(255)
                    ->disabled(fn (): bool => ! Gate::allows('provider.manage', Tenant::current()))
                    ->rules(['uuid']),
                Toggle::make('is_default')
                    ->label('Default connection')
                    ->disabled(fn (): bool => ! Gate::allows('provider.manage', Tenant::current()))
                    ->helperText('Exactly one default connection is required per tenant/provider.'),
                TextInput::make('status')
                    ->label('Status')
                    ->disabled()
                    ->dehydrated(false),
                TextInput::make('health_status')
                    ->label('Health')
                    ->disabled()
                    ->dehydrated(false),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->modifyQueryUsing(function (Builder $query): Builder {
                $tenantId = Tenant::current()?->getKey();

                return $query->when($tenantId, fn (Builder $q) => $q->where('tenant_id', $tenantId));
            })
            ->defaultSort('display_name')
            ->columns([
                Tables\Columns\TextColumn::make('display_name')->label('Name')->searchable(),
                Tables\Columns\TextColumn::make('provider')->label('Provider')->toggleable(),
                Tables\Columns\TextColumn::make('entra_tenant_id')->label('Entra tenant ID')->copyable()->toggleable(),
                Tables\Columns\IconColumn::make('is_default')->label('Default')->boolean(),
                Tables\Columns\TextColumn::make('status')
                    ->label('Status')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::ProviderConnectionStatus))
                    ->color(BadgeRenderer::color(BadgeDomain::ProviderConnectionStatus))
                    ->icon(BadgeRenderer::icon(BadgeDomain::ProviderConnectionStatus))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::ProviderConnectionStatus)),
                Tables\Columns\TextColumn::make('health_status')
                    ->label('Health')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::ProviderConnectionHealth))
                    ->color(BadgeRenderer::color(BadgeDomain::ProviderConnectionHealth))
                    ->icon(BadgeRenderer::icon(BadgeDomain::ProviderConnectionHealth))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::ProviderConnectionHealth)),
                Tables\Columns\TextColumn::make('last_health_check_at')->label('Last check')->since()->toggleable(),
            ])
            ->filters([
                SelectFilter::make('status')
                    ->label('Status')
                    ->options([
                        'connected' => 'Connected',
                        'needs_consent' => 'Needs consent',
                        'error' => 'Error',
                        'disabled' => 'Disabled',
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        $value = $data['value'] ?? null;

                        if (! is_string($value) || $value === '') {
                            return $query;
                        }

                        return $query->where('status', $value);
                    }),
                SelectFilter::make('health_status')
                    ->label('Health')
                    ->options([
                        'ok' => 'OK',
                        'degraded' => 'Degraded',
                        'down' => 'Down',
                        'unknown' => 'Unknown',
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        $value = $data['value'] ?? null;

                        if (! is_string($value) || $value === '') {
                            return $query;
                        }

                        return $query->where('health_status', $value);
                    }),
            ])
            ->actions([
                Actions\ActionGroup::make([
                    Actions\EditAction::make(),

                    Actions\Action::make('check_connection')
                        ->label('Check connection')
                        ->icon('heroicon-o-check-badge')
                        ->color('success')
                        ->visible(fn (ProviderConnection $record): bool => Gate::allows('provider.run', Tenant::current())
                            && $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([
                                        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([
                                        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([
                                    Actions\Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();
                        }),

                    Actions\Action::make('inventory_sync')
                        ->label('Inventory sync')
                        ->icon('heroicon-o-arrow-path')
                        ->color('info')
                        ->visible(fn (ProviderConnection $record): bool => Gate::allows('provider.run', Tenant::current())
                            && $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([
                                        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([
                                        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([
                                    Actions\Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();
                        }),

                    Actions\Action::make('compliance_snapshot')
                        ->label('Compliance snapshot')
                        ->icon('heroicon-o-shield-check')
                        ->color('info')
                        ->visible(fn (ProviderConnection $record): bool => Gate::allows('provider.run', Tenant::current())
                            && $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([
                                        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([
                                        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([
                                    Actions\Action::make('view_run')
                                        ->label('View run')
                                        ->url(OperationRunLinks::view($result->run, $tenant)),
                                ])
                                ->send();
                        }),

                    Actions\Action::make('set_default')
                        ->label('Set as default')
                        ->icon('heroicon-o-star')
                        ->color('primary')
                        ->visible(fn (ProviderConnection $record): bool => Gate::allows('provider.manage', Tenant::current())
                            && $record->status !== 'disabled'
                            && ! $record->is_default)
                        ->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();
                        }),

                    Actions\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 => Gate::allows('provider.manage', Tenant::current()))
                        ->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();
                        }),

                    Actions\Action::make('enable_connection')
                        ->label('Enable connection')
                        ->icon('heroicon-o-play')
                        ->color('success')
                        ->visible(fn (ProviderConnection $record): bool => Gate::allows('provider.manage', Tenant::current())
                            && $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();
                        }),

                    Actions\Action::make('disable_connection')
                        ->label('Disable connection')
                        ->icon('heroicon-o-archive-box-x-mark')
                        ->color('danger')
                        ->requiresConfirmation()
                        ->visible(fn (ProviderConnection $record): bool => Gate::allows('provider.manage', Tenant::current())
                            && $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'),
            ])
            ->bulkActions([]);
    }

    public static function getEloquentQuery(): Builder
    {
        $tenantId = Tenant::current()?->getKey();

        return parent::getEloquentQuery()
            ->when($tenantId, fn (Builder $query) => $query->where('tenant_id', $tenantId))
            ->latest('id');
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListProviderConnections::route('/'),
            'create' => Pages\CreateProviderConnection::route('/create'),
            'edit' => Pages\EditProviderConnection::route('/{record}/edit'),
        ];
    }
}
