<?php
/**
 * Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
 *
 * This file is part of a Moko Consulting project.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

/**
 * Bridge migration helper — MokoCassiopeia → MokoOnyx
 *
 * Called from script.php during the v03.10.00 update. Copies the template
 * to the new directory name, migrates database records, and sets MokoOnyx
 * as the active site template.
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Log\Log;

class MokoBridgeMigration
{
    private const OLD_NAME = 'mokocassiopeia';
    private const NEW_NAME = 'mokoonyx';

    private const OLD_DISPLAY = 'MokoCassiopeia';
    private const NEW_DISPLAY = 'MokoOnyx';

    /**
     * Run the full migration.
     *
     * @return bool True on success, false on failure.
     */
    public static function run(): bool
    {
        $app = Factory::getApplication();
        $db  = Factory::getDbo();

        // 1. Copy template files
        if (!self::copyTemplateFiles()) {
            $app->enqueueMessage(
                'MokoOnyx migration: failed to copy template files. '
                . 'You can manually copy templates/mokocassiopeia to templates/mokoonyx.',
                'error'
            );
            return false;
        }

        // 2. Copy media files
        if (!self::copyMediaFiles()) {
            $app->enqueueMessage(
                'MokoOnyx migration: failed to copy media files. '
                . 'You can manually copy media/templates/site/mokocassiopeia to media/templates/site/mokoonyx.',
                'warning'
            );
        }

        // 3. Rename internals in the new copy (templateDetails.xml, language files, etc.)
        self::renameInternals();

        // 4. Register the new template in the database
        self::migrateDatabase($db);

        // 5. Notify the admin
        $app->enqueueMessage(
            '<strong>MokoCassiopeia has been renamed to MokoOnyx.</strong><br>'
            . 'Your template settings have been migrated automatically. '
            . 'MokoOnyx is now your active site template. '
            . 'You can safely uninstall MokoCassiopeia from Extensions → Manage.',
            'success'
        );

        self::log('Bridge migration completed successfully.');
        return true;
    }

    /**
     * Copy template directory.
     */
    private static function copyTemplateFiles(): bool
    {
        $src = JPATH_ROOT . '/templates/' . self::OLD_NAME;
        $dst = JPATH_ROOT . '/templates/' . self::NEW_NAME;

        if (is_dir($dst)) {
            self::log('MokoOnyx template directory already exists — skipping copy.');
            return true;
        }

        if (!is_dir($src)) {
            self::log('Source template directory not found: ' . $src, 'error');
            return false;
        }

        return Folder::copy($src, $dst);
    }

    /**
     * Copy media directory.
     */
    private static function copyMediaFiles(): bool
    {
        $src = JPATH_ROOT . '/media/templates/site/' . self::OLD_NAME;
        $dst = JPATH_ROOT . '/media/templates/site/' . self::NEW_NAME;

        if (is_dir($dst)) {
            self::log('MokoOnyx media directory already exists — skipping copy.');
            return true;
        }

        if (!is_dir($src)) {
            self::log('Source media directory not found: ' . $src, 'warning');
            return true; // Non-critical
        }

        return Folder::copy($src, $dst);
    }

    /**
     * Rename internal references in the copied template.
     */
    private static function renameInternals(): void
    {
        $base = JPATH_ROOT . '/templates/' . self::NEW_NAME;
        $mediaBase = JPATH_ROOT . '/media/templates/site/' . self::NEW_NAME;

        // templateDetails.xml — name, element, update servers, paths
        $manifest = $base . '/templateDetails.xml';
        if (is_file($manifest)) {
            $content = file_get_contents($manifest);
            $content = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $content);
            $content = str_replace(self::OLD_NAME, self::NEW_NAME, $content);
            // Update the update server URLs to point to MokoOnyx repo
            $content = str_replace('MokoCassiopeia', 'MokoOnyx', $content);
            file_put_contents($manifest, $content);
            self::log('Updated templateDetails.xml for MokoOnyx.');
        }

        // joomla.asset.json
        $assetFile = $base . '/joomla.asset.json';
        if (is_file($assetFile)) {
            $content = file_get_contents($assetFile);
            $content = str_replace(self::OLD_NAME, self::NEW_NAME, $content);
            $content = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $content);
            file_put_contents($assetFile, $content);
        }

        // Language files
        $langDirs = [
            $base . '/language/en-GB',
            $base . '/language/en-US',
        ];
        foreach ($langDirs as $langDir) {
            if (!is_dir($langDir)) continue;

            foreach (glob($langDir . '/*mokocassiopeia*') as $file) {
                $newFile = str_replace(self::OLD_NAME, self::NEW_NAME, $file);
                if (is_file($file)) {
                    $content = file_get_contents($file);
                    $content = str_replace('MOKOCASSIOPEIA', 'MOKOONYX', $content);
                    $content = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $content);
                    $content = str_replace(self::OLD_NAME, self::NEW_NAME, $content);
                    file_put_contents($newFile, $content);
                    if ($newFile !== $file) {
                        File::delete($file);
                    }
                }
            }
        }

        // script.php — class name
        $scriptFile = $base . '/script.php';
        if (is_file($scriptFile)) {
            $content = file_get_contents($scriptFile);
            $content = str_replace('Tpl_MokocassiopeiaInstallerScript', 'Tpl_MokoonyxInstallerScript', $content);
            $content = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $content);
            $content = str_replace(self::OLD_NAME, self::NEW_NAME, $content);
            // Remove the bridge migration call from the new template's script
            $content = preg_replace(
                '/\/\/ Bridge migration.*?MokoBridgeMigration::run\(\);/s',
                '// Migration complete — this is MokoOnyx',
                $content
            );
            file_put_contents($scriptFile, $content);
        }

        // Remove bridge helper from the new template (not needed)
        $bridgeFile = $base . '/helper/bridge.php';
        if (is_file($bridgeFile)) {
            File::delete($bridgeFile);
        }

        self::log('Renamed internal references in MokoOnyx.');
    }

    /**
     * Migrate database records: template_styles, menu assignments.
     */
    private static function migrateDatabase(\Joomla\Database\DatabaseInterface $db): void
    {
        // Get existing MokoCassiopeia styles
        $query = $db->getQuery(true)
            ->select('*')
            ->from('#__template_styles')
            ->where($db->quoteName('template') . ' = ' . $db->quote(self::OLD_NAME))
            ->where($db->quoteName('client_id') . ' = 0');
        $oldStyles = $db->setQuery($query)->loadObjectList();

        if (empty($oldStyles)) {
            self::log('No MokoCassiopeia styles found in database.', 'warning');
            return;
        }

        foreach ($oldStyles as $oldStyle) {
            // Check if MokoOnyx style already exists
            $query = $db->getQuery(true)
                ->select('COUNT(*)')
                ->from('#__template_styles')
                ->where($db->quoteName('template') . ' = ' . $db->quote(self::NEW_NAME))
                ->where($db->quoteName('title') . ' = ' . $db->quote(
                    str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $oldStyle->title)
                ));
            $exists = (int) $db->setQuery($query)->loadResult();

            if ($exists > 0) {
                continue;
            }

            // Create new style with same params
            $newStyle = clone $oldStyle;
            unset($newStyle->id);
            $newStyle->template = self::NEW_NAME;
            $newStyle->title = str_replace(self::OLD_DISPLAY, self::NEW_DISPLAY, $oldStyle->title);

            // Update params: replace any mokocassiopeia paths
            $params = $newStyle->params;
            if (is_string($params)) {
                $params = str_replace(self::OLD_NAME, self::NEW_NAME, $params);
                $newStyle->params = $params;
            }

            $db->insertObject('#__template_styles', $newStyle, 'id');
            $newId = $newStyle->id;

            // Copy menu assignments
            $query = $db->getQuery(true)
                ->select('menuid')
                ->from('#__template_styles_menus')  // Joomla 5 uses this table
                ->where('template_style_id = ' . (int) $oldStyle->id);

            try {
                $menuIds = $db->setQuery($query)->loadColumn();
                foreach ($menuIds as $menuId) {
                    $obj = (object) [
                        'template_style_id' => $newId,
                        'menuid'            => $menuId,
                    ];
                    $db->insertObject('#__template_styles_menus', $obj);
                }
            } catch (\Exception $e) {
                // Table may not exist in all Joomla versions
            }

            // If this was the default style, make MokoOnyx the default
            if ($oldStyle->home == 1) {
                // Set MokoOnyx as default
                $query = $db->getQuery(true)
                    ->update('#__template_styles')
                    ->set($db->quoteName('home') . ' = 1')
                    ->where('id = ' . (int) $newId);
                $db->setQuery($query)->execute();

                // Unset MokoCassiopeia as default
                $query = $db->getQuery(true)
                    ->update('#__template_styles')
                    ->set($db->quoteName('home') . ' = 0')
                    ->where('id = ' . (int) $oldStyle->id);
                $db->setQuery($query)->execute();

                self::log('Set MokoOnyx as default site template.');
            }
        }

        // Register the new template in the extensions table
        self::registerExtension($db);

        self::log('Database migration completed. ' . count($oldStyles) . ' style(s) migrated.');
    }

    /**
     * Register MokoOnyx in the extensions table so Joomla recognizes it.
     */
    private static function registerExtension(\Joomla\Database\DatabaseInterface $db): void
    {
        // Check if already registered
        $query = $db->getQuery(true)
            ->select('extension_id')
            ->from('#__extensions')
            ->where($db->quoteName('element') . ' = ' . $db->quote(self::NEW_NAME))
            ->where($db->quoteName('type') . ' = ' . $db->quote('template'));
        $existing = $db->setQuery($query)->loadResult();

        if ($existing) {
            self::log('MokoOnyx already registered in extensions table.');
            return;
        }

        // Get the old extension record as a base
        $query = $db->getQuery(true)
            ->select('*')
            ->from('#__extensions')
            ->where($db->quoteName('element') . ' = ' . $db->quote(self::OLD_NAME))
            ->where($db->quoteName('type') . ' = ' . $db->quote('template'));
        $oldExt = $db->setQuery($query)->loadObject();

        if (!$oldExt) {
            self::log('MokoCassiopeia extension record not found.', 'warning');
            return;
        }

        $newExt = clone $oldExt;
        unset($newExt->extension_id);
        $newExt->element = self::NEW_NAME;
        $newExt->name    = self::NEW_NAME;

        // Update manifest_cache with new name
        $cache = json_decode($newExt->manifest_cache, true);
        if (is_array($cache)) {
            $cache['name'] = self::NEW_DISPLAY;
            $newExt->manifest_cache = json_encode($cache);
        }

        $db->insertObject('#__extensions', $newExt, 'extension_id');
        self::log('Registered MokoOnyx in extensions table (ID: ' . $newExt->extension_id . ').');
    }

    /**
     * Log a message.
     */
    private static function log(string $message, string $priority = 'info'): void
    {
        $priorities = [
            'info'    => Log::INFO,
            'warning' => Log::WARNING,
            'error'   => Log::ERROR,
        ];

        Log::addLogger(
            ['text_file' => 'mokocassiopeia_bridge.log.php'],
            Log::ALL,
            ['mokocassiopeia_bridge']
        );

        Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokocassiopeia_bridge');
    }
}
