<?php

namespace Croogo\Extensions;

use Cake\Cache\Cache;
use Cake\Core\App;
use Cake\Core\Exception\MissingPluginException;
use Cake\Core\Plugin;
use Cake\Filesystem\Folder;
use Cake\Http\Exception\BadRequestException;
use Cake\ORM\TableRegistry;
use Cake\Routing\Router;
use Cake\Utility\Hash;
use Croogo\Core\PluginManager;
use Croogo\Extensions\Exception\MissingThemeException;
use InvalidArgumentException;
use UnexpectedValueException;

/**
 * CroogoTheme class
 *
 * @category Component
 * @package  Croogo.Extensions.Lib
 * @version  1.4
 * @since    1.4
 * @author   Fahad Ibnay Heylaal <contact@fahad19.com>
 * @license  http://www.opensource.org/licenses/mit-license.php The MIT License
 * @link     http://www.croogo.org
 */
class CroogoTheme
{

    /**
     * Constructor
     */
    public function __construct()
    {
    }

    /**
     * Get theme aliases (folder names)
     *
     * @return array
     */
    public function getThemes()
    {
        return PluginManager::instance()
            ->getPlugins('theme');
    }

    /**
     * Get the content of theme.json or composer.json file from a theme
     *
     * @param string $theme theme plugin name
     * @return array
     */
    public function getData($theme = null, $path = null)
    {
        $themeData = [
            'name' => $theme,
            'isFrontendTheme' => true,
            'isBackendTheme' => false,
            'regions' => [],
            'screenshot' => null,
            'settings' => [
                'templates' => [
                    'input' => '<input type="{{type}}" name="{{name}}"{{attrs}}/>',
                    'select' => '<select name="{{name}}"{{attrs}}>{{content}}</select>',
                    'selectMultiple' => '<select name="{{name}}[]" multiple="multiple"{{attrs}}>{{content}}</select>',
                    'radio' => '<input type="radio" name="{{name}}" value="{{value}}"{{attrs}}>',
                    'textarea' => '<textarea name="{{name}}"{{attrs}}>{{value}}</textarea>',
                ],
                'css' => [
                    'columnFull' => 'col-12',
                    'columnLeft' => 'col-lg-8',
                    'columnRight' => 'col-lg-4',
                    'container' => 'container',
                    'containerFluid' => 'container-fluid',
                    'dashboardFull' => 'col-12',
                    'dashboardLeft' => 'col-sm-6',
                    'dashboardRight' => 'col-sm-6',
                    'dashboardClass' => 'sortable-column',
                    'formInput' => 'input-block-level',
                    'imageClass' => 'img-fluid',
                    'row' => 'row',
                    'tableHeaderClass' => 'thead-light',
                    'tableClass' => 'table table-striped',
                    'tableContainerClass' => 'table-responsive',
                    'thumbnailClass' => 'img-thumbnail',
                    'tabContentClass' => 'tab-content',
                    'boxContainerClass' => 'card',
                    'boxHeaderClass' => 'card-header',
                    'boxBodyClass' => 'card-body',
                ],
                'iconDefaults' => [
                    'iconSet' => 'fas',
                    'prefix' => 'fa',
                    'size' => 'sm',
                ],
                'icons' => [
                    'attach' => 'paperclip',
                    'check-mark' => 'check',
                    'comment' => 'comment-alt',
                    'copy' => 'copy',
                    'create' => 'plus',
                    'delete' => 'trash',
                    'error-sign' => 'exclamation-sign',
                    'home' => 'home',
                    'info-sign' => 'info-circle',
                    'inspect' => 'search',
                    'link' => 'link',
                    'move-down' => 'chevron-down',
                    'move-up' => 'chevron-up',
                    'power-off' => 'power-off',
                    'power-on' => 'bolt',
                    'question-sign' => 'question-sign',
                    'read' => 'eye',
                    'refresh' => 'sync',
                    'resize' => 'arrows-alt',
                    'search' => 'search',
                    'success-sign' => 'ok-sign',
                    'translate' => 'flag',
                    'update' => 'edit',
                    'upload' => 'upload',
                    'warning-sign' => 'warning-sign',
                    'x-mark' => 'times',
                    'user' => 'user',
                    'key' => 'key',
                    'view' => 'eye'
                ],
                'prefixes' => [
                    '' => [
                        'helpers' => [
                            'Html' => [
                                'className' => 'Croogo/Core.Html',
                            ],
                            'Form' => [
                                'className' => 'Croogo/Core.Form',
                            ],
                            'Paginator' => [
                                'className' => 'Croogo/Core.Paginator',
                            ],
                        ],
                    ],
                    'admin' => [
                        'helpers' => [
                            'Html' => [
                                'className' => 'Croogo/Core.Html',
                            ],
                            'Form' => [
                                'className' => 'Croogo/Core.Form',
                            ],
                            'Paginator' => [
                                'className' => 'Croogo/Core.Paginator',
                            ],
                            'Breadcrumbs'
                        ],
                    ],
                ],
            ],
        ];

        $themeConfigs = [
            DS . 'config' . DS . 'theme.json',
            DS . 'webroot' . DS . 'theme.json',
        ];

        if ($theme) {
            if (empty($path)) {
                try {
                    $path = Plugin::path($theme);
                } catch (MissingPluginException $exception) {
                    throw new MissingThemeException([$theme], $exception->getCode(), $exception);
                }
            }
        } else {
            $path = ROOT . DS;
        }

        foreach ($themeConfigs as $themeManifestFile) {
            if (!file_exists($path . $themeManifestFile)) {
                continue;
            }

            $manifestFile = $path . $themeManifestFile;
        }

        if (file_exists($path . 'composer.json')) {
            $composerJson = $path . 'composer.json';
        }

        if (!isset($manifestFile) && !isset($composerJson)) {
            return [];
        }

        if (isset($manifestFile)) {
            $json = json_decode(file_get_contents($manifestFile), true);
            if ($json) {
                $themeData = Hash::merge($themeData, $json);
            }
        }

        if (isset($composerJson)) {
            $json = json_decode(file_get_contents($composerJson), true);
            if ($json) {
                $json['vendor'] = $json['name'];
                unset($json['name']);
                if (isset($manifestFile) && isset($themeData['description'])) {
                    unset($json['description']);
                }
                $themeData = Hash::merge($themeData, $json);
            }
        }

        return $themeData;
    }

    /**
     * Activate theme $alias
     *
     * @param $theme theme alias
     * @return mixed On success Setting::$data or true, false on failure
     */
    public function activate($theme, $type = 'theme')
    {
        if (!in_array($type, ['theme', 'admin_theme'])) {
            throw new BadRequestException('Invalid theme type');
        }
        $themes = $this->getThemes();
        if (!$this->getData($theme, isset($themes[$theme]) ? $themes[$theme] : null)) {
            return false;
        }

        Cache::clearAll();
        (new PluginManager())->activate($theme);
        $settings = TableRegistry::get('Croogo/Settings.Settings');

        return $settings->write('Site.' . $type, $theme);
    }

    /**
     * Delete theme
     *
     * @param string $alias Theme alias
     * @return bool true when successful, false or array or error messages when failed
     * @throws InvalidArgumentException
     * @throws UnexpectedValueException
     */
    public function delete($alias)
    {
        if (empty($alias)) {
            throw new InvalidArgumentException(__d('croogo', 'Invalid theme'));
        }
        $paths = App::path('Plugin', $alias);
        $paths = array_map(function ($path) use ($alias) {
            return $path . $alias;
        }, $paths);
        $folder = new Folder;

        foreach ($paths as $path) {
            if (!file_exists($path)) {
                continue;
            }

            $themeManifest = $path . '/config/theme.json';
            if (!file_exists($themeManifest)) {
                continue;
            }

            if (is_link($path)) {
                return unlink($path);
            } elseif (is_dir($path)) {
                if ($folder->delete($path)) {
                    return true;
                } else {
                    return $folder->errors();
                }
            }
        }
        throw new UnexpectedValueException(__d('croogo', 'Theme %s not found', $alias));
    }

    /**
     * Helper method to retrieve given $theme settings
     *
     * @param string $theme Theme name
     * @return array Theme configuration data
     */
    public static function config($theme = null)
    {
        static $croogoTheme = null;
        static $themeData = [];
        if ($croogoTheme === null) {
            $croogoTheme = new CroogoTheme();
        }

        if (empty($themeData[$theme])) {
            $data = $croogoTheme->getData($theme);
            $request = Router::getRequest();
            if ($request) {
                $prefix = $request->getParam('prefix');
                if (isset($data['settings']['prefixes'][$prefix]['css'])) {
                    $data['settings']['css'] = Hash::merge(
                        $data['settings']['prefixes'][$prefix]['css'],
                        $data['settings']['css']
                    );
                }
            }
            $themeData[$theme] = $data;
        }

        return $themeData[$theme];
    }
}
