<?php

/**
 * @package     VP Prime Framework
 *
 * @author      Abhishek Das <info@virtueplanet.com>
 * @link        https://www.virtueplanet.com
 * @copyright   Copyright (C) 2012-2025 Virtueplanet Services LLP. All Rights Reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Virtueplanet\Plugin\System\Prime\Helper;

use Exception;
use Joomla\CMS\Factory;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Path;
use Joomla\CMS\Uri\Uri;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Google Font Helper class of VP Prime Framework.
 */
abstract class GoogleFontHelper
{
    /**
     * Google Fonts API URL.
     *
     * @var string
     */
    private const API_URL = 'https://www.googleapis.com/webfonts/v1/webfonts';

    /**
     * Added fonts.
     *
     * @var array
     */
    private static $addedFonts = [];

    /**
     * Fonts.
     *
     * @var array
     */
    private static $fonts = [];

    /**
     * Update Google Fonts file.
     *
     * @param string $file File path
     *
     * @return bool
     *
     * @throws Exception
     */
    public static function update($file)
    {
        $filePath = Path::clean(JPATH_ROOT . '/' . $file);

        if (!is_file($filePath)) {
            throw new Exception('Font file does not exists.');
        }

        $apiKey = Factory::getApplication()->get('google_api_key', '');
        $url    = str_replace(' ', '%20', trim(self::API_URL)) . '?key=' . $apiKey;
        $data   = file_get_contents($url);

        if (empty($data)) {
            throw new Exception('Google API returned empty response.');
        }

        if (!File::write($filePath, $data)) {
            throw new Exception($file . ' file is not writable.');
        }

        return true;
    }

    /**
     * Get items from Google Fonts file.
     *
     * @param string $file File path
     *
     * @return array
     *
     * @throws Exception
     */
    public static function getItems($file)
    {
        $filePath = Path::clean(JPATH_ROOT . '/' . $file);

        if (!is_file($filePath)) {
            throw new Exception('Font file does not exists.');
        }

        $content = file_get_contents($filePath);

        if (empty($content)) {
            throw new Exception('No contents found in ' . $file);
        }

        $data = json_decode($content, true);

        if (empty($data) || !isset($data['items'])) {
            throw new Exception('No valid contents found in ' . $file);
        }

        return $data['items'];
    }

    /**
     * Get item from Google Fonts file.
     *
     * @param string $file     File path
     * @param string $fontName Font name
     *
     * @return array|bool
     *
     * @throws Exception
     */
    public static function getItem($file, $fontName)
    {
        $key = md5('file:' . $file . '.name:' . $fontName);

        if (array_key_exists($key, self::$fonts)) {
            return self::$fonts[$key];
        }

        self::$fonts[$key] = false;

        $filePath = Path::clean(JPATH_ROOT . '/' . $file);

        if (!is_file($filePath)) {
            throw new Exception('Font file does not exists.');
        }

        $fonts = self::getItems($file);

        if (empty($fonts)) {
            throw new Exception('Fonts data not found in ' . $file);
        }

        foreach ($fonts as $font) {
            if ($font['family'] == $fontName) {
                self::$fonts[$key] = $font;
                break;
            }
        }

        return self::$fonts[$key];
    }

    /**
     * Get font variants.
     *
     * @param string $fontName Font name
     *
     * @return array|bool
     */
    public static function addFontFamily($fontName, $fontVariant)
    {
        if (empty($fontName)) {
            return false;
        }

        if (isset(self::$addedFonts[$fontName])) {
            $family = self::$addedFonts[$fontName];
        } else {
            $family = new \stdClass();

            $family->name     = $fontName;
            $family->variants = [];
        }

        if (is_array($fontVariant)) {
            foreach ($fontVariant as $variant) {
                if (!in_array($variant, $family->variants)) {
                    $family->variants[] = $variant;
                }
            }
        } elseif (!in_array($fontVariant, $family->variants)) {
            $family->variants[] = $fontVariant;
        }

        self::$addedFonts[$fontName] = $family;
    }

    /**
     * Get added fonts url.
     *
     * @return array|bool
     */
    public static function getAddedFontsUrl()
    {
        if (empty(self::$addedFonts)) {
            return false;
        }

        $families = [];

        foreach (self::$addedFonts as $font) {
            $hasItalic    = false;
            $hasWeight    = false;
            $variantQuery = '';

            if (!empty($font->variants)) {
                foreach ($font->variants as $variant) {
                    if (strpos($variant, 'italic') !== false) {
                        $hasItalic = true;
                    }

                    if (preg_match('~[0-9]+~', $variant) && preg_replace('/\D+/', '', $variant) != 400) {
                        $hasWeight = true;
                    }
                }

                $varKey    = '';
                $varValues = [];

                if ($hasItalic && $hasWeight) {
                    $varKey = 'ital,wght';
                } elseif ($hasItalic) {
                    $varKey = 'ital';
                } elseif ($hasWeight) {
                    $varKey = 'wght';
                }

                foreach ($font->variants as $variant) {
                    $isItalic = 0;
                    $isWeight = 400;

                    if (strpos($variant, 'italic') !== false) {
                        $isItalic = 1;
                    }

                    if (preg_match('~[0-9]+~', $variant)) {
                        $isWeight = preg_replace('/\D+/', '', $variant);
                    }

                    if ($hasItalic && $hasWeight) {
                        $varValues[] = $isItalic . ',' . $isWeight;
                    } elseif ($hasItalic) {
                        $varValues[] = (string) $isItalic;
                    } elseif ($hasWeight) {
                        $varValues[] = (string) $isWeight;
                    }
                }

                if (!empty($varKey) && !empty($varValues)) {
                    $varValues    = array_unique($varValues);
                    $variantQuery = ':' . $varKey . '@' . implode(';', $varValues);
                }
            }

            $families[] = urlencode($font->name) . $variantQuery;
        }

        $baseUrl = '//fonts.googleapis.com/css2';
        $scheme  = Uri::getInstance()->getScheme();

        if ($scheme == 'http' || $scheme == 'https') {
            $baseUrl = $scheme . ':' . $baseUrl;
        }

        $url = $baseUrl;

        foreach ($families as $key => $family) {
            $url .= ($key === 0 ? '?' : '&') . 'family=' . $family;
        }

        $url .= '&display=swap';

        return $url;
    }

    /**
     * Get font info.
     *
     * @param string $fontName    Font name
     * @param string $fontVariant Font variant
     * @param string $file        File path
     *
     * @return \stdClass|bool
     */
    public static function getFontInfo($fontName, $fontVariant, $file)
    {
        $font = self::getItem($file, $fontName);

        if (!$font) {
            return false;
        }

        $category    = $font['category'];
        $categoryMap = [
            'sans-serif'  => 'sans-serif',
            'serif'       => 'serif',
            'monospace'   => 'monospace',
            'handwriting' => 'cursive',
            'display'     => 'cursive'
        ];

        $info = new \stdClass();

        $info->family = '"' . $fontName . '", ' . (array_key_exists($category, $categoryMap) ? $categoryMap[$category] : 'sans-serif');
        $info->weight = '400';
        $info->style  = 'normal';

        if (preg_match('~[0-9]+~', $fontVariant)) {
            $info->weight = preg_replace('/\D+/', '', $fontVariant);
        }

        if (strpos($fontVariant, 'italic') !== false) {
            $info->style = 'italic';
        }

        return $info;
    }
}
