<?php
/**
 * External Data Sources Service
 *
 * Manages external data sources from active FleekDash integrations
 * Provides data source discovery, configuration, and data fetching
 */

namespace FleekDash\Services;

use Exception;

// Load debug helpers
if (!function_exists('fleekdash_log_error')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

defined('ABSPATH') || exit;

class ExternalDataSourcesService
{
    private const CONFIG_JSON_PATH = '/config.json';

    private IntegrationConfigResolver $configResolver;

    public function __construct(?IntegrationConfigResolver $configResolver = null)
    {
        $this->configResolver = $configResolver ?? new IntegrationConfigResolver();
    }

    /**
     * Get all external data sources from active integrations
     *
     * @return array
     */
    public function get_external_data_sources(): array
    {
        $sources = [];

        // V3.0: Use IntegrationManager
        try {
            $manager = \FleekDash\Core\IntegrationManager::getInstance();
            $integrations = $manager->getAll();

            // Process integrations from all categories
            foreach (['plugin', 'theme', 'api'] as $category) {
                if (isset($integrations[$category])) {
                    foreach ($integrations[$category] as $integration) {
                        if (isset($integration['is_active']) && $integration['is_active']) {
                            // Process active integration data sources
                            // Implementation can be added here as needed
                        }
                    }
                }
            }
        } catch (Exception $e) {
            fleekdash_log_error('Error getting integrations in ExternalDataSourcesService: ' . $e->getMessage());
        }

        return $sources;
    }

    /**
     * Get data sources for a specific integration
     *
     * @param string $integration_id
     * @param string $category
     * @return array
     */
    private function get_integration_data_sources(string $integration_id, string $category): array
    {
        $sources = [];

        try {
            // Check if integration exists and is active
            $integration_path = FLEEKDASH_PLUGIN_DIR . "integrations/{$integration_id}";
            $config_path = $integration_path . self::CONFIG_JSON_PATH;

            if (!file_exists($config_path)) {
                return $sources;
            }

            $config = json_decode(file_get_contents($config_path), true);
            if (!$config || !isset($config['status']) || $config['status'] !== 'active') {
                return $sources;
            }

            // Get integration data sources
            $data_sources = $config['data_sources'] ?? [];

            foreach ($data_sources as $source_config) {
                $source = $this->build_data_source($integration_id, $category, $config, $source_config);
                if ($source) {
                    $sources[] = $source;
                }
            }

        } catch (Exception $e) {
            fleekdash_log_error("FleekDash Integration Data Sources Error for {$integration_id}: " . $e->getMessage());
        }

        return $sources;
    }

    /**
     * Build a data source object from integration configuration
     *
     * @param string $integration_id
     * @param string $category
     * @param array $integration_config
     * @param array $source_config
     * @return array|null
     */
    private function build_data_source(string $integration_id, string $category, array $integration_config, array $source_config): ?array
    {
        try {
            $source_id = $source_config['id'] ?? 'default';
            $source_name = $source_config['name'] ?? $integration_config['name'] ?? $integration_id;

            return [
                'id' => "{$integration_id}_{$source_id}",
                'name' => $source_config['name'] ?? "{$source_name} - {$source_id}",
                'description' => $source_config['description'] ?? "Data from {$source_name}",
                'category' => $source_config['category'] ?? $category,
                'integration_id' => $integration_id,
                'integration_name' => $integration_config['name'] ?? $integration_id,
                'status' => $integration_config['status'] ?? 'inactive',
                'metrics' => $this->build_metrics($source_config['metrics'] ?? []),
                'config' => $this->build_source_config($source_config),
                'last_sync' => $this->get_last_sync_time($integration_id, $source_id),
                'error_message' => $this->get_last_error($integration_id, $source_id),
            ];

        } catch (Exception $e) {
            fleekdash_log_error("FleekDash Build Data Source Error: " . $e->getMessage());
            return null;
        }
    }

    /**
     * Build metrics array from source configuration
     *
     * @param array $metrics_config
     * @return array
     */
    private function build_metrics(array $metrics_config): array
    {
        $metrics = [];

        foreach ($metrics_config as $metric_config) {
            $metric = [
                'id' => $metric_config['id'] ?? 'unknown',
                'name' => $metric_config['name'] ?? 'Unknown Metric',
                'description' => $metric_config['description'] ?? '',
                'type' => $metric_config['type'] ?? 'number',
                'unit' => $metric_config['unit'] ?? null,
                'category' => $metric_config['category'] ?? 'general',
                'available_time_ranges' => $metric_config['time_ranges'] ?? ['24h', '7d', '30d', '90d'],
                'data_format' => $metric_config['data_format'] ?? 'time_series',
                'filters' => $this->build_filters($metric_config['filters'] ?? []),
            ];

            $metrics[] = $metric;
        }

        return $metrics;
    }

    /**
     * Build filters array from metric configuration
     *
     * @param array $filters_config
     * @return array
     */
    private function build_filters(array $filters_config): array
    {
        $filters = [];

        foreach ($filters_config as $filter_config) {
            $filter = [
                'name' => $filter_config['name'] ?? 'unknown',
                'type' => $filter_config['type'] ?? 'text',
                'options' => $filter_config['options'] ?? null,
                'required' => $filter_config['required'] ?? false,
            ];

            $filters[] = $filter;
        }

        return $filters;
    }

    /**
     * Build source configuration
     *
     * @param array $source_config
     * @return array
     */
    private function build_source_config(array $source_config): array
    {
        return [
            'endpoint' => $source_config['endpoint'] ?? null,
            'authentication' => $source_config['authentication'] ?? 'none',
            'rate_limit' => $source_config['rate_limit'] ?? null,
            'cache_ttl' => $source_config['cache_ttl'] ?? 300,
        ];
    }

    /**
     * Get last sync time for a data source
     *
     * @param string $integration_id
     * @param string $source_id
     * @return string|null
     */
    private function get_last_sync_time(string $integration_id, string $source_id): ?string
    {
        $sync_key = "fleekdash_sync_{$integration_id}_{$source_id}";
        $last_sync = \get_option($sync_key, null);

        return $last_sync ? date('c', $last_sync) : null;
    }

    /**
     * Get last error for a data source
     *
     * @param string $integration_id
     * @param string $source_id
     * @return string|null
     */
    private function get_last_error(string $integration_id, string $source_id): ?string
    {
        $error_key = "fleekdash_error_{$integration_id}_{$source_id}";
        return \get_option($error_key, null);
    }

    /**
     * Fetch data from an external data source
     *
     * @param string $source_id
     * @param string $metric_id
     * @param array $filters
     * @param string $time_range
     * @return array
     */
    public function fetch_external_data(string $source_id, string $metric_id, array $filters = [], string $time_range = '30d'): array
    {
        $integration_id = null;
        $actual_source_id = null;

        try {
            [$integration_id, $actual_source_id] = $this->configResolver->parseSourceId($source_id);
            $config_path = $this->configResolver->getValidatedIntegrationConfigPath($integration_id);
            $config = $this->configResolver->loadIntegrationConfig($config_path);
            $data_source = $this->configResolver->findDataSourceInConfig($config, $actual_source_id);
            $this->configResolver->validateMetricExists($data_source, $metric_id);

            $data = $this->fetch_integration_data($integration_id, $actual_source_id, $metric_id, $filters, $time_range);
            $this->update_sync_time($integration_id, $actual_source_id);

            return $this->buildSuccessResponse($data, $source_id, $metric_id);
        } catch (Exception $e) {
            $rawError = $e->getMessage();
            fleekdash_log_error("FleekDash ExternalDataSources fetch error: {$rawError}");

            if ($integration_id !== null && $actual_source_id !== null) {
                $this->update_error($integration_id, $actual_source_id, $rawError);
            }

            $userMessage = $this->sanitizeErrorForApiResponse($rawError);
            return $this->buildErrorResponse($userMessage, $source_id, $metric_id);
        }
    }

    private function buildSuccessResponse(array $data, string $source_id, string $metric_id): array
    {
        return [
            'success' => true,
            'data' => $data,
            'source' => $source_id,
            'metric' => $metric_id,
            'timestamp' => \current_time('c'),
        ];
    }

    /**
     * Sanitize error message for API response to prevent leaking internal details.
     * Allows known user-safe messages; returns generic message for upstream/sensitive errors.
     */
    private function sanitizeErrorForApiResponse(string $rawError): string
    {
        $safeMessages = [
            'Invalid source ID format',
            'Invalid integration ID format',
            'Integration not found',
            'Invalid integration configuration',
            'Data source not found',
            'Metric not found',
            'Invalid response from data source',
        ];

        foreach ($safeMessages as $safe) {
            if ($rawError === $safe) {
                return $rawError;
            }
        }

        return 'An error occurred while fetching data. Please try again.';
    }

    private function buildErrorResponse(string $error, string $source_id, string $metric_id): array
    {
        return [
            'success' => false,
            'error' => $error,
            'source' => $source_id,
            'metric' => $metric_id,
            'timestamp' => \current_time('c'),
        ];
    }

    /**
     * Fetch data from integration
     *
     * @param string $integration_id
     * @param string $source_id
     * @param string $metric_id
     * @param array $filters
     * @param string $time_range
     * @return array
     */
    private function fetch_integration_data(string $integration_id, string $source_id, string $metric_id, array $filters, string $time_range): array
    {
        // Try to load integration class
        $integration_class = "FleekDash\\Integrations\\{$integration_id}\\{$integration_id}";

        if (class_exists($integration_class)) {
            $integration = new $integration_class();

            if (method_exists($integration, 'fetch_data')) {
                return $integration->fetch_data($source_id, $metric_id, $filters, $time_range);
            }
        }

        // Fallback: try to fetch from integration's data endpoint
        // Path validated via realpath to prevent traversal (integration_id validated by fetch_external_data)
        $integrations_base = \realpath(FLEEKDASH_PLUGIN_DIR . 'integrations');
        if ($integrations_base && \preg_match('/^[a-zA-Z0-9_-]+$/', $integration_id)) {
            $config_dir = $integrations_base . \DIRECTORY_SEPARATOR . $integration_id;
            $resolved_path = \realpath($config_dir);
            $config_path = ($resolved_path && \str_starts_with($resolved_path, $integrations_base))
                ? $resolved_path . self::CONFIG_JSON_PATH
                : null;
        } else {
            $config_path = null;
        }
        if ($config_path && \file_exists($config_path)) {
            $config = json_decode(file_get_contents($config_path), true);
            $endpoint = $config['data_sources'][$source_id]['endpoint'] ?? null;

            if ($endpoint) {
                return $this->fetch_from_endpoint($endpoint, $metric_id, $filters, $time_range);
            }
        }

        // Final fallback: return mock data
        return $this->generate_mock_data($metric_id, $time_range);
    }

    /**
     * Fetch data from external endpoint
     *
     * @param string $endpoint
     * @param string $metric_id
     * @param array $filters
     * @param string $time_range
     * @return array
     */
    private function fetch_from_endpoint(string $endpoint, string $metric_id, array $filters, string $time_range): array
    {
        $url = add_query_arg([
            'metric' => $metric_id,
            'time_range' => $time_range,
            'filters' => json_encode($filters),
        ], $endpoint);

        $response = \wp_remote_get($url, [
            'timeout' => 30,
            'headers' => [
                'User-Agent' => 'FleekDash/2.0',
            ],
        ]);

        if (is_wp_error($response)) {
            throw new ExternalDataSourcesServiceException('Failed to fetch data: ' . $response->get_error_message());
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (!$data) {
            throw new ExternalDataSourcesServiceException('Invalid response from data source');
        }

        return $data;
    }

    /**
     * Generate mock data for testing
     *
     * @param string $metric_id
     * @param string $time_range
     * @return array
     */
    private function generate_mock_data(string $metric_id, string $time_range): array
    {
        $days = $this->get_days_from_range($time_range);
        $data = [];

        for ($i = $days - 1; $i >= 0; $i--) {
            $date = date('Y-m-d', strtotime("-{$i} days"));
            $value = rand(100, 1000); // Random value for demo

            $data[] = [
                'date' => $date,
                'value' => $value,
                'label' => $date,
            ];
        }

        return [
            'results' => $data,
            'total' => array_sum(array_column($data, 'value')),
            'count' => count($data),
        ];
    }

    /**
     * Get number of days from time range
     *
     * @param string $time_range
     * @return int
     */
    private function get_days_from_range(string $time_range): int
    {
        switch ($time_range) {
            case '24h':
                return 1;
            case '7d':
                return 7;
            case '30d':
                return 30;
            case '90d':
                return 90;
            default:
                return 30;
        }
    }

    /**
     * Update sync time for a data source
     *
     * @param string $integration_id
     * @param string $source_id
     */
    private function update_sync_time(string $integration_id, string $source_id): void
    {
        $sync_key = "fleekdash_sync_{$integration_id}_{$source_id}";
        \update_option($sync_key, time());

        // Clear error if sync was successful
        $error_key = "fleekdash_error_{$integration_id}_{$source_id}";
        \delete_option($error_key);
    }

    /**
     * Update error message for a data source
     *
     * @param string $integration_id
     * @param string $source_id
     * @param string $error_message
     */
    private function update_error(string $integration_id, string $source_id, string $error_message): void
    {
        $error_key = "fleekdash_error_{$integration_id}_{$source_id}";
        \update_option($error_key, $error_message);
    }

    /**
     * Get external data sources API response
     *
     * @return array
     */
    public function get_api_response(): array
    {
        $sources = $this->get_external_data_sources();

        return [
            'success' => true,
            'data' => $sources,
            'count' => count($sources),
            'timestamp' => \current_time('c'),
        ];
    }
}
