<?php

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

use apexl\Io\exceptions\RecordNotFoundException;
use apexl\Io\modules\menu\entities\menuLinkEntity;
use apexl\Io\operators\entityDatabaseOperator;
use apexl\Vault\exceptions\ExecutionException;
use app\vendor\apexl\vault\src\Vault\exceptions\VaultException;
use Exception;

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

    /**
     * @throws RecordNotFoundException
     */
    public function loadByName($name, $skipAccessCheck = false): array
    {
        return $this->loadBy('name', $name, $skipAccessCheck);
    }

    /**
     * @throws Exception
     */
    public function store($data): bool|array
    {
        $meta = null;
        //first, are we inserting or updating
        if (empty($data)) {
            throw new Exception("Cannot store empty entity data");
        }
        //store and strip metadata if it is available. Allows entities to contain processing and maintain easy store methods.
        if (isset($data['_metadata'])) {
            //we need to store this for update methods
            $meta = $data['_metadata'];
            unset($data['_metadata']);
        }
        $entity = [];
        if (isset($data[$this->primaryKey]) && !empty($data[$this->primaryKey])) {
            $entity = $this->load($data[$this->primaryKey]);
        }
        $links = null;
        if (isset($data['links'])) {
            $links = $data['links'];
            unset($data['links']); //stop fatal errors
        }

        if (!empty($entity['data'])) {
            //not empty? we have a record, so update.
            $this->vault->update($this->dbTable)->fields($data)->where(
                $this->primaryKey,
                $data[$this->primaryKey]
            )->execute();
            $this->storeLinks($links, $data[$this->primaryKey]);

            //assume we might have updated or added links which need to be reloaded
            return ['updateEntityData' => true, 'data' => $this->load($data[$this->primaryKey])['data']];
        } else {
            //we're inserting to force an entity update in case we have a new primary key.
            $this->vault->insert($this->dbTable)->fields($data)->execute();
            $lastStored = $this->getLastNewRecord();
            $this->storeLinks($links, $lastStored[$this->primaryKey]);
            //force the local entity to be updated with the new data (id etc.)
            $updatedData = $this->load($lastStored[$this->primaryKey])['data'];
            if ($meta) {
                $updatedData['_metadata'] = $meta;
            }

            return ['updateEntityData' => true, 'data' => $updatedData];
        }
    }

    /**
     * @throws VaultException
     * @throws ExecutionException
     */
    public function load($id, $skipAccessCheck = false): array
    {
        $entity = parent::load($id, $skipAccessCheck);
        $links = (new menuLinkEntity())->getMenuLinks($entity['data']["id"]);
        $entity['data']['links'] = (object) $links;

        return ['updateEntityData' => true, 'data' => $entity['data']];
    }

    /**
     * @param $links
     * @param $menuId
     */
    protected function storeLinks($links, $menuId): void
    {
        if ($links) {
            foreach ($links as $link) {
                $link->mid = $menuId;
                $link->store();
            }
        }
    }
}
