<?php

declare(strict_types=1);

namespace App\Services\Baselines;

use App\Models\Finding;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Models\Workspace;
use App\Services\Findings\FindingWorkflowService;
use App\Services\Settings\SettingsResolver;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use Carbon\CarbonImmutable;

final class BaselineAutoCloseService
{
    public function __construct(
        private readonly SettingsResolver $settingsResolver,
        private readonly ?FindingWorkflowService $findingWorkflowService = null,
    ) {}

    public function shouldAutoClose(ManagedEnvironment $tenant, OperationRun $run): bool
    {
        if ($run->status !== OperationRunStatus::Completed->value) {
            return false;
        }

        if ($run->outcome !== OperationRunOutcome::Succeeded->value) {
            return false;
        }

        $summaryCounts = is_array($run->summary_counts) ? $run->summary_counts : [];

        if (
            ! array_key_exists('total', $summaryCounts)
            || ! array_key_exists('processed', $summaryCounts)
            || ! array_key_exists('failed', $summaryCounts)
        ) {
            return false;
        }

        $total = (int) $summaryCounts['total'];
        $processed = (int) $summaryCounts['processed'];
        $failed = (int) $summaryCounts['failed'];

        if ($processed !== $total || $failed !== 0) {
            return false;
        }

        $workspace = $this->resolveWorkspace($tenant);

        if (! $workspace instanceof Workspace) {
            return false;
        }

        try {
            return (bool) $this->settingsResolver->resolveValue(
                workspace: $workspace,
                domain: 'baseline',
                key: 'auto_close_enabled',
            );
        } catch (\InvalidArgumentException) {
            return false;
        }
    }

    /**
     * @param  array<int, string>  $seenFingerprints
     */
    public function resolveStaleFindings(
        ManagedEnvironment $tenant,
        int $baselineProfileId,
        array $seenFingerprints,
        int $currentOperationRunId,
    ): int {
        $scopeKey = 'baseline_profile:'.$baselineProfileId;
        $resolvedAt = CarbonImmutable::now();
        $resolvedCount = 0;

        $query = Finding::query()
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->where('finding_type', Finding::FINDING_TYPE_DRIFT)
            ->where('source', 'baseline.compare')
            ->where('scope_key', $scopeKey)
            ->whereIn('status', Finding::openStatusesForQuery())
            ->orderBy('id');

        if ($seenFingerprints !== []) {
            $query->whereNotIn('fingerprint', array_values(array_unique($seenFingerprints)));
        }

        $query->chunkById(100, function ($findings) use ($tenant, &$resolvedCount, $resolvedAt, $currentOperationRunId): void {
            foreach ($findings as $finding) {
                if (! $finding instanceof Finding) {
                    continue;
                }

                $this->findingWorkflowService()->resolveBySystem(
                    finding: $finding,
                    tenant: $tenant,
                    reason: 'no_longer_drifting',
                    resolvedAt: $resolvedAt,
                    operationRunId: $currentOperationRunId,
                    mutate: function (Finding $record) use ($currentOperationRunId): void {
                        $record->current_operation_run_id = $currentOperationRunId;
                    },
                );

                $resolvedCount++;
            }
        });

        return $resolvedCount;
    }

    private function resolveWorkspace(ManagedEnvironment $tenant): ?Workspace
    {
        $workspaceId = (int) ($tenant->workspace_id ?? 0);

        if ($workspaceId <= 0) {
            return null;
        }

        return Workspace::query()->whereKey($workspaceId)->first();
    }

    private function findingWorkflowService(): FindingWorkflowService
    {
        return $this->findingWorkflowService ?? app(FindingWorkflowService::class);
    }
}
