<?php

namespace FleekDash\Integrations\Core;

defined('ABSPATH') || exit;

/**
 * Dependency Resolver for Integration Load Order
 *
 * Handles dependency resolution and priority-based loading order
 */
class DependencyResolver
{
    private DiscoveryEngine $discovery;

    public function __construct(DiscoveryEngine $discovery)
    {
        $this->discovery = $discovery;
    }

    /**
     * Resolve load order for integrations
     *
     * @param array $integrationIds Integration IDs to resolve
     * @return array Sorted array with load order and priorities
     */
    public function resolveLoadOrder(array $integrationIds): array
    {
        $integrations = [];
        $loadOrder = [];

        // Get integration configs
        foreach ($integrationIds as $id) {
            $config = $this->getIntegrationConfig($id);
            if ($config) {
                $integrations[$id] = $config;
            }
        }

        // Check for circular dependencies
        $this->checkCircularDependencies($integrations);

        // Resolve dependencies using topological sort
        $resolved = $this->topologicalSort($integrations);

        // Group by priority and sort within groups
        $priorityGroups = [];
        foreach ($resolved as $id) {
            $priority = $integrations[$id]['priority'] ?? 10;
            if (!isset($priorityGroups[$priority])) {
                $priorityGroups[$priority] = [];
            }
            $priorityGroups[$priority][] = $id;
        }

        // Sort by priority (lower number = higher priority)
        ksort($priorityGroups);

        // Build final load order
        foreach ($priorityGroups as $priority => $group) {
            foreach ($group as $id) {
                $loadOrder[] = [
                    'id' => $id,
                    'priority' => $priority,
                    'dependencies' => $integrations[$id]['dependencies'] ?? []
                ];
            }
        }

        return $loadOrder;
    }

    /**
     * Check for circular dependencies
     */
    private function checkCircularDependencies(array $integrations): void
    {
        $visited = [];
        $recursionStack = [];

        foreach ($integrations as $id => $config) {
            if ($this->hasCycle($id, $integrations, $visited, $recursionStack)) {
                throw new \Exception("Circular dependency detected involving integration: {$id}");
            }
        }
    }

    /**
     * Detect cycles using DFS
     */
    private function hasCycle(string $id, array $integrations, array &$visited, array &$recursionStack): bool
    {
        if (isset($recursionStack[$id])) {
            return true; // Cycle detected
        }

        if (isset($visited[$id])) {
            return false; // Already processed
        }

        $visited[$id] = true;
        $recursionStack[$id] = true;

        $dependencies = $integrations[$id]['dependencies'] ?? [];
        foreach ($dependencies as $dependency) {
            if (isset($integrations[$dependency])) {
                if ($this->hasCycle($dependency, $integrations, $visited, $recursionStack)) {
                    return true;
                }
            }
        }

        unset($recursionStack[$id]);
        return false;
    }

    /**
     * Topological sort for dependency resolution
     */
    private function topologicalSort(array $integrations): array
    {
        $inDegree = [];
        $graph = [];
        $result = [];
        $queue = [];

        // Initialize
        foreach ($integrations as $id => $config) {
            $inDegree[$id] = 0;
            $graph[$id] = [];
        }

        // Build graph and calculate in-degrees
        foreach ($integrations as $id => $config) {
            $dependencies = $config['dependencies'] ?? [];
            foreach ($dependencies as $dependency) {
                if (isset($integrations[$dependency])) {
                    $graph[$dependency][] = $id;
                    $inDegree[$id]++;
                }
            }
        }

        // Find nodes with no dependencies
        foreach ($inDegree as $id => $degree) {
            if ($degree === 0) {
                $queue[] = $id;
            }
        }

        // Process queue
        while (!empty($queue)) {
            $current = array_shift($queue);
            $result[] = $current;

            foreach ($graph[$current] as $neighbor) {
                $inDegree[$neighbor]--;
                if ($inDegree[$neighbor] === 0) {
                    $queue[] = $neighbor;
                }
            }
        }

        // Check if all nodes were processed
        if (count($result) !== count($integrations)) {
            throw new \Exception('Dependency resolution failed - possible circular dependency');
        }

        return $result;
    }

    /**
     * Get integration configuration
     */
    private function getIntegrationConfig(string $id): ?array
    {
        $configPath = FLEEKDASH_PLUGIN_DIR . "integrations/{$id}/config.json";

        if (!file_exists($configPath)) {
            return null;
        }

        $configContent = file_get_contents($configPath);
        $config = json_decode($configContent, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            return null;
        }

        return $config;
    }

    /**
     * Validate dependencies exist
     */
    public function validateDependencies(array $integrationIds): array
    {
        $missing = [];
        $integrations = $this->discovery->discover();

        foreach ($integrationIds as $id) {
            $config = $integrations[$id] ?? null;
            if (!$config)
                continue;

            $dependencies = $config['dependencies'] ?? [];
            foreach ($dependencies as $dependency) {
                if (!isset($integrations[$dependency])) {
                    $missing[] = [
                        'integration' => $id,
                        'missing_dependency' => $dependency
                    ];
                } elseif (!in_array($dependency, $integrationIds)) {
                    $missing[] = [
                        'integration' => $id,
                        'inactive_dependency' => $dependency
                    ];
                }
            }
        }

        return $missing;
    }

    /**
     * Get load order summary for debugging
     */
    public function getLoadOrderSummary(array $integrationIds): array
    {
        $loadOrder = $this->resolveLoadOrder($integrationIds);
        $summary = [];

        foreach ($loadOrder as $index => $item) {
            $summary[] = [
                'position' => $index + 1,
                'id' => $item['id'],
                'priority' => $item['priority'],
                'dependencies' => $item['dependencies'],
                'load_time_estimate' => $this->estimateLoadTime($item['priority'])
            ];
        }

        return $summary;
    }

    /**
     * Estimate load time based on priority
     */
    private function estimateLoadTime(int $priority): string
    {
        // Lower priority = earlier loading
        $baseTime = 50; // Base microseconds
        $time = $baseTime + ($priority * 10);
        return $time . 'μs';
    }
}
