<?php

/**
 * @package         Advanced Custom Fields
 * @version         2.8.9 Pro
 * 
 * @author          Tassos Marinos <info@tassos.gr>
 * @link            http://www.tassos.gr
 * @copyright       Copyright © 2021 Tassos Marinos All Rights Reserved
 * @license         GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/

defined('_JEXEC') or die('Restricted access');

use NRFramework\Extension;
use Joomla\Registry\Registry;
use Joomla\Filesystem\Path;
use Joomla\CMS\Factory;

class ACFMigrator
{
    /**
     * Application object.
     * 
     * @var  object
     */
    private $app;

    /**
     * Database object.
     * 
     * @var  object
     */
    private $db;

    /**
     * Database object.
     * 
     * @var  object
     */
    private $dbDestination;

    /**
     * The currently installed version.
     * 
     * @var  string
     */
    private $installedVersion;
    
    /**
     * Class constructor
     *
     * @param string $installedVersion  The current extension version
     */
    public function __construct($installedVersion, $dbSource = null, $dbDestination = null)
    {
        $this->app = Factory::getApplication();
        $this->db = $dbSource ? $dbSource : Factory::getDbo();
        $this->dbDestination = $dbDestination ? $dbDestination : Factory::getDbo();
        $this->installedVersion = $installedVersion;
    }

    public function do()
    {
        if (version_compare($this->installedVersion, '1.3.0', '<='))
        {
            $this->uploadFieldConvertBasenameToRelativePaths();
        }
        
        
        if (version_compare($this->installedVersion, '2.1', '<='))
        {
            $this->migratePublishingRulesToConditionBuilder();
        }
        

        if (version_compare($this->installedVersion, '2.7.2', '<='))
        {
            $this->updateMapMaximumMarkers();
        }

        if (version_compare($this->installedVersion, '2.7.3', '<='))
        {
            
            $this->updateAddressDefaultMarkerImage();
            
            $this->updateMapValue();
        }
    }

    
    /**
     * ACF Address used the default marker image from the ACF OSM custom field.
     * 
     * Old path: media/plg_fields_acfosm/img/marker.png
     * 
     * However, since the ACF OSM map is now removed from the build, we must update it
     * to point to the same file but from the ACF Address custom field.
     * 
     * New value: media/plg_fields_acfaddress/img/marker.png
     */
    public function updateAddressDefaultMarkerImage()
    {
        // Get all custom fields.
        $db = $this->db;

        $query = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName('#__fields'))
            ->where($db->quoteName('type') . ' = ' . $db->q('acfaddress'));

        $db->setQuery($query);

        if (!$fields = $db->loadObjectList())
        {
            return;
        }

        foreach ($fields as $key => $field)
        {
            if (!isset($field->fieldparams))
            {
                continue;
            }

            $params = json_decode($field->fieldparams);

            $markerImage = $params->marker_image;

            if ($markerImage !== 'media/plg_fields_acfosm/img/marker.png')
            {
                continue;
            }

            $params->marker_image = 'media/plg_fields_acfaddress/img/marker.png';

            // Update field `param` with new value
            $dbDestination = $this->dbDestination;

            $query = $dbDestination->getQuery(true)
                ->update($dbDestination->quoteName('#__fields'))
                ->set($dbDestination->quoteName('fieldparams') . '=' . $dbDestination->quote(json_encode($params)))
                ->where($dbDestination->quoteName('id') . '=' . $dbDestination->quote($field->id));

            $dbDestination->setQuery($query);
            $dbDestination->execute();
        }
    }
    

    public function updateMapValue()
    {
        // Get all custom fields.
        $db = $this->db;

        $query = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName('#__fields'))
            ->where($db->quoteName('type') . ' = ' . $db->q('acfmap'));

        $db->setQuery($query);

        if (!$fields = $db->loadObjectList())
        {
            return;
        }

        $count = 0;

        foreach ($fields as $key => $field)
        {
            // Find now all field values
            $query = $db->getQuery(true)
                ->select('*')
                ->from($db->quoteName('#__fields_values'))
                ->where($db->quoteName('field_id') . ' = ' . $field->id);

            $db->setQuery($query);

            if (!$values = $db->loadObjectList())
            {
                return;
            }

            foreach ($values as $value)
            {
                if (!$decoded_value = json_decode($value->value, true))
                {
                    continue;
                }

                if (!isset($decoded_value['markers']))
                {
                    continue;
                }

                $newValue = $decoded_value['markers'];

                $query = $db->getQuery(true);
                
                $fields = [
                    $db->quoteName('value') . ' = ' . $db->q($newValue)
                ];
                
                $conditions = [
                    $db->quoteName('field_id') . ' = ' . $value->field_id, 
                    $db->quoteName('item_id') . ' = ' . $db->quote($value->item_id),
                    $db->quoteName('value') . ' = ' . $db->quote($value->value)
                ];
                
                $query->update($db->quoteName('#__fields_values'))->set($fields)->where($conditions);
                
                $db->setQuery($query);
                $db->execute();

                $count++;
            }
        }

        return $count;
    }

    public function updateMapMaximumMarkers()
    {
        // Get all custom fields.
        $db = $this->db;

        $query = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName('#__fields'))
            ->where($db->quoteName('type') . ' = ' . $db->q('acfmap'));

        $db->setQuery($query);

        if (!$fields = $db->loadObjectList())
        {
            return;
        }

        foreach ($fields as $key => $field)
        {
            if (!isset($field->fieldparams))
            {
                continue;
            }

            $params = json_decode($field->fieldparams);

            if (isset($params->maximum_markers))
            {
                continue;
            }

            $params->maximum_markers = 0;

            // Update field `param` with new value
            $dbDestination = $this->dbDestination;

            $query = $dbDestination->getQuery(true)
                ->update($dbDestination->quoteName('#__fields'))
                ->set($dbDestination->quoteName('fieldparams') . '=' . $dbDestination->quote(json_encode($params)))
                ->where($dbDestination->quoteName('id') . '=' . $dbDestination->quote($field->id));

            $dbDestination->setQuery($query);
            $dbDestination->execute();
        }
    }

    
    public function migratePublishingRulesToConditionBuilder()
    {
        // Get all custom fields.
        $db = $this->db;

        $query = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName('#__fields'));

        $db->setQuery($query);

        if (!$fields = $db->loadObjectList())
        {
            return;
        }

        foreach ($fields as $key => $field)
        {
            if (!isset($field->params))
            {
                continue;
            }

            $params = new Registry($field->params);

            \NRFramework\Conditions\Migrator::run($params);

            if (!isset($params['rules']) || empty($params['rules']))
            {
                continue;
            }

            // Update field `param` with new value
            $dbDestination = $this->dbDestination;

            $query = $dbDestination->getQuery(true)
                ->update($dbDestination->quoteName('#__fields'))
                ->set($dbDestination->quoteName('params') . '=' . $dbDestination->quote(json_encode($params)))
                ->where($dbDestination->quoteName('id') . '=' . $dbDestination->quote($field->id));

            $dbDestination->setQuery($query);
            $dbDestination->execute();
        }
    }
    

    /**
     * Since v1.3.0, the File Upload field is no longer storing just the file's name in the database but the full relative path to Joomla's root.
     * 
     * This migration tasks, loops through all file values in the database and prepends to them the Upload Folder as configured in the Field settings.
     * 
     * Previous value: myAwesomePhoto.jpg
     * New value: media/myimages/myAwesomePhoto.jpg
     *
     * @return mixed    Null when the migrationt ask doesn't run, Integer when the migration run.
     */
    public function uploadFieldConvertBasenameToRelativePaths()
    {
        // Get all upload fields.
        $db = $this->db;

        $query = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName('#__fields'))
            ->where($db->quoteName('type') . ' = ' . $db->q('acfupload'));

        $db->setQuery($query);

        if (!$fields = $db->loadObjectList())
        {
            return;
        }

        $count = 0;

        foreach ($fields as $key => $field)
        {
            if (!isset($field->fieldparams))
            {
                continue;
            }

            $params = json_decode($field->fieldparams);

            if (!isset($params->upload_folder))
            {
                continue;
            }

            $upload_folder = $params->upload_folder;

            // Find now all field values
            $query = $db->getQuery(true)
                ->select('*')
                ->from($db->quoteName('#__fields_values'))
                ->where($db->quoteName('field_id') . ' = ' . $field->id);

            $db->setQuery($query);

            if (!$values = $db->loadObjectList())
            {
                return;
            }

            foreach ($values as $value)
            {
                if (!isset($value->value) || empty($value->value))
                {
                    continue;
                }

                // If the path has a slash, consider it as already fixed.
                if (strpos($value->value, DIRECTORY_SEPARATOR) !== false)
                {
                    continue;
                }

                $newValue = trim(Path::clean($upload_folder . '/' . $value->value));

                $query = $db->getQuery(true);
                
                $fields = [
                    $db->quoteName('value') . ' = ' . $db->q($newValue)
                ];
                
                $conditions = [
                    $db->quoteName('field_id') . ' = ' . $value->field_id, 
                    $db->quoteName('item_id') . ' = ' . $db->quote($value->item_id),
                    $db->quoteName('value') . ' = ' . $db->quote($value->value)
                ];
                
                $query->update($db->quoteName('#__fields_values'))->set($fields)->where($conditions);
                
                $db->setQuery($query);
                $db->execute();

                $count++;
            }
        }

        return $count;
    }
}
