<?php

    namespace Icing\Model\Behavior;

    use Cake\Cache\Cache;
    use Cake\Datasource\EntityInterface;
    use Cake\Event\EventInterface;
    use Cake\ORM\Behavior;
    use Cake\ORM\Table;

    /**
     * VirtualStoredBehavior: This behavior will automatically update the entity with the new values that persisted on
     * the database that are virtually calculated via the database. The problem that is solved with this behavior is
     * when you have a virtual field that is calculated on the database, but that value available to the entity on
     * saving the entity because they are not queried from the updated row. This behavior will look to read those changes.
     *
     */
    class VirtualStoredBehavior extends Behavior
    {

        protected array $_defaultConfig = [
            'priority' => 1
        ];

        public function initialize(array $config): void
        {
            if (!isset($this->_config['fields'])) {
                $this->_config['fields'] = $this->_getVirtualFields();
            }
        }

        public function afterSave(EventInterface $e, EntityInterface $entity, $options): void
        {
            if (!empty($this->_config['fields'])) {
                $this->_loadInto($e->getSubject(), $entity, $this->_config['fields']);
            }
        }

        protected function _loadInto(Table $table, EntityInterface $entity, array $fields): EntityInterface
        {
            $pk = $table->getPrimaryKey();

            $values = $table->find()
                ->where([$table->aliasField($pk) => $entity->get($pk)])
                ->select($fields)
                ->disableHydration()
                ->first();

            foreach ($values as $field => $value) {
                if ($entity->get($field) !== $value) {
                    $entity->set($field, $value);
                }
            }

            return $entity;
        }

        protected function _getVirtualFields()
        {
            $table = $this->_table->getTable();
            return Cache::remember(
                "$table-virtual-fields",
                function () {
                    $virtualFields = $this->_table->getConnection()
                        ->execute(
                            "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = :table AND EXTRA like '%GENERATED%'",
                            ['table' => $this->_table->getTable()]
                        )
                        ->fetchAll();
                    return collection($virtualFields)->unfold()->toList();
                },
                '_cake_model_'
            );
        }

    }
