<?php
/**
 * @package     MefisAssetBridge
 * @subpackage  Joomla_System_Plugin
 *
 * @copyright   Copyright (C) 2025 Goran Usljebrka. All rights reserved.
 * @license     Proprietary — No resale or incorporation into other projects without explicit written permission. see LICENSE.txt
 */

namespace Mefi\Plugin\System\MefisAssetBridge;

defined('_JEXEC') or die;

use YOOtheme\Application;
use YOOtheme\Path;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Session\Session;
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;
use MefisAssetBridgeCore\Auth\AuthenticatedUser;
use Mefi\Component\MefisAssetBridge\Administrator\Traits\CoreBridgeTrait;

final class MefisAssetBridge extends CMSPlugin implements SubscriberInterface
{
    use CoreBridgeTrait;
    protected $app;
    protected $status           = true;
    protected $userDto          = null;
    protected $autoloadLanguage = true;

    /**
     * Returns an array of events this subscriber will listen to.
     *
     * @return  array
     *
     * @since   1.0.0
     */
    public static function getSubscribedEvents(): array
    {
        return [
            'onAfterInitialise'      => 'mefiOnAfterInitialise',
            'onBeforeCompileHead'    => 'mefiOnBeforeCompileHead',
            'onAfterDispatch'        => 'mefiOnAfterDispatch',
            'onAjaxMefisassetbridge' => 'onAjaxMefisassetbridge',
            'onUserAfterLogin'       => 'mefiOnUserAfterLogin',
        ];
    }

    // connect with yootheme
    public function mefiOnAfterInitialise()
    {

        // Check if YOOtheme Pro is loaded
        if (!class_exists(Application::class, false)) {
            return;
        }

        $app = Application::getInstance();
        $app->load('~/plugins/system/mefisassetbridge/src/modules/mefisassetbridge/bootstrap.php');

    }

    public function mefiOnAfterDispatch()
    {

        // Log::add('mefiOnAfterDispatch', Log::INFO, 'mefis-asset-bridge');
        // Get application
        $app = $this->app;

        // Check if we're in the admin area
        if (! $app->isClient('administrator')) {
            $this->status = false;
            return;
        }

        if ($app->input->getCmd('option', '') === 'com_installer') {
            $this->status = false;
            Log::add('Skipping MefisAssetBridge during com_installer request', Log::INFO, 'mefis-asset-bridge');
            return;
        }

        $user = $app->getIdentity();
        if ($user->id == 0) {
            $this->status = false;
            return;
        }

        $lang = $app->getLanguage();
        $lang->load('com_mefisassetbridge', JPATH_ADMINISTRATOR, null, true);

        // handshake if needed
        try {
            // Log::add('INITIATE HANDSHAKE PROTOCOL', Log::INFO, 'mefis-asset-bridge');
            $this->registerApplicationIfNeeded();
        } catch (\Exception $e) {
            $error_message = $e->getMessage();
            Log::add('Handshake failed', Log::ERROR, 'mefis-asset-bridge');
            // uncomment to output error message to log
            // Log::add('Handshake failed: ' . $error_message . "\n" . $e->getTraceAsString() . "\n", Log::ERROR, 'mefis-asset-bridge');

            // something is not right here, but we will not spam user with messages
            if (empty($error_message)) {
                $error_message = 'Mefi\'s Asset Bridge app failed to register with remote server. If this message persist, please contact support.';
            }
            $app->enqueueMessage($error_message, 'error');
        }
    }


    public function mefiOnUserAfterLogin($user)
    {

        $app      = $this->app;
        $params   = ComponentHelper::getParams('com_mefisassetbridge');
        $remember = $params->get('keep_signed_in', false);
        if ($remember == false) {
            return;
        }

        // handshake if needed
        try {
            // Log::add('INITIATE HANDSHAKE PROTOCOL AFTER LOGIN', Log::INFO, 'mefis-asset-bridge');
            $this->registerApplicationIfNeeded();
        } catch (\Exception $e) {
            $error_message = $e->getMessage();
            Log::add('Handshake failed', Log::ERROR, 'mefis-asset-bridge');

            // uncomment to output error message to log
            // Log::add('Handshake failed handshake (catch): ' . $error_message . "\n" . $e->getTraceAsString() . "\n", Log::ERROR, 'mefis-asset-bridge');
            // something is not right here, but we will not spam user with messages
            if (empty($error_message)) {
                $error_message = 'Mefi\'s Asset Bridge app failed to register with remote server. If this message persist, please contact support.';
            }
            $app->enqueueMessage($error_message, 'error');
        }

        // check cookies
        $cookieData = $this->getMefisCookie();

        if ($cookieData === null) {
            LOG::add('No cookies', Log::INFO, 'mefis-asset-bridge');
            return;
        }

        try {
            $userDto = $this->coreRefreshToken($cookieData['refresh_token']);
            $userDto->setMasterKey($cookieData['master_key']);

            $this->setDataToSession($userDto);
            if ($remember) {
                $this->setMefisCookie($userDto->getToken()->getRefreshToken() . '.' . $cookieData['master_key']);
            }

            $app->enqueueMessage('Welcome back ' . $userDto->getUser()->getName() . '!', 'success');

        } catch (\Exception $th) {
            LOG::add('Failed to refresh token from cookie', Log::ERROR, 'mefis-asset-bridge');
            // uncomment to output error message to log
            // LOG::add('Failed to refresh token from cookie: ' . $th->getMessage(), Log::INFO, 'mefis-asset-bridge');
            return;
        }

    }

    public function mefiOnBeforeCompileHead()
    {
        if (! $this->status) {
            return;
        }

        $app = $this->app;

        // Check if we're in the admin area
        if (! $app->isClient('administrator')) {
            return;
        }

        // Check if we're in the YOOtheme customizer
        $option = $app->input->get('option', '');
        $p      = $app->input->get('p', '');
        if ($option !== 'com_ajax' || $p !== 'customizer') {
            return;
        }

        try {

            $wa = $app->getDocument()->getWebAssetManager();

            $wa->registerAndUseStyle(
                'plg_system_mefisassetbridge.mefis-asset-bridge',
                'plg_system_mefisassetbridge/mefis-asset-bridge-v2.css',
                [],
                []
            );

            $authenticated = false;
            $_             = [];
            $_['APP_NAME'] = Text::_('COM_MEFISASSETBRIDGE');
            if ($this->isAuthenticated() === true) {

                $authenticated = true;

                // Get token from authenticated user
                $this->userDto = $this->getDataFromSession();

            }

            // Auth
            $_['auth.email.label'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_EMAIL_LABEL');
            $_['auth.email.placeholder'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_EMAIL_PLACEHOLDER');
            $_['auth.password.label'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_PASSWORD_LABEL');
            $_['auth.password.placeholder'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_PASSWORD_PLACEHOLDER');
            $_['auth.masterKey.label'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_MASTERKEY_LABEL');
            $_['auth.masterKey.placeholder'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_MASTERKEY_PLACEHOLDER');
            $_['auth.masterKey.description'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_MASTERKEY_DESCRIPTION');
            $_['auth.name.label'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_NAME_LABEL');
            $_['auth.name.placeholder'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_NAME_PLACEHOLDER');
            $_['auth.password.help'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_PASSWORD_HELP');
            $_['auth.passwordConfirm.label'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_PASSWORDCONFIRM_LABEL');
            $_['auth.passwordConfirm.placeholder'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_PASSWORDCONFIRM_PLACEHOLDER');
            $_['auth.acceptTerms'] = Text::sprintf('COM_MEFISASSETBRIDGE_V2_AUTH_ACCEPTTERMS', '<a href="https://asset-bridge.ugoran.com/page/terms" target="_blank" class="uk-button uk-button-text">', '</a>');
            $_['auth.button.register'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_BUTTON_REGISTER');
            $_['auth.button.signIn'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_BUTTON_SIGNIN');
            $_['auth.alreadyHaveAccount'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_ALREADYHAVEACCOUNT');
            $_['auth.loginInstead'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_LOGININSTEAD');
            $_['auth.tab.forgotPassword'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_TAB_FORGOTPASSWORD');

            // login
            $_['auth.login.title'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_LOGIN_TITLE');
            $_['auth.login.description'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_LOGIN_DESCRIPTION');

            // register
            $_['auth.register.title'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_REGISTER_TITLE');
            $_['auth.register.description'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_REGISTER_DESCRIPTION');
            $_['auth.register.description.1'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_REGISTER_DESCRIPTION_1');
            $_['auth.register.description.2'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_REGISTER_DESCRIPTION_2');
            $_['auth.register.description.3'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_REGISTER_DESCRIPTION_3');

            // forgot password
            $_['auth.reset.title'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_RESET_TITLE');
            $_['auth.reset.description'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_RESET_DESCRIPTION');
            $_['auth.reset.sendResetLink'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_RESET_SENDRESETLINK');
            $_['auth.reset.description'] = Text::_('COM_MEFISASSETBRIDGE_V2_AUTH_RESET_DESCRIPTION');

            // Assets
            $_['assets.toolbar.saveLayout'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSETS_TOOLBAR_SAVELAYOUT');
            $_['assets.toolbar.noLayout'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSETS_TOOLBAR_NOLAYOUT');
            $_['assets.toolbar.allCategories'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSETS_TOOLBAR_ALLCATEGORIES');
            $_['assets.toolbar.allOrigins'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSETS_TOOLBAR_ALLORIGINS');
            $_['assets.toolbar.allTypes'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSETS_TOOLBAR_ALLTYPES');
            $_['assets.toolbar.search.placeholder'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSETS_TOOLBAR_SEARCH_PLACEHOLDER');

            // Asset
            $_['asset.button.apply'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_BUTTON_APPLY');
            $_['asset.button.delete'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_BUTTON_DELETE');
            $_['asset.button.download'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_BUTTON_DOWNLOAD');
            $_['asset.button.share'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_BUTTON_SHARE');
            $_['asset.button.photo'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_BUTTON_PHOTO');
            $_['asset.button.replaceImage'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_BUTTON_REPLACEIMAGE');
            $_['asset.save.name.label'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_NAME_LABEL');
            $_['asset.save.name.placeholder'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_NAME_PLACEHOLDER');
            $_['asset.save.category.label'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_CATEGORY_LABEL');
            $_['asset.save.category.placeholder'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_CATEGORY_PLACEHOLDER');
            $_['asset.save.button.cancel'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_BUTTON_CANCEL');
            $_['asset.save.button.save'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_BUTTON_SAVE');
            $_['asset.save.button.saving'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_BUTTON_SAVING');
            $_['asset.save.title.createScreenshot'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_TITLE_CREATESCREENSHOT');
            $_['asset.save.title.createScreenshot.not.enabled'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_TITLE_CREATESCREENSHOT_NOT_ENABLED');
            $_['asset.save.title.createScreenshot.not.description'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_TITLE_CREATESCREENSHOT_NOT_ENABLED_DESCRIPTION');
            $_['asset.save.title.saveAsset'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_TITLE_SAVEASSET');
            $_['asset.save.device.desktop'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_DEVICE_DESKTOP');
            $_['asset.save.device.phone'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_DEVICE_PHONE');
            $_['asset.save.screenshotButton'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_SCREENSHOTBUTTON');
            $_['asset.save.screenshot.html2canvas'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_SCREENSHOT_HTML2CANVAS');
            $_['asset.save.screenshot.media'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_SCREENSHOT_MEDIA');
            $_['asset.save.loading'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_SAVE_LOADING');
            $_['asset.invalid.image'] = Text::_('COM_MEFISASSETBRIDGE_V2_ASSET_THUMBNAIL_REPLACE_INVALID_IMAGE');

            // Messages
            $_['msg.authenticated'] = Text::_('COM_MEFISASSETBRIDGE_V2_MSG_AUTHENTICATED');
            $_['msg.notAuthenticated'] = Text::_('COM_MEFISASSETBRIDGE_V2_MSG_NOTAUTHENTICATED');
            $_['msg.exit'] = Text::_('COM_MEFISASSETBRIDGE_V2_MSG_EXIT');
            $_['msg.asset.save.invalid.payload'] = Text::_('COM_MEFISASSETBRIDGE_V2_MSG_ASSET_SAVE_INVALID_PAYLOAD');
            $_['msg.success'] = Text::_('COM_MEFISASSETBRIDGE_V2_SUCCESS');
            $_['msg.fail'] = Text::_('COM_MEFISASSETBRIDGE_V2_FAIL');
            $_['msg.invalidKey'] = Text::_('COM_MEFISASSETBRIDGE_V2_MSG_INVALID_KEY');
            $_['msg.pleaseSignIn'] = Text::_('COM_MEFISASSETBRIDGE_V2_MSG_PLEASE_SIGN_IN');
            $_['msg.asset.load.error'] = Text::_('COM_MEFISASSETBRIDGE_V2_MSG_ASSET_LOAD_ERROR');

            // Miscelaneous
            $_['sort.dateDesc'] = Text::_('COM_MEFISASSETBRIDGE_V2_SORT_DATEDESC');
            $_['sort.dateAsc'] = Text::_('COM_MEFISASSETBRIDGE_V2_SORT_DATEASC');
            $_['sort.nameAsc'] = Text::_('COM_MEFISASSETBRIDGE_V2_SORT_NAMEASC');
            $_['sort.nameDesc'] = Text::_('COM_MEFISASSETBRIDGE_V2_SORT_NAMEDESC');


            // get params from com_mefisassetbridge component
            $p = ComponentHelper::getParams('com_mefisassetbridge');

            $jsParams = [
                'platform'      => 0,
                'authenticated' => $authenticated,
                'CSRF'          => Session::getFormToken(),
                '_'             => $_,
                'screenshot' => [
                    'media' => [
                        'status' => $p->get('mediacapture_status', 1),
                        'priority' => $p->get('mediacapture_priority', 0),
                    ],
                    'html2canvas' => [
                        'status' => $p->get('html2canvas_status', 1),
                        'priority' => $p->get('html2canvas_priority', 0),
                        'cache' => $p->get('html2canvas_cache', 1),
                    ]
                ],
                'assetThumbnail' => '../media/plg_system_mefisassetbridge/images/thumbnail.png',
                'assetMemoryThumbnail' => '../media/plg_system_mefisassetbridge/images/thumbnail-memory.png',
            ];

            $app->getDocument()->addScriptDeclaration("window.MefisAssetBridgeConfig = " . json_encode($jsParams) . ";");

        } catch (\Exception $e) {

            Log::add('Mefi\'s Asset Manager plugin Failed to initiate', Log::ERROR, 'mefis-asset-bridge');

            // uncomment to output error message to log
            // Log::add('Asset loading error: ' . $e->getMessage() . "\n" . $e->getTraceAsString() . "\n", Log::ERROR, 'mefis-asset-bridge');
            $app->enqueueMessage('Failed to load assets', 'error');
        }
    }

    public function onAjaxMefisassetbridge(Event $event)
    {
        if (! $this->status) {
            return;
        }

        $app = $this->app;

        $this->loadLanguage('com_mefisassetbridge');

        $input  = $app->input;
        $method = $app->input->server->get('REQUEST_METHOD', '', 'string');



        // load private/public keys and initialize handshake if needed
        try {
            // Log::add('INITIATE HANDSHAKE PROTOCOL ON AJAX CALL', Log::INFO, 'mefis-asset-bridge');
            $this->registerApplicationIfNeeded();
        } catch (\Exception $e) {
            $error_message = $e->getMessage();

            Log::add('Handshake failed', Log::ERROR, 'mefis-asset-bridge');

            // uncomment to output error message to log
            // Log::add('Handshake failed handshake (catch): ' . $error_message . "\n" . $e->getTraceAsString() . "\n", Log::ERROR, 'mefis-asset-bridge');

            // something is not right here, but we will not spam user with messages
            if (empty($error_message)) {
                $error_message = 'Mefi\'s Asset Bridge app failed to register with remote server. If this message persist, please contact support.';
            }
            $app->enqueueMessage($error_message, 'error');
            throw new \RuntimeException(Text::_('COM_MEFISASSETBRIDGE_APP_NOT_REGISTERED'), 401);
        }



        // Get task from request
        $task = $input->get('task', '', 'string');
        $taskGroup = false;
        $action = explode('.', $task);
        if( count($action) == 2) {
            $taskGroup = $action[0];
            $task = $action[1];
        }


        $publicTasks = ['login', 'register', 'forgotPassword', 'checkAuth'];

        $masterKey = null;
        if (in_array($task, $publicTasks, true) !== true) {
            // Check if user is authenticated
            if (! $this->isAuthenticated()) {
                throw new \RuntimeException(Text::_('COM_MEFISASSETBRIDGE_AJAX_ERROR_NOT_AUTHENTICATED'), 401);
                return;
            }
            // Get token from authenticated user
            $this->userDto = $this->getDataFromSession();
            // $token         = $this->userDto->getToken()->getToken();
            $masterKey     = $this->userDto->getMasterKey();
        }

        try {

            // For all tasks, dynamically load the handler class
            $handlerClass = __NAMESPACE__ . '\\Ajax\\' . ucfirst($task);

            // Check if the handler exists
            if (! class_exists($handlerClass)) {
                throw new \RuntimeException(sprintf(Text::_('COM_MEFISASSETBRIDGE_AJAX_ERROR_UNKNOWN_TASK_HANDLER'), $task));
            }

            // Create an instance of the handler class
            $handler = new $handlerClass();

            // Handle the request using the API factory directly
            $apiFactory = $this->getApiFactory();

            if($this->userDto) $apiFactory->setUser(new AuthenticatedUser($this->userDto));


            $apiFactory->setMasterKey($masterKey);

            // Execute the handler with the current request data
            $response = $handler->execute($apiFactory, $input);

            if (! $response) {
                throw new \RuntimeException(sprintf(Text::_('COM_MEFISASSETBRIDGE_AJAX_ERROR_HANDLER_NO_RESPONSE'), $task));
            }

            // Return the result directly - the handler already formats it correctly
            $event->setArgument('result', $response['data']);

        } catch (\Exception $e) {
            Log::add('Unexpected error during ajax call', Log::ERROR, 'mefis-asset-bridge');
            throw new \RuntimeException($e->getMessage(), 500, $e);
            // throw new \RuntimeException(sprintf(Text::_('COM_MEFISASSETBRIDGE_AJAX_ERROR_TASK_FAILED'), $e->getMessage()), 500, $e);
            return false;
        }
    }

}
