<?php

declare(strict_types=1);

namespace Tests\Feature\Concerns;

use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineSnapshotItem;
use App\Models\BaselineTenantAssignment;
use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Support\Baselines\BaselineCaptureMode;
use App\Support\Baselines\BaselineProfileStatus;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Tests\Feature\Baselines\Support\BaselineSubjectResolutionFixtures;

trait BuildsBaselineCompareMatrixFixtures
{
    /**
     * @return array{
     *     user: User,
     *     workspace: Workspace,
     *     profile: BaselineProfile,
     *     snapshot: BaselineSnapshot,
     *     visibleTenant: ManagedEnvironment,
     *     visibleTenantTwo: ManagedEnvironment,
     *     hiddenTenant: ManagedEnvironment,
     *     subjects: array<string, BaselineSnapshotItem>
     * }
     */
    protected function makeBaselineCompareMatrixFixture(
        string $viewerRole = 'owner',
        ?string $workspaceRole = null,
    ): array {
        [$user, $visibleTenant] = createMinimalUserWithTenant(role: $viewerRole, workspaceRole: $workspaceRole ?? $viewerRole);

        $workspace = Workspace::query()->findOrFail((int) $visibleTenant->workspace_id);

        $profile = BaselineProfile::factory()->create([
            'workspace_id' => (int) $workspace->getKey(),
            'status' => BaselineProfileStatus::Active->value,
            'capture_mode' => BaselineCaptureMode::Opportunistic->value,
            'name' => 'Visible-set baseline',
            'scope_jsonb' => [
                'policy_types' => ['deviceConfiguration', 'compliancePolicy'],
                'foundation_types' => [],
            ],
        ]);

        $snapshot = BaselineSnapshot::factory()->complete()->create([
            'workspace_id' => (int) $workspace->getKey(),
            'baseline_profile_id' => (int) $profile->getKey(),
            'captured_at' => now()->subHours(2),
            'completed_at' => now()->subHours(2),
        ]);

        $profile->forceFill([
            'active_snapshot_id' => (int) $snapshot->getKey(),
        ])->save();

        $visibleTenantTwo = ManagedEnvironment::factory()->create([
            'workspace_id' => (int) $workspace->getKey(),
            'name' => 'Northwind',
        ]);

        $hiddenTenant = ManagedEnvironment::factory()->create([
            'workspace_id' => (int) $workspace->getKey(),
            'name' => 'Hidden Fabrikam',
        ]);

        $user->tenants()->syncWithoutDetaching([
            (int) $visibleTenantTwo->getKey() => ['role' => 'owner'],
        ]);

        WorkspaceMembership::query()->updateOrCreate([
            'workspace_id' => (int) $workspace->getKey(),
            'user_id' => (int) $user->getKey(),
        ], [
            'role' => $workspaceRole ?? $viewerRole,
        ]);

        $this->assignTenantToBaselineProfile($profile, $visibleTenant);
        $this->assignTenantToBaselineProfile($profile, $visibleTenantTwo);
        $this->assignTenantToBaselineProfile($profile, $hiddenTenant);

        $subjects = [
            'wifi-corp-profile' => $this->makeBaselineCompareMatrixSubject(
                $snapshot,
                'deviceConfiguration',
                'wifi-corp-profile',
                'WiFi Corp Profile',
                'dc:wifi-corp-profile',
            ),
            'windows-compliance' => $this->makeBaselineCompareMatrixSubject(
                $snapshot,
                'compliancePolicy',
                'windows-compliance',
                'Windows Compliance',
                'cp:windows-compliance',
            ),
        ];

        return [
            'user' => $user,
            'workspace' => $workspace,
            'profile' => $profile,
            'snapshot' => $snapshot,
            'visibleTenant' => $visibleTenant,
            'visibleTenantTwo' => $visibleTenantTwo,
            'hiddenTenant' => $hiddenTenant,
            'subjects' => $subjects,
        ];
    }

    protected function makeBaselineCompareMatrixSubject(
        BaselineSnapshot $snapshot,
        string $policyType,
        string $subjectKey,
        string $displayName,
        ?string $subjectExternalId = null,
    ): BaselineSnapshotItem {
        return BaselineSnapshotItem::factory()->create([
            'baseline_snapshot_id' => (int) $snapshot->getKey(),
            'policy_type' => $policyType,
            'subject_key' => $subjectKey,
            'subject_external_id' => $subjectExternalId ?? $policyType.':'.$subjectKey,
            'meta_jsonb' => ['display_name' => $displayName],
        ]);
    }

    protected function assignTenantToBaselineProfile(BaselineProfile $profile, ManagedEnvironment $tenant): BaselineTenantAssignment
    {
        return BaselineTenantAssignment::factory()->create([
            'workspace_id' => (int) $profile->workspace_id,
            'baseline_profile_id' => (int) $profile->getKey(),
            'managed_environment_id' => (int) $tenant->getKey(),
        ]);
    }

    /**
     * @param  array<string, mixed>  $contextOverrides
     * @param  array<string, mixed>  $attributes
     */
    protected function makeBaselineCompareMatrixRun(
        ManagedEnvironment $tenant,
        BaselineProfile $profile,
        BaselineSnapshot $snapshot,
        array $contextOverrides = [],
        array $attributes = [],
    ): OperationRun {
        $defaults = [
            'managed_environment_id' => (int) $tenant->getKey(),
            'workspace_id' => (int) $tenant->workspace_id,
            'type' => OperationRunType::BaselineCompare->value,
            'status' => OperationRunStatus::Completed->value,
            'outcome' => OperationRunOutcome::Succeeded->value,
            'initiator_name' => 'Spec190 Matrix',
            'summary_counts' => [
                'matched_items' => 1,
                'different_items' => 0,
                'missing_items' => 0,
                'unexpected_items' => 0,
            ],
            'context' => array_replace_recursive([
                'baseline_profile_id' => (int) $profile->getKey(),
                'baseline_snapshot_id' => (int) $snapshot->getKey(),
                'baseline_compare' => [
                    'reason_code' => null,
                    'subjects_total' => 2,
                    'fidelity' => 'content',
                    'coverage' => [
                        'proof' => true,
                        'effective_types' => ['deviceConfiguration', 'compliancePolicy'],
                        'covered_types' => ['deviceConfiguration', 'compliancePolicy'],
                        'uncovered_types' => [],
                    ],
                    'evidence_gaps' => [
                        'count' => 0,
                        'by_reason' => [],
                        'subjects' => [],
                    ],
                ],
            ], $contextOverrides),
            'started_at' => now()->subMinutes(5),
            'completed_at' => now()->subMinute(),
        ];

        return OperationRun::factory()->create(array_replace_recursive($defaults, $attributes));
    }

    /**
     * @param  array<string, mixed>  $overrides
     */
    protected function makeBaselineCompareMatrixFinding(
        ManagedEnvironment $tenant,
        BaselineProfile $profile,
        OperationRun $run,
        string $subjectKey,
        array $overrides = [],
    ): Finding {
        $defaults = [
            'managed_environment_id' => (int) $tenant->getKey(),
            'workspace_id' => (int) $tenant->workspace_id,
            'finding_type' => Finding::FINDING_TYPE_DRIFT,
            'source' => 'baseline.compare',
            'scope_key' => 'baseline_profile:'.(int) $profile->getKey(),
            'baseline_operation_run_id' => (int) $run->getKey(),
            'current_operation_run_id' => (int) $run->getKey(),
            'subject_type' => 'policy',
            'subject_external_id' => 'subject:'.$subjectKey,
            'severity' => Finding::SEVERITY_HIGH,
            'status' => Finding::STATUS_NEW,
            'evidence_jsonb' => [
                'subject_key' => $subjectKey,
                'change_type' => 'different_version',
            ],
        ];

        return Finding::factory()->create(array_replace_recursive($defaults, $overrides));
    }

    /**
     * @param  array<string, mixed>  $overrides
     * @return array<string, mixed>
     */
    protected function baselineCompareMatrixGap(string $policyType, string $subjectKey, array $overrides = []): array
    {
        return BaselineSubjectResolutionFixtures::structuredGap(array_replace([
            'policy_type' => $policyType,
            'subject_key' => $subjectKey,
        ], $overrides));
    }

    protected function setAdminWorkspaceContext(User $user, Workspace|int $workspace, ?ManagedEnvironment $rememberedEnvironment = null): array
    {
        $workspaceId = $workspace instanceof Workspace ? (int) $workspace->getKey() : (int) $workspace;

        $session = [
            WorkspaceContext::SESSION_KEY => $workspaceId,
        ];

        if ($rememberedEnvironment instanceof ManagedEnvironment) {
            $session[WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY] = [
                (string) $workspaceId => (int) $rememberedEnvironment->getKey(),
            ];
        }

        $this->actingAs($user)->withSession($session);
        session()->put(WorkspaceContext::SESSION_KEY, $workspaceId);

        if ($rememberedEnvironment instanceof ManagedEnvironment) {
            session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
                (string) $workspaceId => (int) $rememberedEnvironment->getKey(),
            ]);
        }

        Filament::setCurrentPanel('admin');
        Filament::setTenant(null, true);
        Filament::bootCurrentPanel();

        return $session;
    }
}
