<?php
/**
 * Language Download Controller
 *
 * Handles language pack downloads from Cloudflare R2 CDN
 * Implements hybrid approach: core languages in plugin, others on-demand
 *
 * @package FleekDash\Controllers
 * @since 2.2.0
 */

namespace FleekDash\Controllers;

use WP_REST_Request;
use FleekDash\Services\ResponseService;
use FleekDash\Services\LanguagePackDownloader;
use FleekDash\Core\I18nManager;
use ZipArchive;

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

class LanguageDownloadController
{
    /**
     * Cloudflare R2 CDN URL for language packs
     */
    const R2_CDN_URL = 'https://storage.fleekdash.com/fleekdash/languages';

    /**
     * Local cache directory for downloaded languages
     */
    const LOCAL_CACHE_DIR = WP_CONTENT_DIR . '/languages/plugins/fleekdash';

    /**
     * Core languages included in plugin (minimal size)
     * NOTE: All languages are now downloadable since plugin is slim
     */
    const CORE_LANGUAGES = [];

    /**
     * Default language (no pack needed - uses code strings)
     */
    const DEFAULT_LANGUAGE = 'en_US';

    /**
     * Get manifest from Cloudflare R2
     *
     * @return array|null Manifest data or null on error
     */
    private static function fetchManifest(): ?array
    {
        $manifest_url = self::R2_CDN_URL . '/manifest.json';

        // Use WordPress HTTP API
        $response = \wp_remote_get($manifest_url, [
            'timeout' => 10,
            'sslverify' => true
        ]);

        if (is_wp_error($response)) {
            fleekdash_log_error('Failed to fetch language manifest: ' . $response->get_error_message());
            return null;
        }

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

        if (!$data) {
            fleekdash_log_error('Failed to decode language manifest JSON');
            return null;
        }

        return $data;
    }

    /**
     * Get list of all available languages (installed + downloadable)
     *
     * @param WP_REST_Request $_request Required by WP REST callback signature
     * @return \WP_REST_Response
     */
    public function getAvailableLanguages(WP_REST_Request $_request) // NOSONAR php:S1172 - WP REST callback
    {
        try {
            $i18n = I18nManager::getInstance();
            $installed = $i18n->getAvailableLanguages();
            $manifest = self::fetchManifest();

            $languages = $manifest
                ? $this->mergeInstalledWithManifest($installed, $manifest)
                : $this->buildInstalledOnly($installed);

            return ResponseService::withNoCacheHeaders(ResponseService::success($languages));
        } catch (\Exception $e) {
            fleekdash_log_error('Failed to get available languages: ' . $e->getMessage());
            return ResponseService::error('Failed to get available languages: ' . $e->getMessage());
        }
    }

    /**
     * @param array<string, string> $installed
     * @return list<array<string, mixed>>
     */
    private function buildInstalledOnly(array $installed): array
    {
        $languages = [];
        foreach ($installed as $locale => $name) {
            $isCore = in_array($locale, self::CORE_LANGUAGES) || $locale === self::DEFAULT_LANGUAGE;
            $languages[] = [
                'locale' => $locale,
                'name' => $name,
                'nativeName' => $name,
                'status' => 'installed',
                'isCore' => $isCore,
                'source' => $isCore ? 'core' : 'local'
            ];
        }
        return $languages;
    }

    /**
     * @param array<string, string> $installed
     * @param array<string, mixed> $manifest
     * @return list<array<string, mixed>>
     */
    private function mergeInstalledWithManifest(array $installed, array $manifest): array
    {
        $languages = [];
        foreach ($installed as $locale => $name) {
            $languages[] = $this->buildInstalledLanguageEntry($locale, $name, $manifest);
        }
        if (isset($manifest['languages'])) {
            foreach ($manifest['languages'] as $locale => $data) {
                if (!isset($installed[$locale])) {
                    $languages[] = $this->buildAvailableLanguageEntry($locale, $data);
                }
            }
        }
        return $languages;
    }

    /**
     * @param array<string, mixed> $manifest
     * @return array<string, mixed>
     */
    private function buildInstalledLanguageEntry(string $locale, string $name, array $manifest): array
    {
        $isCore = in_array($locale, self::CORE_LANGUAGES) || $locale === self::DEFAULT_LANGUAGE;
        $manifestData = $manifest['languages'][$locale] ?? null;
        $translationProgress = ($manifestData && isset($manifestData['translationProgress']))
            ? (int) $manifestData['translationProgress']
            : 100;

        return [
            'locale' => $locale,
            'name' => $manifestData['name'] ?? $name,
            'nativeName' => $manifestData['nativeName'] ?? $name,
            'flag' => $manifestData['flag'] ?? '🌐',
            'status' => 'installed',
            'isCore' => $isCore,
            'size' => $manifestData['size'] ?? 0,
            'translationProgress' => $translationProgress,
            'source' => $isCore ? 'core' : $this->detectLanguageSource($locale)
        ];
    }

    /**
     * @param array<string, mixed> $data
     * @return array<string, mixed>
     */
    private function buildAvailableLanguageEntry(string $locale, array $data): array
    {
        return [
            'locale' => $locale,
            'name' => $data['name'],
            'nativeName' => $data['nativeName'],
            'flag' => $data['flag'] ?? '🌐',
            'status' => 'available',
            'isCore' => false,
            'size' => $data['size'],
            'translationProgress' => $data['translationProgress'] ?? 0,
            'source' => 'r2'
        ];
    }

    /**
     * Detect source of installed language
     *
     * @param string $locale
     * @return string 'wporg'|'downloaded'|'core'
     */
    private function detectLanguageSource(string $locale): string
    {
        $text_domain = 'fleekdash';

        // Check WordPress.org location
        if (file_exists(WP_CONTENT_DIR . "/languages/plugins/{$text_domain}-{$locale}.mo")) {
            return 'wporg';
        }

        // Check downloaded location
        if (file_exists(WP_CONTENT_DIR . "/languages/plugins/fleekdash/{$text_domain}-{$locale}.mo")) {
            return 'downloaded';
        }

        // Core
        return 'core';
    }

    /**
     * Download and install a language pack from R2
     *
     * @param WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function downloadLanguage(WP_REST_Request $request)
    {
        try {
            return $this->executeDownloadFlow($request->get_param('locale'));
        } catch (\Exception $e) {
            fleekdash_log_error('Language download error: ' . $e->getMessage());
            return ResponseService::error('Failed to download language pack: ' . $e->getMessage());
        }
    }

    private function executeDownloadFlow(string $locale)
    {
        $validation = $this->validateLocaleAndFetchManifest($locale);
        if (isset($validation['error'])) {
            return $validation['error'];
        }
        $download = $this->getDownloader()->downloadToTemp($locale, $validation['langData']);
        if (isset($download['error'])) {
            return $download['error'];
        }
        $install = $this->validateExtractAndInstall(
            $download['tempFile'],
            $locale,
            $validation['manifest']
        );
        return isset($install['error'])
            ? $install['error']
            : ResponseService::withNoCacheHeaders(ResponseService::success([
                'locale' => $locale,
                'name' => $validation['langData']['name'],
                'size' => $validation['langData']['size'],
                'installed' => true
            ], 'Language pack installed successfully'));
    }

    /**
     * @return array{error: \WP_REST_Response}|array{manifest: array, langData: array}
     */
    private function validateLocaleAndFetchManifest(string $locale)
    {
        $result = null;
        if (!preg_match('/^[a-z]{2}_[A-Z]{2}$/', $locale) && !preg_match('/^[a-z]{2}$/', $locale)) {
            $result = ['error' => ResponseService::error('Invalid locale format', null, 400)];
        } elseif (in_array($locale, self::CORE_LANGUAGES) || $locale === self::DEFAULT_LANGUAGE) {
            $result = ['error' => ResponseService::error('This is a core language and cannot be re-downloaded', null, 400)];
        } else {
            $manifest = self::fetchManifest();
            if (!$manifest || !isset($manifest['languages'][$locale])) {
                $result = ['error' => ResponseService::error('Language not found in manifest', null, 404)];
            } else {
                $result = ['manifest' => $manifest, 'langData' => $manifest['languages'][$locale]];
            }
        }
        return $result;
    }

    private function getDownloader(): LanguagePackDownloader
    {
        return new LanguagePackDownloader(self::R2_CDN_URL, self::LOCAL_CACHE_DIR);
    }

    /**
     * @param array<string, mixed> $manifest
     * @return array{error: \WP_REST_Response}|array{}
     */
    private function validateExtractAndInstall(string $tempFile, string $locale, array $manifest)
    {
        $error = $this->getDownloader()->extractAndValidate($tempFile, $locale);
        if ($error !== null) {
            return ['error' => $error];
        }
        $this->saveLanguageMetadata($locale, $manifest['version']);
        I18nManager::getInstance()->clearCache();
        fleekdash_log_info("Language pack {$locale} downloaded and installed successfully");
        return [];
    }

    /**
     * Check for language pack updates
     *
     * @param WP_REST_Request $_request Required by WP REST callback signature
     * @return \WP_REST_Response
     */
    public function checkUpdates(WP_REST_Request $_request) // NOSONAR php:S1172 - WP REST callback
    {
        try {
            // Get manifest
            $manifest = self::fetchManifest();
            if (!$manifest) {
                return ResponseService::error('Failed to fetch update manifest');
            }

            // Get installed languages metadata
            $installed_metadata = \get_option('fleekdash_installed_languages', []);

            if (!is_array($installed_metadata)) {
                $installed_metadata = [];
            }

            $updates = [];

            foreach ($installed_metadata as $locale => $lang_metadata) {
                // Skip core languages
                if (in_array($locale, self::CORE_LANGUAGES)) {
                    continue;
                }

                // Check if update available
                if (isset($manifest['languages'][$locale])) {
                    $remote_version = $manifest['version'];
                    $local_version = $lang_metadata['version'] ?? '0.0.0';

                    if (version_compare($remote_version, $local_version, '>')) {
                        $updates[] = [
                            'locale' => $locale,
                            'name' => $manifest['languages'][$locale]['name'],
                            'current_version' => $local_version,
                            'new_version' => $remote_version,
                            'size' => $manifest['languages'][$locale]['size']
                        ];
                    }
                }
            }

            $response = ResponseService::success([
                'updates_available' => count($updates),
                'updates' => $updates,
                'current_version' => FLEEKDASH_VERSION,
                'manifest_version' => $manifest['version']
            ]);
            // Apply no-cache headers to ensure fresh update data
            return ResponseService::withNoCacheHeaders($response);

        } catch (\Exception $e) {
            fleekdash_log_error('Check updates error: ' . $e->getMessage());
            return ResponseService::error('Failed to check updates: ' . $e->getMessage());
        }
    }

    /**
     * Save language installation metadata
     *
     * @param string $locale
     * @param string $version
     * @return bool Success status
     */
    private function saveLanguageMetadata(string $locale, string $version): bool
    {
        $installed = \get_option('fleekdash_installed_languages', []);

        $installed[$locale] = [
            'version' => $version,
            'installed_at' => time(),
            'source' => 'r2'
        ];

        return \update_option('fleekdash_installed_languages', $installed);
    }

    /**
     * Delete an installed language pack
     *
     * @param WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function deleteLanguage(WP_REST_Request $request)
    {
        try {
            $locale = $request->get_param('locale');

            // Don't allow deleting core languages
            if (in_array($locale, self::CORE_LANGUAGES) || $locale === self::DEFAULT_LANGUAGE) {
                return ResponseService::error('Cannot delete core language', null, 400);
            }

            // Delete files from downloaded location
            $files_to_delete = [
                self::LOCAL_CACHE_DIR . "/fleekdash-{$locale}.po",
                self::LOCAL_CACHE_DIR . "/fleekdash-{$locale}.mo",
                self::LOCAL_CACHE_DIR . "/fleekdash-{$locale}-js.json"
            ];

            $deleted_count = 0;
            foreach ($files_to_delete as $file) {
                if (file_exists($file) && unlink($file)) {
                    $deleted_count++;
                }
            }

            // Remove from metadata
            $installed = \get_option('fleekdash_installed_languages', []);
            unset($installed[$locale]);
            \update_option('fleekdash_installed_languages', $installed);

            // Clear I18n cache
            $i18n = I18nManager::getInstance();
            $i18n->clearCache();

            fleekdash_log_info("Language pack {$locale} deleted successfully");

            $response = ResponseService::success([
                'locale' => $locale,
                'files_deleted' => $deleted_count
            ], 'Language pack deleted successfully');
            // Apply no-cache headers to ensure UI reflects deletion immediately
            return ResponseService::withNoCacheHeaders($response);

        } catch (\Exception $e) {
            fleekdash_log_error('Delete language error: ' . $e->getMessage());
            return ResponseService::error('Failed to delete language pack: ' . $e->getMessage());
        }
    }

    /**
     * Get download progress (for future use with chunked downloads)
     *
     * @param WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getDownloadProgress(WP_REST_Request $request)
    {
        $locale = $request->get_param('locale');

        // Check if currently downloading (stored in transient)
        $progress = \get_transient("fleekdash_language_download_{$locale}");

        if (!$progress) {
            $response = ResponseService::success([
                'locale' => $locale,
                'downloading' => false,
                'progress' => 0
            ]);
            // Apply no-cache headers for real-time progress updates
            return ResponseService::withNoCacheHeaders($response);
        }

        $response = ResponseService::success([
            'locale' => $locale,
            'downloading' => true,
            'progress' => $progress['progress'] ?? 0,
            'status' => $progress['status'] ?? 'downloading'
        ]);
        // Apply no-cache headers for real-time progress updates
        return ResponseService::withNoCacheHeaders($response);
    }
}
