<?php

namespace apexl\Io\modules\component\entities\operators;

use apexl\Io\modules\component\entities\componentEntity;
use apexl\Io\modules\component\entities\componentTypeEntity;
use apexl\Io\modules\component\entities\propertyEntity;
use apexl\Io\operators\entityDatabaseOperator;

class componentOperator extends entityDatabaseOperator
{
    public function __construct($table, $primaryKey = 'id')
    {
        parent::__construct($table, $primaryKey);
    }

    public function load($id, $skipAccessCheck = false)
    {
        //first we need to load everything for the component
        $entity = $this->vault->select($this->dbTable)
            ->fields()
            ->where($this->primaryKey, $id)
            ->execute()->fetchAssoc();

        //next, we get all properties
        $props = (new propertyEntity())->getByCid($entity['id']);

        //get any components, we need to load the appropriate namespace to be able to do this.
        //Allows for base component entity to be extended and not have to extend the base operator every time.
        $type = (new componentTypeEntity())->load($entity['type'], $skipAccessCheck);
        $componentType = $type->namespace.'\\'.$entity['type']."Entity";
        $components = (new $componentType())->getByPid($entity['id']);

        //then we need to take any ids for props and components and load those entities
        return ['updateEntityData' => true, 'data' => ["data" => $entity, "props" => $props, "components" => $components]];
    }

    public function getByPid($pid)
    {
        $components = $this->vault->select($this->dbTable)
            ->fields('id')
            ->where('pid', $pid)
            ->execute()->fetchAll();

        //return a loaded array
        $loadedComponents = [];
        if (!empty($components)) {
            foreach ($components as $component) {
                $loadedProps[$component->id] = (new componentEntity())->load($component->id);
            }
        }
        return $loadedComponents;
    }

    /**
     * Override apexl\Io\operators\entityDatabaseOperator::store;
     * @param $data
     * @param $props
     * @param $components
     * @return array|bool
     * @throws \Exception
     */
    public function store($data)
    {
        //first, are we inserting or updating
        if (empty($data)) {
            throw new \Exception("Cannot store empty entity data");
        }
        $entity = [];
        if (isset($data['entity'][$this->primaryKey]) && !empty($data['entity'][$this->primaryKey])) {
            $entity = $this->load($data['entity'][$this->primaryKey]);
        }
        if (isset($entity['data']) && !empty($entity['data'])) {
            //not empty? we have a record, so update.
            $this->vault->update($this->dbTable)->fields($data['entity'])->where($this->primaryKey, $data['entity'][$this->primaryKey])->execute();
            $this->upSertProperties($this->primaryKey, $data['props'])->upsertComponents($this->primaryKey, $data['components']);
            return true;
        } else {
            //we're inserting to force an entity update in case we have a new primary key.
            $this->vault->insert($this->dbTable)->fields($data['entity'])->execute();
            $lastStored = $this->getLastNewRecord();
            $this->upsertProperties($lastStored[$this->primaryKey], $data['props'])->upsertComponents($lastStored[$this->primaryKey], $data['components']);
            //force the local entity to be updated with the new data (id etc)
            return ['updateEntityData' => true, 'data' => $this->load($lastStored[$this->primaryKey])];
        }
    }

    /**
     * Store Component Properties
     * @param $componentID
     * @param $props
     * @return $this
     */
    private function upsertProperties($componentID, $props)
    {
        foreach ($props as $property) {
            //Force the component ID to be sure
            $property->cid = $componentID;
            $property->store();
        }
        return $this;
    }

    /**
     * Store Child components (This will be recursive).
     * @param $componentID
     * @param $components
     * @return $this
     */
    private function upsertComponents($componentID, $components)
    {
        foreach ($components as $component) {
            //Force the component ID to be sure
            $component->pid = $componentID;
            $component->store();
        }
        return $this;
    }
}
