<?php

namespace App\Services\Intune;

use App\Models\ManagedEnvironment;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphContractRegistry;
use App\Services\Providers\MicrosoftGraphOptionsResolver;

class FoundationSnapshotService
{
    public function __construct(
        private readonly GraphClientInterface $graphClient,
        private readonly GraphContractRegistry $contracts,
        private readonly MicrosoftGraphOptionsResolver $graphOptionsResolver,
    ) {}

    /**
     * @return array{items: array<int, array{source_id:string,display_name:?string,payload:array,metadata:array}>, failures: array<int, array{foundation_type:string,reason:string,status:int|string|null}>}
     */
    public function fetchAll(ManagedEnvironment $tenant, string $foundationType): array
    {
        $resource = $this->contracts->resourcePath($foundationType);

        if (! $resource) {
            return [
                'items' => [],
                'failures' => [[
                    'foundation_type' => $foundationType,
                    'reason' => 'Graph contract resource missing for foundation type.',
                    'status' => null,
                ]],
            ];
        }

        $contract = $this->contracts->get($foundationType);
        $query = [];

        if (! empty($contract['allowed_select']) && is_array($contract['allowed_select'])) {
            $query['$select'] = $contract['allowed_select'];
        }

        if (! empty($contract['allowed_expand']) && is_array($contract['allowed_expand'])) {
            $query['$expand'] = $contract['allowed_expand'];
        }

        $sanitized = $this->contracts->sanitizeQuery($foundationType, $query);
        $options = $this->graphOptionsResolver->resolveForTenant($tenant);
        $items = [];
        $failures = [];
        $nextPath = $resource;
        $useQuery = $sanitized['query'] ?? [];

        while ($nextPath) {
            $response = $this->graphClient->request('GET', $nextPath, $options + [
                'query' => $useQuery,
            ]);

            if ($response->failed()) {
                $failures[] = [
                    'foundation_type' => $foundationType,
                    'reason' => $response->meta['error_message'] ?? $response->warnings[0] ?? 'Graph request failed.',
                    'status' => $response->status,
                ];

                break;
            }

            $data = $response->data;
            $pageItems = $data['value'] ?? (is_array($data) ? $data : []);

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

                $sourceId = $item['id'] ?? null;

                if (! is_string($sourceId) || $sourceId === '') {
                    continue;
                }

                $displayName = $item['displayName'] ?? $item['name'] ?? null;

                $items[] = [
                    'source_id' => $sourceId,
                    'display_name' => is_string($displayName) ? $displayName : null,
                    'payload' => $item,
                    'metadata' => [
                        'displayName' => is_string($displayName) ? $displayName : null,
                        'kind' => $foundationType,
                        'graph' => [
                            'resource' => $resource,
                            'apiVersion' => config('graph.version', 'beta'),
                        ],
                    ],
                ];
            }

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

            if (! $nextLink) {
                break;
            }

            $nextPath = $this->stripGraphBaseUrl((string) $nextLink);
            $useQuery = [];
        }

        return [
            'items' => $items,
            'failures' => $failures,
        ];
    }

    private function stripGraphBaseUrl(string $nextLink): string
    {
        $base = rtrim(config('graph.base_url', 'https://graph.microsoft.com'), '/')
            .'/'.trim(config('graph.version', 'beta'), '/');

        if (str_starts_with($nextLink, $base)) {
            return ltrim(substr($nextLink, strlen($base)), '/');
        }

        return ltrim($nextLink, '/');
    }
}
