<?php

namespace App\Services\Intune;

use App\Models\SettingsCatalogCategory;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphContractRegistry;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class SettingsCatalogCategoryResolver
{
    private const MEMORY_CACHE_PREFIX = 'settings_catalog_category:';

    private const CACHE_TTL = 3600; // 1 hour in memory

    public function __construct(
        private readonly GraphClientInterface $graphClient,
        private readonly GraphContractRegistry $contracts,
    ) {}

    /**
     * Resolve category IDs to display names.
     *
     * @param  array<string>  $categoryIds
     * @return array<string, array{displayName: string, description: ?string}>
     */
    public function resolve(array $categoryIds): array
    {
        if (empty($categoryIds)) {
            return [];
        }

        $categories = [];
        $missingIds = [];

        // Step 1: Check memory cache
        foreach ($categoryIds as $id) {
            $cached = Cache::get(self::MEMORY_CACHE_PREFIX.$id);
            if ($cached !== null) {
                $categories[$id] = $cached;
            } else {
                $missingIds[] = $id;
            }
        }

        if (empty($missingIds)) {
            return $categories;
        }

        // Step 2: Check database cache
        $dbCategories = SettingsCatalogCategory::whereIn('category_id', $missingIds)->get();
        foreach ($dbCategories as $dbCat) {
            $metadata = [
                'displayName' => $dbCat->display_name,
                'description' => $dbCat->description,
            ];
            $categories[$dbCat->category_id] = $metadata;

            // Cache in memory
            Cache::put(
                self::MEMORY_CACHE_PREFIX.$dbCat->category_id,
                $metadata,
                now()->addSeconds(self::CACHE_TTL)
            );

            $missingIds = array_diff($missingIds, [$dbCat->category_id]);
        }

        if (empty($missingIds)) {
            return $categories;
        }

        // Step 3: Fetch from Graph API
        try {
            $graphCategories = $this->fetchFromGraph($missingIds);

            foreach ($graphCategories as $categoryId => $metadata) {
                // Store in database
                SettingsCatalogCategory::updateOrCreate(
                    ['category_id' => $categoryId],
                    [
                        'display_name' => $metadata['displayName'],
                        'description' => $metadata['description'],
                    ]
                );

                // Cache in memory
                Cache::put(
                    self::MEMORY_CACHE_PREFIX.$categoryId,
                    $metadata,
                    now()->addSeconds(self::CACHE_TTL)
                );

                $categories[$categoryId] = $metadata;
            }
        } catch (\Exception $e) {
            Log::error('Failed to fetch categories from Graph API', [
                'category_ids' => $missingIds,
                'error' => $e->getMessage(),
            ]);
        }

        // Step 4: Fallback for still missing categories
        foreach ($missingIds as $id) {
            if (! isset($categories[$id])) {
                $fallback = [
                    'displayName' => 'Category',
                    'description' => null,
                ];
                $categories[$id] = $fallback;

                // Cache fallback in memory too (short TTL)
                Cache::put(
                    self::MEMORY_CACHE_PREFIX.$id,
                    $fallback,
                    now()->addMinutes(5)
                );
            }
        }

        return $categories;
    }

    /**
     * Resolve a single category ID.
     */
    public function resolveOne(string $categoryId): ?array
    {
        $result = $this->resolve([$categoryId]);

        return $result[$categoryId] ?? null;
    }

    /**
     * Fetch categories from Graph API.
     */
    private function fetchFromGraph(array $categoryIds): array
    {
        $categories = [];

        // Fetch each category individually
        foreach ($categoryIds as $categoryId) {
            try {
                $path = $this->contracts->settingsCatalogCategoryItemPath($categoryId);
                $response = $this->graphClient->request(
                    'GET',
                    $path
                );

                if ($response->successful() && isset($response->data)) {
                    $item = $response->data;
                    $categories[$categoryId] = [
                        'displayName' => $item['displayName'] ?? 'Category',
                        'description' => $item['description'] ?? null,
                    ];
                }
            } catch (\Exception $e) {
                Log::warning('Failed to fetch category from Graph API', [
                    'categoryId' => $categoryId,
                    'error' => $e->getMessage(),
                ]);
                // Continue with other categories
            }
        }

        return $categories;
    }
}
