<?php

declare(strict_types=1);

namespace App\Filament\Resources\EnvironmentReviewResource\Pages;

use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\EnvironmentReview;
use App\Models\ManagedEnvironment;
use App\Models\ReviewPack;
use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\EnvironmentReviews\EnvironmentReviewLifecycleService;
use App\Services\EnvironmentReviews\EnvironmentReviewService;
use App\Services\ReviewPackService;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\Capabilities;
use App\Support\EnvironmentReviewStatus;
use App\Support\Rbac\UiEnforcement;
use App\Support\ReviewPackStatus;
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
use Filament\Actions;
use Filament\Forms\Components\Textarea;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ViewRecord;
use Illuminate\Database\Eloquent\Model;

class ViewEnvironmentReview extends ViewRecord
{
    protected static string $resource = EnvironmentReviewResource::class;

    public function mount(int|string $record): void
    {
        parent::mount($record);

        $this->auditCustomerWorkspaceOpen();
    }

    protected function resolveRecord(int|string $key): Model
    {
        return EnvironmentReviewResource::resolveScopedRecordOrFail($key);
    }

    protected function authorizeAccess(): void
    {
        $tenant = EnvironmentReviewResource::panelTenantContext();
        $record = $this->getRecord();
        $user = auth()->user();

        if (! $user instanceof User || ! $tenant instanceof ManagedEnvironment || ! $record instanceof EnvironmentReview) {
            abort(404);
        }

        if ((int) $record->managed_environment_id !== (int) $tenant->getKey()) {
            abort(404);
        }

        if (! $user->canAccessTenant($tenant)) {
            abort(404);
        }

        if (! $user->can('view', $record)) {
            abort(403);
        }
    }

    protected function getHeaderActions(): array
    {
        if ($this->isCustomerWorkspaceView()) {
            return [
                $this->downloadCurrentReviewPackAction(),
            ];
        }

        $secondaryActions = $this->secondaryLifecycleActions();

        return array_values(array_filter([
            $this->primaryLifecycleAction(),
            Actions\ActionGroup::make($secondaryActions)
                ->label('More')
                ->icon('heroicon-m-ellipsis-vertical')
                ->color('gray')
                ->visible(fn (): bool => $secondaryActions !== []),
            Actions\ActionGroup::make([
                $this->archiveReviewAction(),
            ])
                ->label('Danger')
                ->icon('heroicon-o-archive-box')
                ->color('danger')
                ->visible(fn (): bool => ! $this->isCustomerWorkspaceView() && ! $this->record->statusEnum()->isTerminal()),
        ]));
    }

    private function primaryLifecycleAction(): ?Actions\Action
    {
        return match ($this->primaryLifecycleActionName()) {
            'refresh_review' => $this->refreshReviewAction(),
            'publish_review' => $this->publishReviewAction(),
            'export_executive_pack' => $this->exportExecutivePackAction(),
            default => null,
        };
    }

    private function primaryLifecycleActionName(): ?string
    {
        if ($this->isCustomerWorkspaceView()) {
            return null;
        }

        if ((string) $this->record->status === EnvironmentReviewStatus::Published->value) {
            return 'export_executive_pack';
        }

        if ((string) $this->record->status === EnvironmentReviewStatus::Ready->value) {
            return 'publish_review';
        }

        if ($this->record->isMutable()) {
            return 'refresh_review';
        }

        return null;
    }

    /**
     * @return list<Actions\Action>
     */
    private function secondaryLifecycleActions(): array
    {
        return array_values(array_filter(array_map(
            fn (string $name): ?Actions\Action => match ($name) {
                'refresh_review' => $this->refreshReviewAction(),
                'publish_review' => $this->publishReviewAction(),
                'export_executive_pack' => $this->exportExecutivePackAction(),
                'create_next_review' => $this->createNextReviewAction(),
                default => null,
            },
            $this->secondaryLifecycleActionNames(),
        )));
    }

    /**
     * @return array<int, string>
     */
    private function secondaryLifecycleActionNames(): array
    {
        if ($this->isCustomerWorkspaceView()) {
            return [];
        }

        $names = [];

        if ($this->record->isMutable()) {
            $names[] = 'refresh_review';
            $names[] = 'publish_review';
        }

        if (in_array((string) $this->record->status, [
            EnvironmentReviewStatus::Ready->value,
            EnvironmentReviewStatus::Published->value,
        ], true)) {
            $names[] = 'export_executive_pack';
        }

        if ($this->record->isPublished()) {
            $names[] = 'create_next_review';
        }

        return array_values(array_filter(
            $names,
            fn (string $name): bool => $name !== $this->primaryLifecycleActionName(),
        ));
    }

    private function refreshReviewAction(): Actions\Action
    {
        $rule = GovernanceActionCatalog::rule('refresh_review');

        return UiEnforcement::forAction(
            Actions\Action::make('refresh_review')
                ->label($rule->canonicalLabel)
                ->icon('heroicon-o-arrow-path')
                ->color('primary')
                ->hidden(fn (): bool => ! $this->record->isMutable())
                ->requiresConfirmation()
                ->modalHeading($rule->modalHeading)
                ->modalDescription($rule->modalDescription)
                ->action(function () use ($rule): void {
                    $user = auth()->user();

                    if (! $user instanceof User) {
                        abort(403);
                    }

                    try {
                        app(EnvironmentReviewService::class)->refresh($this->record, $user);
                    } catch (\Throwable $throwable) {
                        Notification::make()->danger()->title('Unable to refresh review')->body($throwable->getMessage())->send();

                        return;
                    }

                    Notification::make()->success()->title($rule->successTitle)->send();
                }),
        )
            ->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
            ->apply();
    }

    private function publishReviewAction(): Actions\Action
    {
        $rule = GovernanceActionCatalog::rule('publish_review');

        return UiEnforcement::forAction(
            Actions\Action::make('publish_review')
                ->label($rule->canonicalLabel)
                ->icon('heroicon-o-check-badge')
                ->color('primary')
                ->hidden(fn (): bool => ! $this->record->isMutable())
                ->requiresConfirmation()
                ->modalHeading($rule->modalHeading)
                ->modalDescription($rule->modalDescription)
                ->form([
                    Textarea::make('publish_reason')
                        ->label('Publication reason')
                        ->rows(4)
                        ->required()
                        ->maxLength(2000),
                ])
                ->action(function (array $data) use ($rule): void {
                    $user = auth()->user();

                    if (! $user instanceof User) {
                        abort(403);
                    }

                    try {
                        app(EnvironmentReviewLifecycleService::class)->publish(
                            $this->record,
                            $user,
                            (string) ($data['publish_reason'] ?? ''),
                        );
                    } catch (\Throwable $throwable) {
                        Notification::make()->danger()->title('Unable to publish review')->body($throwable->getMessage())->send();

                        return;
                    }

                    $this->refreshFormData(['status', 'published_at', 'published_by_user_id', 'summary']);
                    Notification::make()->success()->title($rule->successTitle)->send();
                }),
        )
            ->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
            ->preserveVisibility()
            ->apply();
    }

    private function exportExecutivePackAction(): Actions\Action
    {
        $action = UiEnforcement::forAction(
            Actions\Action::make('export_executive_pack')
                ->label('Export executive pack')
                ->icon('heroicon-o-arrow-down-tray')
                ->color('primary')
                ->hidden(fn (): bool => ! in_array((string) $this->record->status, [
                    EnvironmentReviewStatus::Ready->value,
                    EnvironmentReviewStatus::Published->value,
                ], true))
                ->disabled(fn (): bool => EnvironmentReviewResource::reviewPackGenerationBlocked($this->record->tenant))
                ->action(fn (): mixed => EnvironmentReviewResource::executeExport($this->record)),
        )
            ->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
            ->preserveVisibility()
            ->preserveDisabled()
            ->apply();

        $action->tooltip(fn (): ?string => EnvironmentReviewResource::reviewPackGenerationActionTooltip($this->record->tenant));

        return $action;
    }

    private function createNextReviewAction(): Actions\Action
    {
        return UiEnforcement::forAction(
            Actions\Action::make('create_next_review')
                ->label('Create next review')
                ->icon('heroicon-o-document-duplicate')
                ->hidden(fn (): bool => ! $this->record->isPublished())
                ->action(function (): void {
                    $user = auth()->user();

                    if (! $user instanceof User) {
                        abort(403);
                    }

                    try {
                        $nextReview = app(EnvironmentReviewLifecycleService::class)->createNextReview($this->record, $user);
                    } catch (\Throwable $throwable) {
                        Notification::make()->danger()->title('Unable to create next review')->body($throwable->getMessage())->send();

                        return;
                    }

                    $this->redirect(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $nextReview], $nextReview->tenant));
                }),
        )
            ->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
            ->preserveVisibility()
            ->apply();
    }

    private function archiveReviewAction(): Actions\Action
    {
        $rule = GovernanceActionCatalog::rule('archive_review');

        return UiEnforcement::forAction(
            Actions\Action::make('archive_review')
                ->label($rule->canonicalLabel)
                ->icon('heroicon-o-archive-box')
                ->color('danger')
                ->hidden(fn (): bool => $this->record->statusEnum()->isTerminal())
                ->requiresConfirmation()
                ->modalHeading($rule->modalHeading)
                ->modalDescription($rule->modalDescription)
                ->form([
                    Textarea::make('archive_reason')
                        ->label('Archive reason')
                        ->rows(4)
                        ->required()
                        ->maxLength(2000),
                ])
                ->action(function (array $data) use ($rule): void {
                    $user = auth()->user();

                    if (! $user instanceof User) {
                        abort(403);
                    }

                    app(EnvironmentReviewLifecycleService::class)->archive(
                        $this->record,
                        $user,
                        (string) ($data['archive_reason'] ?? ''),
                    );
                    $this->refreshFormData(['status', 'archived_at']);

                    Notification::make()->success()->title($rule->successTitle)->send();
                }),
        )
            ->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
            ->preserveVisibility()
            ->apply();
    }

    private function downloadCurrentReviewPackAction(): Actions\Action
    {
        return Actions\Action::make('download_current_review_pack')
            ->label(__('localization.review.download_governance_package'))
            ->icon('heroicon-o-arrow-down-tray')
            ->color('primary')
            ->disabled(fn (): bool => $this->currentReviewPackDownloadUrl() === null)
            ->tooltip(fn (): ?string => $this->currentReviewPackUnavailableReason())
            ->url(fn (): ?string => $this->currentReviewPackDownloadUrl())
            ->openUrlInNewTab();
    }

    private function currentReviewPackDownloadUrl(): ?string
    {
        $pack = $this->record->currentExportReviewPack;
        $tenant = $this->record->tenant;
        $user = auth()->user();

        if (! $pack instanceof ReviewPack || ! $tenant instanceof ManagedEnvironment || ! $user instanceof User) {
            return null;
        }

        if (! $user->can(Capabilities::REVIEW_PACK_VIEW, $tenant)) {
            return null;
        }

        if ($pack->status !== ReviewPackStatus::Ready->value) {
            return null;
        }

        if ($pack->expires_at !== null && $pack->expires_at->isPast()) {
            return null;
        }

        return app(ReviewPackService::class)->generateDownloadUrl($pack, [
            'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
            'review_id' => (int) $this->record->getKey(),
            'tenant_filter_id' => request()->query('tenant_filter_id'),
            'interpretation_version' => $this->record->controlInterpretationVersion(),
        ]);
    }

    private function currentReviewPackUnavailableReason(): ?string
    {
        if ($this->currentReviewPackDownloadUrl() !== null) {
            return null;
        }

        $pack = $this->record->currentExportReviewPack;
        $tenant = $this->record->tenant;
        $user = auth()->user();

        if (! $pack instanceof ReviewPack) {
            return __('localization.review.customer_review_pack_missing');
        }

        if (! $user instanceof User || ! $tenant instanceof ManagedEnvironment || ! $user->can(Capabilities::REVIEW_PACK_VIEW, $tenant)) {
            return __('localization.review.customer_review_pack_forbidden');
        }

        if ($pack->status !== ReviewPackStatus::Ready->value) {
            return __('localization.review.customer_review_pack_not_ready');
        }

        if ($pack->expires_at !== null && $pack->expires_at->isPast()) {
            return __('localization.review.customer_review_pack_expired');
        }

        return __('localization.review.customer_review_pack_unavailable');
    }

    private function isCustomerWorkspaceView(): bool
    {
        return request()->boolean(CustomerReviewWorkspace::DETAIL_CONTEXT_QUERY_KEY);
    }

    private function auditCustomerWorkspaceOpen(): void
    {
        if (! $this->isCustomerWorkspaceView()) {
            return;
        }

        $user = auth()->user();
        $tenant = $this->record->tenant;

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

        app(WorkspaceAuditLogger::class)->log(
            workspace: $tenant->workspace,
            action: AuditActionId::EnvironmentReviewOpened,
            context: [
                'metadata' => [
                    'review_id' => (int) $this->record->getKey(),
                    'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
                    'tenant_filter_id' => request()->query('tenant_filter_id'),
                    'interpretation_version' => $this->record->controlInterpretationVersion(),
                ],
            ],
            actor: $user,
            resourceType: 'environment_review',
            resourceId: (string) $this->record->getKey(),
            targetLabel: sprintf('ManagedEnvironment review #%d', (int) $this->record->getKey()),
            tenant: $tenant,
        );
    }
}
