<?php

declare(strict_types=1);

namespace App\Services\EnvironmentReviews;

use App\Models\ManagedEnvironment;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Services\Auth\CapabilityResolver;
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
use App\Support\Auth\Capabilities;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;

final class EnvironmentReviewRegisterService
{
    public function __construct(
        private readonly CapabilityResolver $capabilityResolver,
        private readonly ManagedEnvironmentAccessScopeResolver $managedEnvironmentAccessScopeResolver,
    ) {}

    /**
     * @return array<int, ManagedEnvironment>
     */
    public function authorizedTenants(User $user, Workspace $workspace): array
    {
        $query = ManagedEnvironment::query()
            ->where('managed_environments.workspace_id', (int) $workspace->getKey());

        $this->managedEnvironmentAccessScopeResolver->applyWorkspaceScopeToQuery(
            query: $query,
            user: $user,
            workspaceId: (int) $workspace->getKey(),
            qualifiedEnvironmentColumn: 'managed_environments.id',
        );

        $tenants = $query
            ->orderBy('managed_environments.name')
            ->get();

        $this->capabilityResolver->primeMemberships($user, $tenants->modelKeys());

        return $tenants
            ->filter(fn (ManagedEnvironment $tenant): bool => $this->capabilityResolver->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_VIEW))
            ->keyBy(static fn (ManagedEnvironment $tenant): int => (int) $tenant->getKey())
            ->all();
    }

    public function query(User $user, Workspace $workspace): Builder
    {
        $tenantIds = array_keys($this->authorizedTenants($user, $workspace));

        return EnvironmentReview::query()
            ->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
            ->forWorkspace((int) $workspace->getKey())
            ->whereIn('managed_environment_id', $tenantIds === [] ? [-1] : $tenantIds)
            ->latest('generated_at')
            ->latest('id');
    }

    public function latestPublishedQuery(User $user, Workspace $workspace): Builder
    {
        $tenantIds = array_keys($this->authorizedTenants($user, $workspace));

        $rankedReviews = EnvironmentReview::query()
            ->select([
                'environment_reviews.id',
                'environment_reviews.managed_environment_id',
                'environment_reviews.published_at',
                'environment_reviews.generated_at',
            ])
            ->selectRaw('ROW_NUMBER() OVER (PARTITION BY managed_environment_id ORDER BY published_at DESC, generated_at DESC, id DESC) as rn')
            ->forWorkspace((int) $workspace->getKey())
            ->whereIn('managed_environment_id', $tenantIds === [] ? [-1] : $tenantIds)
            ->published();

        $latestPublishedIds = DB::query()
            ->fromSub($rankedReviews, 'ranked_environment_reviews')
            ->where('rn', 1)
            ->select('id');

        return EnvironmentReview::query()
            ->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
            ->forWorkspace((int) $workspace->getKey())
            ->whereIn('environment_reviews.id', $latestPublishedIds)
            ->orderByDesc('published_at')
            ->orderByDesc('generated_at')
            ->orderByDesc('id');
    }

    public function customerWorkspaceTenantQuery(User $user, Workspace $workspace): Builder
    {
        $tenantIds = array_keys($this->authorizedTenants($user, $workspace));

        return ManagedEnvironment::query()
            ->where('workspace_id', (int) $workspace->getKey())
            ->whereIn('id', $tenantIds === [] ? [-1] : $tenantIds)
            ->whereHas('environmentReviews', fn ($query) => $query->published())
            ->with([
                'environmentReviews' => fn ($query) => $query
                    ->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
                    ->published()
                    ->orderByDesc('published_at')
                    ->orderByDesc('generated_at')
                    ->orderByDesc('id')
                    ->limit(1),
            ])
            ->orderBy('name');
    }

    public function canAccessWorkspace(User $user, Workspace $workspace): bool
    {
        return WorkspaceMembership::query()
            ->where('workspace_id', (int) $workspace->getKey())
            ->where('user_id', (int) $user->getKey())
            ->exists();
    }
}
