<?php

app_require('classes/edge_gateway_manager.php');
app_require('classes/edge_gateway_view_cache.php');

use classes\edge_gateway_manager;
use classes\edge_gateway_view_cache;

if (!class_exists('EdgeGatewayViewCacheRedisFake')) {
    class EdgeGatewayViewCacheRedisFake
    {
        /** @var array<string,string> */
        public array $store = [];

        public function get(string $key): ?string
        {
            return $this->store[$key] ?? null;
        }

        public function setEx(string $key, string $value, int $ttl): void
        {
            $this->store[$key] = $value;
        }

        public function clear_keys(string $pattern): void
        {
            $regex = '/^' . str_replace('\*', '.*', preg_quote($pattern, '/')) . '$/';
            foreach (array_keys($this->store) as $key) {
                if (preg_match($regex, $key) === 1) {
                    unset($this->store[$key]);
                }
            }
        }
    }
}

beforeEach(function (): void {
    $this->oldTtl = getenv('EDGE_GATEWAY_VIEW_CACHE_TTL');
    putenv('EDGE_GATEWAY_VIEW_CACHE_TTL=15');
    $this->redis = new EdgeGatewayViewCacheRedisFake();
    edge_gateway_view_cache::setAdapterForTests($this->redis);
});

afterEach(function (): void {
    edge_gateway_view_cache::setAdapterForTests(null);

    if ($this->oldTtl === false) {
        putenv('EDGE_GATEWAY_VIEW_CACHE_TTL');
        return;
    }

    putenv('EDGE_GATEWAY_VIEW_CACHE_TTL=' . $this->oldTtl);
});

function edgeGatewayCacheGatewayRow(
    int $gatewayId,
    int $departmentId,
    string $status,
    array $inventorySummary = ['total' => 0, 'online' => 0, 'offline' => 0],
    array $bindingSummary = ['total' => 0, 'fallback_overrides' => 0],
    array $fallbackSummary = ['cloud_only_relays' => 0, 'local_only_relays' => 0]
): array {
    return [
        'id' => $gatewayId,
        'department_id' => $departmentId,
        'label' => 'Gateway ' . $gatewayId,
        'status' => $status,
        'version_drift' => ['is_drifted' => false],
        'channel_status' => ['broker' => ['connected' => $status === edge_gateway_manager::STATUS_ONLINE]],
        'active_operation' => null,
        'recent_operations_summary' => ['pending' => 0, 'in_progress' => 0],
        'backlog_depth' => ['operations' => 0, 'commands' => 0],
        'metadata' => [
            'system_metrics' => [
                'latency_ms' => 100,
                'cpu_usage_pct' => 10,
                'memory_usage_pct' => 20,
                'disk_usage_pct' => 30,
            ],
        ],
        'inventory_summary' => $inventorySummary,
        'binding_summary' => $bindingSummary,
        'fallback_summary' => $fallbackSummary,
        'inventory' => [],
        'bindings' => [],
        'recent_commands' => [],
        'audit_logs' => [],
        'operations' => [],
    ];
}

it('uses sane ttl defaults and deterministic cache keys', function (): void {
    putenv('EDGE_GATEWAY_VIEW_CACHE_TTL');
    expect(edge_gateway_view_cache::getTtl())->toBe(15);

    putenv('EDGE_GATEWAY_VIEW_CACHE_TTL=25');
    expect(edge_gateway_view_cache::getTtl())->toBe(25);

    putenv('EDGE_GATEWAY_VIEW_CACHE_TTL=-5');
    expect(edge_gateway_view_cache::getTtl())->toBe(0);

    expect(edge_gateway_view_cache::listKey(null, false))->toBe('edge_gateway:view:v1:list:department:all:detail:0');
    expect(edge_gateway_view_cache::detailKey(701))->toBe('edge_gateway:view:v1:detail:701');
});

it('stores and retrieves cached list and detail payloads', function (): void {
    $gateway = edgeGatewayCacheGatewayRow(701, 1, edge_gateway_manager::STATUS_ONLINE, ['total' => 2, 'online' => 1, 'offline' => 1]);
    $payload = [
        'gateways' => [$gateway],
        'fleet_usage' => edge_gateway_manager::summarizeFleetUsageFromGatewayRows([$gateway]),
    ];

    edge_gateway_view_cache::storeListPayload(null, false, $payload, 30);
    edge_gateway_view_cache::storeDetailPayload(701, $gateway, 30);

    expect(edge_gateway_view_cache::getListPayload(null, false))->toBe($payload);
    expect(edge_gateway_view_cache::getDetailPayload(701))->toBe($gateway);
});

it('syncs gateway snapshots into cached list payloads and refreshes fleet usage', function (): void {
    $staleGateway = edgeGatewayCacheGatewayRow(
        701,
        1,
        edge_gateway_manager::STATUS_OFFLINE,
        ['total' => 1, 'online' => 0, 'offline' => 1],
        ['total' => 1, 'fallback_overrides' => 0],
        ['cloud_only_relays' => 0, 'local_only_relays' => 0]
    );
    $otherGateway = edgeGatewayCacheGatewayRow(
        702,
        2,
        edge_gateway_manager::STATUS_ONLINE,
        ['total' => 1, 'online' => 1, 'offline' => 0],
        ['total' => 1, 'fallback_overrides' => 1],
        ['cloud_only_relays' => 1, 'local_only_relays' => 0]
    );

    edge_gateway_view_cache::storeListPayload(null, false, [
        'gateways' => [$staleGateway, $otherGateway],
        'fleet_usage' => edge_gateway_manager::summarizeFleetUsageFromGatewayRows([$staleGateway, $otherGateway]),
    ]);
    edge_gateway_view_cache::storeListPayload(1, false, [
        'gateways' => [$staleGateway],
        'fleet_usage' => edge_gateway_manager::summarizeFleetUsageFromGatewayRows([$staleGateway]),
    ]);

    $freshGateway = [
        'id' => 701,
        'department_id' => 1,
        'label' => 'Gateway 701',
        'status' => edge_gateway_manager::STATUS_ONLINE,
        'version_drift' => ['is_drifted' => false],
        'channel_status' => ['broker' => ['connected' => true]],
        'active_operation' => null,
        'recent_operations_summary' => ['pending' => 0, 'in_progress' => 0],
        'backlog_depth' => ['operations' => 0, 'commands' => 0],
        'metadata' => [
            'system_metrics' => [
                'latency_ms' => 150,
                'cpu_usage_pct' => 15,
                'memory_usage_pct' => 25,
                'disk_usage_pct' => 35,
            ],
        ],
        'inventory' => [
            ['id' => 1, 'online' => true],
            ['id' => 2, 'online' => true],
        ],
        'bindings' => [
            ['id' => 1, 'fallback_mode' => edge_gateway_manager::RELAY_FALLBACK_PREFER_LOCAL],
            ['id' => 2, 'fallback_mode' => edge_gateway_manager::RELAY_FALLBACK_CLOUD_ONLY],
        ],
        'recent_commands' => [],
        'audit_logs' => [],
        'operations' => [],
        'fallback_summary' => ['cloud_only_relays' => 1, 'local_only_relays' => 0],
    ];

    edge_gateway_view_cache::syncGateway($freshGateway);

    $allGatewaysPayload = edge_gateway_view_cache::getListPayload(null, false);
    $departmentPayload = edge_gateway_view_cache::getListPayload(1, false);
    $detailPayload = edge_gateway_view_cache::getDetailPayload(701);

    expect($detailPayload)->not->toBeNull();
    expect($detailPayload['inventory_summary'])->toBe(['total' => 2, 'online' => 2, 'offline' => 0]);
    expect($detailPayload['binding_summary'])->toBe(['total' => 2, 'fallback_overrides' => 1]);

    expect($allGatewaysPayload)->not->toBeNull();
    expect($allGatewaysPayload['gateways'][0]['status'])->toBe(edge_gateway_manager::STATUS_ONLINE);
    expect($allGatewaysPayload['gateways'][0]['inventory'])->toBe([]);
    expect($allGatewaysPayload['fleet_usage']['gateways']['online'])->toBe(2);
    expect($allGatewaysPayload['fleet_usage']['inventory']['online'])->toBe(3);
    expect($allGatewaysPayload['fleet_usage']['bindings']['cloud_only'])->toBe(2);

    expect($departmentPayload)->not->toBeNull();
    expect($departmentPayload['gateways'][0]['status'])->toBe(edge_gateway_manager::STATUS_ONLINE);
    expect($departmentPayload['fleet_usage']['inventory']['online'])->toBe(2);
});

it('removes deleted gateways from cached list and detail payloads', function (): void {
    $gatewayA = edgeGatewayCacheGatewayRow(701, 1, edge_gateway_manager::STATUS_ONLINE);
    $gatewayB = edgeGatewayCacheGatewayRow(702, 1, edge_gateway_manager::STATUS_OFFLINE);
    $payload = [
        'gateways' => [$gatewayA, $gatewayB],
        'fleet_usage' => edge_gateway_manager::summarizeFleetUsageFromGatewayRows([$gatewayA, $gatewayB]),
    ];

    edge_gateway_view_cache::storeListPayload(null, false, $payload);
    edge_gateway_view_cache::storeListPayload(1, false, $payload);
    edge_gateway_view_cache::storeDetailPayload(701, $gatewayA);

    edge_gateway_view_cache::removeGateway(701, 1);

    expect(edge_gateway_view_cache::getDetailPayload(701))->toBeNull();
    expect(edge_gateway_view_cache::getListPayload(null, false)['gateways'])->toHaveCount(1);
    expect(edge_gateway_view_cache::getListPayload(1, false)['gateways'])->toHaveCount(1);
    expect(edge_gateway_view_cache::getListPayload(null, false)['fleet_usage']['gateways']['total'])->toBe(1);
});
