<?php

namespace App\Element\UseCases\CreateElement;

use App\Element\CreateElementDto;
use App\Element\Element;
use App\Element\ElementRepository;
use App\Exceptions\BadRequestException;
use App\Set\Set as DomainSet;
use App\Set\SetRepository;
use DomainException;

class CreateElement
{
    public function __construct(
        private ElementRepository $elementRepo,
        private SetRepository $setRepo,
    ) {}

    /**
     * @throws BadRequestException
     * @throws DomainException
     */
    public function execute(CreateElementRequest $request): Element
    {
        if ($request->setId === null) {
            throw new BadRequestException('setId is required');
        }
        if ($request->title === null || $request->title === '') {
            throw new BadRequestException('title is required');
        }

        $set = $this->setRepo->find($request->setId);
        if ($set === null) {
            throw new DomainException(
                "Set with id: {$request->setId} doesnt exist"
            );
        }

        if ($request->parentElementId === null) {
            $this->validateNoRootElementExists($set);

            return $this->elementRepo->create(new CreateElementDto(
                set: $set,
                title: $request->title,
                parentElement: null,
            ));
        }

        $parentElement = $this->elementRepo->find(
            $request->parentElementId
        );
        if ($parentElement === null) {
            throw new DomainException(
                "Element with id: {$request->parentElementId} doesnt exist"
            );
        }
        if ($parentElement->getSet()->getId() !== $set->getId()) {
            throw new DomainException(
                'Parent element must belong to the same set'
            );
        }

        return $this->elementRepo->create(new CreateElementDto(
            set: $set,
            title: $request->title,
            parentElement: $parentElement,
        ));
    }

    /**
     * @throws DomainException
     */
    private function validateNoRootElementExists(DomainSet $set): void
    {
        $elements = $this->elementRepo->findBySet($set);
        foreach ($elements as $element) {
            if ($element->getParentElement() === null) {
                throw new DomainException(
                    'A root element already exists for this set'
                );
            }
        }
    }
}
