<?php

namespace App\Services\Providers;

use App\Models\ProviderConnection;
use App\Services\Graph\GraphContractRegistry;
use App\Services\Graph\GraphResponse;
use App\Services\Providers\Contracts\ProviderComplianceCollector;
use RuntimeException;

final class MicrosoftComplianceSnapshotService implements ProviderComplianceCollector
{
    private const int MAX_PAGES = 50;

    public function __construct(
        private readonly ProviderGateway $gateway,
        private readonly GraphContractRegistry $contracts,
    ) {}

    public function snapshot(ProviderConnection $connection): array
    {
        $resource = $this->contracts->resourcePath('managedDevices');

        if (! is_string($resource) || $resource === '') {
            throw new RuntimeException('Graph contract missing for managed devices.');
        }

        $queryInput = [
            '$top' => 999,
            '$select' => 'id,complianceState',
        ];

        $sanitized = $this->contracts->sanitizeQuery('managedDevices', $queryInput);
        $query = $sanitized['query'];

        $counts = [
            'total' => 0,
            'compliant' => 0,
            'noncompliant' => 0,
            'unknown' => 0,
        ];

        $path = $resource;
        $pages = 0;

        while (true) {
            $pages++;

            if ($pages > self::MAX_PAGES) {
                throw new RuntimeException('Graph pagination exceeded maximum page limit.');
            }

            $options = $query === [] ? [] : ['query' => $query];

            $response = $this->gateway->request(
                connection: $connection,
                method: 'GET',
                path: $path,
                options: $options,
            );

            $payload = $this->requireSuccess($response);

            $items = $payload['value'] ?? [];
            if (! is_array($items)) {
                $items = [];
            }

            foreach ($items as $item) {
                if (! is_array($item)) {
                    continue;
                }

                $counts['total']++;

                $state = strtolower((string) ($item['complianceState'] ?? ''));

                if ($state === 'compliant') {
                    $counts['compliant']++;
                } elseif ($state === 'noncompliant') {
                    $counts['noncompliant']++;
                } else {
                    $counts['unknown']++;
                }
            }

            $nextLink = $payload['@odata.nextLink'] ?? null;

            if (! is_string($nextLink) || $nextLink === '') {
                break;
            }

            $path = $nextLink;
            $query = [];
        }

        return $counts;
    }

    /**
     * @return array<string, mixed>
     */
    private function requireSuccess(GraphResponse $response): array
    {
        if ($response->successful()) {
            $data = $response->data;

            return is_array($data) ? $data : [];
        }

        $message = $this->messageForResponse($response);
        $status = (int) ($response->status ?? 0);

        throw new RuntimeException("Graph request failed (status {$status}): {$message}");
    }

    private function messageForResponse(GraphResponse $response): string
    {
        $error = $response->errors[0] ?? null;

        if (is_string($error)) {
            return $error;
        }

        if (is_array($error)) {
            $message = $error['message'] ?? null;

            if (is_string($message) && $message !== '') {
                return $message;
            }

            return json_encode($error, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: 'Request failed.';
        }

        return 'Request failed.';
    }
}
