<?php

namespace Src\Modules\JsonObjects\Application;

use Src\Common\Application\TraitDomain;
use Src\Common\Application\ResponsesCode;
use Src\Modules\JsonObjects\Interfaces\Application\IDomain;
use Src\Modules\JsonObjects\Interfaces\Application\IValidator;
use Src\Modules\JsonObjects\Interfaces\Dto\IFactory as IDtoFactory;
use Src\Modules\JsonObjects\Interfaces\Dto\Item\IResourceItem;
use Src\Modules\JsonObjects\Interfaces\Infrastructure\IItemPersistLayer;
use Src\Modules\JsonObjects\Interfaces\Infrastructure\IItemStorage;
use Src\Modules\JsonObjects\Interfaces\Application\IDataBuilder;
use Src\Modules\JsonObjects\Interfaces\Infrastructure\IDirsStorage;
use Src\Modules\JsonObjects\Interfaces\Infrastructure\IDirsPersistLayer;
use Src\Modules\JsonObjects\Interfaces\Dto\Dir\IResource as IDirResource;
use Illuminate\Support\Facades\Log;

class Domain implements IDomain
{

    use TraitDomain;

    protected IValidator $validator;

    protected IDtoFactory $dtoFactory;

    protected IItemPersistLayer $persistLayer;

    protected IItemStorage $storage;

    protected IResourceItem $item;

    protected IDataBuilder $dataBuilder;

    protected IDirsStorage $dirsStorage;

    protected IDirsPersistLayer $dirsPersistLayer;

    protected IDirResource $dirResource;

    public function setValidator(IValidator $validator): void
    {
        $this->validator = $validator;
    }

    public function setDtoFactory(IDtoFactory $factory): void
    {
        $this->dtoFactory = $factory;
    }

    public function setPersistLayer(IItemPersistLayer $layer): void
    {
        $this->persistLayer = $layer;
    }

    public function setStorage(IItemStorage $storage): void
    {
        $this->storage = $storage;
    }

    public function setDataBuilder(IDataBuilder $dataBuilder): void
    {
        $this->dataBuilder = $dataBuilder;
    }

    public function setDirStorage(IDirsStorage $storage): void
    {
        $this->dirsStorage = $storage;
    }

    public function setDirsPersistLayer(IDirsPersistLayer $layer): void
    {
        $this->dirsPersistLayer = $layer;
    }

    public function createObject(array $data): bool
    {
        $this->item = $this->dtoFactory->getItemFactory()->createResource();
        if ($this->validator->createObject($data)) {
            $cleanData = $this->validator->getCleanData();
            $fullData = $this->dataBuilder->build($cleanData);
            $item = $this->dtoFactory->getItemFactory()->createPersist();
            $item->load($fullData);
            $this->item->load($item->getAttributes());
            if ($this->item->validate()) {
                $this->persistLayer->create($item);
                $this->item->getObject()->onSave();
                return true;
            }
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        } else {
            $this->errors = $this->validator->getErrors();
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        }
    }

    public function editObject(array $data): bool
    {
        $this->item = $this->dtoFactory->getItemFactory()->createResource();
        if ($this->validator->editObject($data)) {
            $cleanData = $this->validator->getCleanData();
            $itemData = $this->storage->getById($cleanData['id']);
            $item = $this->dtoFactory->getItemFactory()->createPersist();
            $item->load($itemData);
            $item->update($cleanData);
            $this->item->load($item->getAttributes());
            if ($this->item->validate()) {
                $this->persistLayer->update($item);
                $this->item->getObject()->onSave();
                return true;
            }
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        } else {
            $this->errors = $this->validator->getErrors();
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        }
    }

    public function renameObject(array $data): bool
    {
        $this->item = $this->dtoFactory->getItemFactory()->createResource();
        if ($this->validator->renameObject($data)) {
            $cleanData = $this->validator->getCleanData();
            $itemData = $this->storage->getById($cleanData['id']);
            $item = $this->dtoFactory->getItemFactory()->createPersist();
            $item->load($itemData);
            $item->update($cleanData);
            $this->persistLayer->update($item);
            $this->item->load($item->getAttributes());
            return true;
        } else {
            $this->errors = $this->validator->getErrors();
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        }
    }

    public function deleteObject(array $data): bool
    {
        if ($this->validator->deleteObject($data)) {
            $cleanData = $this->validator->getCleanData();
            $itemData = $this->storage->getById($cleanData['id']);
            if (!$itemData['disabled']) {
                $this->persistLayer->delete($cleanData['id']);
                return true;
            }
            return false;
        } else {
            $this->errors = $this->validator->getErrors();
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        }
    }

    public function deleteDir(array $data): bool
    {
        if ($this->validator->deleteDir($data)) {
            $cleanData = $this->validator->getCleanData();
            $dirIds = $this->dirsStorage->getIdsInDir($cleanData['id']);
            $dirIds[] = $cleanData['id'];
            $this->persistLayer->deleteInDirs($dirIds);
            $this->_deleteDir($data);
            return true;
        } else {
            $this->errors = $this->validator->getErrors();
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        }
    }

    public function createDir(array $data): bool
    {
        $this->dirResource = $this->dtoFactory->getDirFactory()->createResource();
        if ($this->validator->createDir($data)) {
            try {
                $cleanData = $this->validator->getCleanData();
                $dirData = $this->dataBuilder->buildDirData($cleanData);
                $dirPersist = $this->dtoFactory->getDirFactory()->createPersist();
                $dirPersist->load($dirData);
                $this->dirsPersistLayer->newDir($dirPersist);
                $this->dirResource->load($dirPersist->getAttributes());
                return true;
            } catch (\Throwable $e) {
                Log::error($e);
                return false;
            }
        } else {
            $this->errors = $this->validator->getErrors();
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        }
    }

    public function renameDir(array $data): bool
    {
        $this->dirResource = $this->dtoFactory->getDirFactory()->createResource();
        if ($this->validator->renameDir($data)) {
            try {
                $cleanData = $this->validator->getCleanData();
                $dirData = $this->getDirData($cleanData['id']);
                $dirPersist = $this->dtoFactory->getDirFactory()->createPersist();
                $dirPersist->load($dirData);
                $dirPersist->update($cleanData);
                $this->dirsPersistLayer->updateDir($dirPersist);
                $this->dirResource->load($dirPersist->getAttributes());
                return true;
            } catch (\Throwable $e) {
                Log::error($e);
                return false;
            }
        } else {
            $this->errors = $this->validator->getErrors();
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        }
    }

    private function _deleteDir(array $data): bool
    {
        if ($this->validator->deleteDir($data)) {
            try {
                $cleanData = $this->validator->getCleanData();
                $dirsIds = $this->dirsStorage->getIdsInDir($cleanData['id']);
                $dirsIds[] = $cleanData['id'];
                $this->dirsPersistLayer->deleteDirs($dirsIds);
                return true;
            } catch (\Throwable $e) {
                Log::error($e);
                return false;
            }
        } else {
            $this->errors = $this->validator->getErrors();
            $this->responseCode = ResponsesCode::VALIDATION_FAILED_CODE;
            return false;
        }
    }

    public function getDir(): IDirResource
    {
        return $this->dirResource;
    }

    protected function getDirData(string $dirId, array $dsl = [])
    {
        return $this->dirsStorage->getById($dirId, $dsl);
    }

    public function getItem(): IResourceItem
    {
        return $this->item;
    }
}
