<?php

declare(strict_types=1);

namespace App\Support\Workspaces;

use App\Filament\Pages\ChooseTenant;
use App\Filament\Resources\AlertDeliveryResource;
use App\Models\AlertDelivery;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\Auth\WorkspaceRoleCapabilityMap;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\OperationCatalog;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;

final class WorkspaceOverviewBuilder
{
    public function __construct(
        private WorkspaceCapabilityResolver $workspaceCapabilityResolver,
    ) {}

    /**
     * @return array{
     *     workspace: array{id: int, name: string, slug: ?string},
     *     accessible_tenant_count: int,
     *     summary_metrics: list<array{
     *         key: string,
     *         label: string,
     *         value: int,
     *         description: string,
     *         destination_url: ?string,
     *         color: string
     *     }>,
     *     attention_items: list<array{
     *         title: string,
     *         body: string,
     *         url: string,
     *         badge: string,
     *         badge_color: string
     *     }>,
     *     attention_empty_state: array{
     *         title: string,
     *         body: string,
     *         action_label: string,
     *         action_url: string
     *     },
     *     recent_operations: list<array{
     *         id: int,
     *         title: string,
     *         tenant_label: ?string,
     *         status_label: string,
     *         status_color: string,
     *         outcome_label: string,
     *         outcome_color: string,
     *         started_at: string,
     *         url: string
     *     }>,
     *     recent_operations_empty_state: array{
     *         title: string,
     *         body: string,
     *         action_label: string,
     *         action_url: string
     *     },
     *     quick_actions: list<array{
     *         key: string,
     *         label: string,
     *         description: string,
     *         url: string,
     *         icon: string,
     *         color: string
     *     }>,
     *     zero_tenant_state: ?array{
     *         title: string,
     *         body: string,
     *         action_label: string,
     *         action_url: string
     *     }
     * }
     */
    public function build(Workspace $workspace, User $user): array
    {
        $accessibleTenants = $this->accessibleTenants($workspace, $user);
        $accessibleTenantIds = $accessibleTenants
            ->pluck('id')
            ->map(static fn (mixed $id): int => (int) $id)
            ->all();

        $canViewAlerts = $this->workspaceCapabilityResolver->can($user, $workspace, Capabilities::ALERTS_VIEW);

        $recentOperations = $this->recentOperations((int) $workspace->getKey(), $accessibleTenantIds);
        $attentionItems = $this->attentionItems((int) $workspace->getKey(), $accessibleTenantIds, $canViewAlerts);
        $quickActions = $this->quickActions($workspace, $accessibleTenants->count(), $canViewAlerts, $user);

        $zeroTenantState = null;

        if ($accessibleTenants->isEmpty()) {
            $fallbackAction = collect($quickActions)
                ->first(fn (array $action): bool => in_array($action['key'], ['manage_workspaces', 'switch_workspace'], true));

            $zeroTenantState = [
                'title' => 'No accessible tenants in this workspace',
                'body' => 'You can still review workspace-wide operations or switch to another workspace while tenant access is being set up.',
                'action_label' => is_array($fallbackAction) ? $fallbackAction['label'] : 'Switch workspace',
                'action_url' => is_array($fallbackAction) ? $fallbackAction['url'] : $this->switchWorkspaceUrl(),
            ];
        }

        return [
            'workspace' => [
                'id' => (int) $workspace->getKey(),
                'name' => (string) $workspace->name,
                'slug' => filled($workspace->slug) ? (string) $workspace->slug : null,
            ],
            'accessible_tenant_count' => $accessibleTenants->count(),
            'summary_metrics' => $this->summaryMetrics(
                workspaceId: (int) $workspace->getKey(),
                accessibleTenantCount: $accessibleTenants->count(),
                accessibleTenantIds: $accessibleTenantIds,
                canViewAlerts: $canViewAlerts,
                needsAttentionCount: count($attentionItems),
            ),
            'attention_items' => $attentionItems,
            'attention_empty_state' => [
                'title' => 'Nothing urgent in your current scope',
                'body' => 'Recent operations and alert deliveries look healthy right now.',
                'action_label' => $canViewAlerts ? 'Open alerts' : 'Open operations',
                'action_url' => $canViewAlerts ? '/admin/alerts' : route('admin.operations.index'),
            ],
            'recent_operations' => $recentOperations,
            'recent_operations_empty_state' => [
                'title' => 'No recent operations yet',
                'body' => 'Workspace-wide activity will show up here once syncs, evaluations, or restores start running.',
                'action_label' => 'Open operations',
                'action_url' => route('admin.operations.index'),
            ],
            'quick_actions' => $quickActions,
            'zero_tenant_state' => $zeroTenantState,
        ];
    }

    /**
     * @return Collection<int, Tenant>
     */
    private function accessibleTenants(Workspace $workspace, User $user): Collection
    {
        return Tenant::query()
            ->where('workspace_id', (int) $workspace->getKey())
            ->where('status', 'active')
            ->whereIn('id', $user->tenantMemberships()->select('tenant_id'))
            ->orderBy('name')
            ->get(['id', 'name', 'external_id', 'workspace_id']);
    }

    /**
     * @param  array<int, int>  $accessibleTenantIds
     * @return list<array{
     *     key: string,
     *     label: string,
     *     value: int,
     *     description: string,
     *     destination_url: ?string,
     *     color: string
     * }>
     */
    private function summaryMetrics(
        int $workspaceId,
        int $accessibleTenantCount,
        array $accessibleTenantIds,
        bool $canViewAlerts,
        int $needsAttentionCount,
    ): array {
        $activeOperationsCount = (int) $this->scopeToAuthorizedTenants(
            OperationRun::query(),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->whereIn('status', [
                OperationRunStatus::Queued->value,
                OperationRunStatus::Running->value,
            ])
            ->count();

        $metrics = [
            [
                'key' => 'accessible_tenants',
                'label' => 'Accessible tenants',
                'value' => $accessibleTenantCount,
                'description' => $accessibleTenantCount > 0
                    ? 'Tenant drill-down stays explicit from this workspace home.'
                    : 'No tenant memberships are available in this workspace yet.',
                'destination_url' => $accessibleTenantCount > 0 ? ChooseTenant::getUrl(panel: 'admin') : null,
                'color' => 'primary',
            ],
            [
                'key' => 'active_operations',
                'label' => 'Active operations',
                'value' => $activeOperationsCount,
                'description' => 'Workspace-wide runs that are still queued or in progress.',
                'destination_url' => route('admin.operations.index'),
                'color' => $activeOperationsCount > 0 ? 'warning' : 'gray',
            ],
        ];

        if ($canViewAlerts) {
            $failedAlertDeliveriesCount = (int) $this->scopeToAuthorizedTenants(
                AlertDelivery::query(),
                $workspaceId,
                $accessibleTenantIds,
            )
                ->where('created_at', '>=', now()->subDays(7))
                ->where('status', AlertDelivery::STATUS_FAILED)
                ->count();

            $metrics[] = [
                'key' => 'alerts',
                'label' => 'Alert failures (7d)',
                'value' => $failedAlertDeliveriesCount,
                'description' => 'Failed alert deliveries in the last 7 days.',
                'destination_url' => AlertDeliveryResource::getUrl(panel: 'admin'),
                'color' => $failedAlertDeliveriesCount > 0 ? 'danger' : 'gray',
            ];
        }

        $metrics[] = [
            'key' => 'needs_attention',
            'label' => 'Needs attention',
            'value' => $needsAttentionCount,
            'description' => 'Urgent workspace-safe items surfaced below.',
            'destination_url' => $needsAttentionCount > 0 ? route('admin.operations.index') : null,
            'color' => $needsAttentionCount > 0 ? 'warning' : 'gray',
        ];

        return $metrics;
    }

    /**
     * @param  array<int, int>  $accessibleTenantIds
     * @return list<array{
     *     title: string,
     *     body: string,
     *     url: string,
     *     badge: string,
     *     badge_color: string
     * }>
     */
    private function attentionItems(int $workspaceId, array $accessibleTenantIds, bool $canViewAlerts): array
    {
        $items = [];

        $latestFailedRun = $this->scopeToAuthorizedTenants(
            OperationRun::query()->with('tenant'),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->where('status', OperationRunStatus::Completed->value)
            ->whereIn('outcome', [
                OperationRunOutcome::Failed->value,
                OperationRunOutcome::PartiallySucceeded->value,
            ])
            ->latest('created_at')
            ->first();

        if ($latestFailedRun instanceof OperationRun) {
            $items[] = [
                'title' => OperationCatalog::label((string) $latestFailedRun->type).' needs review',
                'body' => 'Latest outcome: '.BadgeRenderer::spec(BadgeDomain::OperationRunOutcome, $latestFailedRun->outcome)->label.'.',
                'url' => route('admin.operations.view', ['run' => (int) $latestFailedRun->getKey()]),
                'badge' => 'Operations',
                'badge_color' => $latestFailedRun->outcome === OperationRunOutcome::Failed->value ? 'danger' : 'warning',
            ];
        }

        $activeRunsCount = (int) $this->scopeToAuthorizedTenants(
            OperationRun::query(),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->whereIn('status', [
                OperationRunStatus::Queued->value,
                OperationRunStatus::Running->value,
            ])
            ->count();

        if ($activeRunsCount > 0) {
            $items[] = [
                'title' => 'Operations are still running',
                'body' => $activeRunsCount.' workspace run(s) are active right now.',
                'url' => route('admin.operations.index'),
                'badge' => 'Operations',
                'badge_color' => 'warning',
            ];
        }

        if ($canViewAlerts) {
            $failedAlertDeliveriesCount = (int) $this->scopeToAuthorizedTenants(
                AlertDelivery::query(),
                $workspaceId,
                $accessibleTenantIds,
            )
                ->where('created_at', '>=', now()->subDays(7))
                ->where('status', AlertDelivery::STATUS_FAILED)
                ->count();

            if ($failedAlertDeliveriesCount > 0) {
                $items[] = [
                    'title' => 'Alert deliveries failed',
                    'body' => $failedAlertDeliveriesCount.' alert delivery attempt(s) failed in the last 7 days.',
                    'url' => AlertDeliveryResource::getUrl(panel: 'admin'),
                    'badge' => 'Alerts',
                    'badge_color' => 'danger',
                ];
            }
        }

        return array_slice($items, 0, 5);
    }

    /**
     * @param  array<int, int>  $accessibleTenantIds
     * @return list<array{
     *     id: int,
     *     title: string,
     *     tenant_label: ?string,
     *     status_label: string,
     *     status_color: string,
     *     outcome_label: string,
     *     outcome_color: string,
     *     started_at: string,
     *     url: string
     * }>
     */
    private function recentOperations(int $workspaceId, array $accessibleTenantIds): array
    {
        $statusSpec = BadgeRenderer::label(BadgeDomain::OperationRunStatus);
        $statusColorSpec = BadgeRenderer::color(BadgeDomain::OperationRunStatus);
        $outcomeSpec = BadgeRenderer::label(BadgeDomain::OperationRunOutcome);
        $outcomeColorSpec = BadgeRenderer::color(BadgeDomain::OperationRunOutcome);

        return $this->scopeToAuthorizedTenants(
            OperationRun::query()->with('tenant'),
            $workspaceId,
            $accessibleTenantIds,
        )
            ->latest('created_at')
            ->limit(5)
            ->get()
            ->map(function (OperationRun $run) use ($statusSpec, $statusColorSpec, $outcomeSpec, $outcomeColorSpec): array {
                return [
                    'id' => (int) $run->getKey(),
                    'title' => OperationCatalog::label((string) $run->type),
                    'tenant_label' => $run->tenant instanceof Tenant ? (string) $run->tenant->name : null,
                    'status_label' => $statusSpec($run->status),
                    'status_color' => $statusColorSpec($run->status),
                    'outcome_label' => $outcomeSpec($run->outcome),
                    'outcome_color' => $outcomeColorSpec($run->outcome),
                    'started_at' => $run->created_at?->diffForHumans() ?? 'just now',
                    'url' => route('admin.operations.view', ['run' => (int) $run->getKey()]),
                ];
            })
            ->all();
    }

    /**
     * @param  array<int, int>  $accessibleTenantIds
     */
    private function scopeToAuthorizedTenants(Builder $query, int $workspaceId, array $accessibleTenantIds): Builder
    {
        return $query
            ->where('workspace_id', $workspaceId)
            ->where(function (Builder $query) use ($accessibleTenantIds): void {
                $query->whereNull('tenant_id');

                if ($accessibleTenantIds !== []) {
                    $query->orWhereIn('tenant_id', $accessibleTenantIds);
                }
            });
    }

    /**
     * @return list<array{
     *     key: string,
     *     label: string,
     *     description: string,
     *     url: string,
     *     icon: string,
     *     color: string
     * }>
     */
    private function quickActions(Workspace $workspace, int $accessibleTenantCount, bool $canViewAlerts, User $user): array
    {
        $actions = [
            [
                'key' => 'choose_tenant',
                'label' => 'Choose tenant',
                'description' => 'Deliberately enter tenant context from this workspace.',
                'url' => ChooseTenant::getUrl(panel: 'admin'),
                'icon' => 'heroicon-o-building-office-2',
                'color' => 'primary',
                'visible' => $accessibleTenantCount > 0,
            ],
            [
                'key' => 'operations',
                'label' => 'Open operations',
                'description' => 'Review current and recent workspace-wide runs.',
                'url' => route('admin.operations.index'),
                'icon' => 'heroicon-o-queue-list',
                'color' => 'gray',
                'visible' => true,
            ],
            [
                'key' => 'alerts',
                'label' => 'Open alerts',
                'description' => 'Inspect alert overview, rules, and deliveries.',
                'url' => '/admin/alerts',
                'icon' => 'heroicon-o-bell-alert',
                'color' => 'gray',
                'visible' => $canViewAlerts,
            ],
            [
                'key' => 'switch_workspace',
                'label' => 'Switch workspace',
                'description' => 'Change the active workspace context.',
                'url' => $this->switchWorkspaceUrl(),
                'icon' => 'heroicon-o-arrows-right-left',
                'color' => 'gray',
                'visible' => true,
            ],
            [
                'key' => 'manage_workspaces',
                'label' => 'Manage workspaces',
                'description' => 'Open workspace management and memberships.',
                'url' => route('filament.admin.resources.workspaces.index'),
                'icon' => 'heroicon-o-squares-2x2',
                'color' => 'gray',
                'visible' => $this->canManageWorkspaces($workspace, $user),
            ],
        ];

        return collect($actions)
            ->filter(fn (array $action): bool => (bool) $action['visible'])
            ->map(function (array $action): array {
                unset($action['visible']);

                return $action;
            })
            ->values()
            ->all();
    }

    private function canManageWorkspaces(Workspace $workspace, User $user): bool
    {
        if ($this->workspaceCapabilityResolver->can($user, $workspace, Capabilities::WORKSPACE_MANAGE)) {
            return true;
        }

        $roles = WorkspaceRoleCapabilityMap::rolesWithCapability(Capabilities::WORKSPACE_MEMBERSHIP_MANAGE);

        return $user->workspaceMemberships()
            ->whereIn('role', $roles)
            ->exists();
    }

    private function switchWorkspaceUrl(): string
    {
        return route('filament.admin.pages.choose-workspace').'?choose=1';
    }
}
