<?php
/**
 * Integrations Controller
 *
 * REST API endpoints for the new integration system.
 *
 * @package FleekDash\Controllers\Integration
 * @since 2.3.0
 */

namespace FleekDash\Controllers\Integration;

use FleekDash\Integration\Registry;
use FleekDash\Integration\PluginLifecycleSync;
use FleekDash\Integration\RouteManager;
use FleekDash\Integration\SettingsManager;
use FleekDash\Integration\MessageBusSecurity;
use FleekDash\Services\IntegrationMergerService;
use FleekDash\Services\IntegrationNormalizerService;

defined('ABSPATH') || exit;

class IntegrationsController
{
    /**
     * List all integrations
     *
     * GET /fleekdash/v1/integrations
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function index($request)
    {
        try {
            $active_only_param = $request->get_param('active');
            $active_only = false;

            if (!is_null($active_only_param)) {
                if (is_bool($active_only_param)) {
                    $active_only = $active_only_param;
                } else {
                    $active_only = filter_var($active_only_param, FILTER_VALIDATE_BOOLEAN);
                }
            }

            $integrations = IntegrationMergerService::getAll($active_only);
            $category = strtolower((string) $request->get_param('category'));
            $integration_type = strtolower((string) $request->get_param('integration_type'));
            $status_filter = strtolower((string) $request->get_param('status'));
            $search = strtolower(trim((string) $request->get_param('search')));

            $filtered = array_filter($integrations, function ($integration) use ($category, $integration_type, $status_filter, $search) {
                return $this->integrationMatchesFilters($integration, $category, $integration_type, $status_filter, $search);
            });

            $filtered = array_values($filtered);

            $page = max(1, (int) $request->get_param('page')) ?: 1;
            $per_page = (int) $request->get_param('per_page');
            if ($per_page <= 0) {
                $per_page = count($filtered) > 0 ? count($filtered) : 1;
            }
            $per_page = min($per_page, 100);

            $total = count($filtered);
            $offset = ($page - 1) * $per_page;
            $paged = array_slice($filtered, $offset, $per_page);

            return \rest_ensure_response([
                'success' => true,
                'count' => $total,
                'page' => $page,
                'per_page' => $per_page,
                'integrations' => $paged,
            ]);
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Get single integration
     *
     * GET /fleekdash/v1/integrations/{id}
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function show($request)
    {
        try {
            $integration_id = $request->get_param('integration_id');
            $integration = IntegrationMergerService::get((string) $integration_id);

            if (!$integration) {
                return $this->responseWithStatus([
                    'success' => false,
                    'error' => SettingsManager::ERROR_INTEGRATION_NOT_FOUND,
                ], 404);
            }

            return \rest_ensure_response([
                'success' => true,
                'integration' => $integration,
            ]);
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Activate integration
     *
     * POST /fleekdash/v1/integrations/{id}/activate
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function activate($request) // NOSONAR php:S1142 - Activation has explicit installed/premium/registry outcomes.
    {
        try {
            $integration_id = $request->get_param('integration_id');
            $result = Registry::activate($integration_id);

            if (!$result['success']) {
                $integration = IntegrationMergerService::get((string) $integration_id);
                if (
                    $integration !== null
                    && ($integration['source'] ?? '') === 'premium-catalog'
                ) {
                    return $this->responseWithStatus([
                        'success' => false,
                        'error' => 'This premium extension is not installed yet.',
                    ], 400);
                }

                return $this->responseWithStatus($result, 400);
            }

            $plugin_sync = PluginLifecycleSync::syncWordPressPluginForIntegration((string) $integration_id, true);
            if (!($plugin_sync['success'] ?? false)) {
                Registry::deactivate((string) $integration_id);
                return $this->responseWithStatus([
                    'success' => false,
                    'error' => (string) ($plugin_sync['error'] ?? 'Failed to activate linked plugin'),
                ], 400);
            }

            // Clear route cache
            RouteManager::clearCache();

            return \rest_ensure_response([
                'success' => true,
                'message' => 'Integration activated successfully',
            ]);
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Deactivate integration
     *
     * POST /fleekdash/v1/integrations/{id}/deactivate
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function deactivate($request)
    {
        $response = null;
        try {
            $integration_id = $request->get_param('integration_id');
            $result = Registry::deactivate($integration_id);

            if (!$result['success']) {
                $response = $this->responseWithStatus($result, 400);
            } else {
                $plugin_sync = PluginLifecycleSync::syncWordPressPluginForIntegration((string) $integration_id, false);
                if (!($plugin_sync['success'] ?? false)) {
                    Registry::activate((string) $integration_id);
                    $response = $this->responseWithStatus([
                        'success' => false,
                        'error' => (string) ($plugin_sync['error'] ?? 'Failed to deactivate linked plugin'),
                    ], 400);
                } else {
                    // Clear route cache
                    RouteManager::clearCache();

                    $response = \rest_ensure_response([
                        'success' => true,
                        'message' => 'Integration deactivated successfully',
                    ]);
                }
            }
        } catch (\Exception $e) {
            $response = \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }

        return $response;
    }

    /**
     * Get integration routes
     *
     * GET /fleekdash/v1/integrations/routes
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function routes($request)
    {
        try {
            $active_only = $request->get_param('active') !== 'false';
            $grouped = $request->get_param('grouped') === 'true';
            $integration_id = $request->get_param('integration_id');

            if (!empty($integration_id)) {
                $integration_id = \sanitize_text_field($integration_id);
                $routes = RouteManager::getByIntegration($integration_id);

                if ($active_only) {
                    $routes = array_values(array_filter($routes, static function ($route) {
                        return !empty($route['is_active']);
                    }));
                }
                $integration = IntegrationMergerService::get($integration_id);
                if ($integration) {
                    foreach ($routes as &$route) {
                        $route['integration_name'] = $integration['name'] ?? '';
                        $route['integration_type'] = $integration['integration_type'] ?? ($integration['category'] ?? '');
                    }
                    unset($route);
                }
            } elseif ($grouped) {
                $routes = RouteManager::getGroupedByIntegration();
            } else {
                $routes = RouteManager::getAll($active_only);
            }

            return \rest_ensure_response([
                'success' => true,
                'count' => is_array($routes) ? count($routes) : 0,
                'routes' => $routes,
            ]);
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Validate route
     *
     * GET /fleekdash/v1/integrations/routes/validate
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function validateRoute($request)
    {
        try {
            $path = $request->get_param('path');

            if (empty($path)) {
                return $this->responseWithStatus([
                    'success' => false,
                    'error' => 'Path parameter is required',
                ], 400);
            }

            $result = RouteManager::validateRoute($path);

            return \rest_ensure_response($result);
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Get integration settings
     *
     * GET /fleekdash/v1/integrations/{id}/settings
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getSettings($request)
    {
        try {
            $integration_id = $request->get_param('integration_id');
            $include_sensitive = $request->get_param('include_sensitive') === 'true';

            // Only admins can see sensitive settings
            if ($include_sensitive && !\current_user_can('manage_options')) {
                $include_sensitive = false;
            }

            $settings = SettingsManager::getAll($integration_id, $include_sensitive);

            return \rest_ensure_response([
                'success' => true,
                'settings' => $settings,
            ]);
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Update integration settings
     *
     * POST /fleekdash/v1/integrations/{id}/settings
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function updateSettings($request) // NOSONAR php:S1142 - Settings update has validation, save, and error exits.
    {
        try {
            $integration_id = $request->get_param('integration_id');
            $settings = $request->get_json_params();

            if (empty($settings)) {
                return $this->responseWithStatus([
                    'success' => false,
                    'error' => 'Settings data is required',
                ], 400);
            }

            // Validate settings against schema
            $validation = SettingsManager::validate($integration_id, $settings);

            if (!$validation['success']) {
                $status = (($validation['error'] ?? '') === SettingsManager::ERROR_INTEGRATION_NOT_FOUND)
                    ? 404
                    : 400;

                return $this->responseWithStatus($validation, $status);
            }

            // Save each setting
            $validated_settings = $validation['settings'];
            $integration = Registry::get((string) $integration_id);
            if ($integration === null) {
                return $this->responseWithStatus([
                    'success' => false,
                    'error' => SettingsManager::ERROR_INTEGRATION_NOT_FOUND,
                ], 404);
            }

            $schema = is_array($integration['settings_schema'] ?? null)
                ? $integration['settings_schema']
                : [];

            foreach ($validated_settings as $key => $value) {
                SettingsManager::set($integration_id, $key, $value, $schema[$key] ?? []);
            }

            return \rest_ensure_response([
                'success' => true,
                'message' => 'Settings updated successfully',
                'settings' => $validated_settings,
            ]);
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Get MessageBus nonce (for frontend)
     *
     * GET /fleekdash/v1/integrations/messagebus/nonce
     *
     * @return \WP_REST_Response
     */
    public function getMessageBusNonce()
    {
        try {
            $nonce_data = MessageBusSecurity::getNonceForFrontend();

            return \rest_ensure_response([
                'success' => true,
                'nonce_data' => $nonce_data,
            ]);
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * @param array<string, mixed> $integration
     */
    private function integrationMatchesFilters(
        array $integration,
        string $category,
        string $integration_type,
        string $status_filter,
        string $search
    ): bool {
        $matches = true;

        if ($category !== '' && strtolower((string) ($integration['category'] ?? '')) !== $category) {
            $matches = false;
        }

        if ($matches && $integration_type !== '' && strtolower((string) ($integration['integration_type'] ?? '')) !== $integration_type) {
            $matches = false;
        }

        if ($matches && $status_filter !== '') {
            $matches = $this->matchesStatusFilter($integration, $status_filter);
        }

        if ($matches && $search !== '') {
            $matches = strpos($this->buildSearchHaystack($integration), $search) !== false;
        }

        return $matches;
    }

    /**
     * @param array<string, mixed> $integration
     */
    private function matchesStatusFilter(array $integration, string $status_filter): bool
    {
        $is_active = !empty($integration['is_active']) || ($integration['status'] ?? '') === 'active';
        $has_error = ($integration['status'] ?? '') === 'error' || !empty($integration['last_error']);
        $matches = true;

        if ($status_filter === 'active') {
            $matches = $is_active;
        } elseif ($status_filter === 'inactive') {
            $matches = !$is_active;
        } elseif ($status_filter === 'error') {
            $matches = $has_error;
        }

        return $matches;
    }

    /**
     * @param array<string, mixed> $integration
     */
    private function buildSearchHaystack(array $integration): string
    {
        return strtolower(
            implode(' ', [
                (string) ($integration['integration_id'] ?? ''),
                (string) ($integration['id'] ?? ''),
                (string) ($integration['name'] ?? ''),
                (string) ($integration['description'] ?? ''),
                (string) ($integration['source'] ?? ''),
                (string) ($integration['source_label'] ?? ''),
            ])
        );
    }

    /**
     * Update role access for an extension.
     *
     * POST /fleekdash/v1/integrations/{id}/permissions
     *
     * @param \WP_REST_Request $request
     */
    public function updatePermissions(\WP_REST_Request $request)
    {
        try {
            global $wpdb;

            $integration_id = \sanitize_text_field((string) $request->get_param('integration_id'));
            $payload = $request->get_json_params();
            $allowed_roles = IntegrationNormalizerService::roles($payload['allowed_roles'] ?? null);
            $response = $this->getIntegrationNotFoundResponse($integration_id);

            if ($response === null) {
                $saved = $this->updateAllowedRolesInStorage($integration_id, $allowed_roles);

                if (!$saved) {
                    $response = $this->responseWithStatus([
                        'success' => false,
                        'error' => 'Failed to update integration permissions: ' . (string) $wpdb->last_error,
                    ], 400);
                } else {
                    IntegrationMergerService::clearCache();
                    $persisted = Registry::get($integration_id);
                    $rolesForResponse = $persisted !== null && \array_key_exists(
                        'allowed_roles',
                        $persisted,
                    )
                        ? $persisted['allowed_roles']
                        : $allowed_roles;

                    $response = \rest_ensure_response([
                        'success' => true,
                        'allowed_roles' => $rolesForResponse,
                    ]);
                }
            }

            return $response;
        } catch (\Exception $e) {
            return \rest_ensure_response([
                'success' => false,
                'error' => $e->getMessage(),
            ]);
        }
    }

    private function getIntegrationNotFoundResponse(string $integration_id): ?\WP_REST_Response
    {
        $response = null;
        if (Registry::get($integration_id) === null) {
            $response = $this->responseWithStatus([
                'success' => false,
                'error' => SettingsManager::ERROR_INTEGRATION_NOT_FOUND,
            ], 404);
        }

        return $response;
    }

    /**
     * Persist allowed roles with backward compatibility for legacy schemas.
     *
     * @param array<int, string>|null $allowed_roles
     */
    private function updateAllowedRolesInStorage(string $integration_id, ?array $allowed_roles): bool
    {
        global $wpdb;

        $table = $wpdb->prefix . 'fleekdash_integration_registry';
        $roles_json = \wp_json_encode($allowed_roles);
        $result = $wpdb->update(
            $table,
            ['allowed_roles' => $roles_json],
            ['integration_id' => $integration_id],
            ['%s'],
            ['%s']
        );

        $saved = $result !== false;
        if (!$saved) {
            $last_error = (string) $wpdb->last_error;
            $missing_allowed_roles_column = stripos($last_error, 'unknown column') !== false
                && stripos($last_error, 'allowed_roles') !== false;

            if ($missing_allowed_roles_column) {
                $integration = Registry::get($integration_id);
                if ($integration !== null) {
                    $manifest = is_array($integration['manifest'] ?? null) ? $integration['manifest'] : [];
                    $manifest['allowed_roles'] = $allowed_roles;
                    $manifest_json = \wp_json_encode($manifest);
                    $manifest_hash = hash('sha256', $manifest_json);

                    $fallback_result = $wpdb->update(
                        $table,
                        [
                            'manifest' => $manifest_json,
                            'manifest_hash' => $manifest_hash,
                        ],
                        ['integration_id' => $integration_id],
                        ['%s', '%s'],
                        ['%s']
                    );

                    $saved = $fallback_result !== false;
                }
            }
        }

        return $saved;
    }

    private function responseWithStatus(array $data, int $status): \WP_REST_Response
    {
        $response = \rest_ensure_response($data);
        $response->set_status($status);
        return $response;
    }
}
