<?php

namespace Velis\Model\RelationLoader\Relations;

use Velis\Model\DataObject;
use Velis\Model\RelationLoader\Exceptions\CannotLoadRelatedModelsException;

/**
 * Base class for the HasMany and HasOne relations.
 *
 * @author Szymon Janaczek <szymon.janaczek@velistech.com>
 *
 * @template TRelatedModel of DataObject
 * @template TTargetModel of DataObject
 *
 * @extends Relation<TRelatedModel, TTargetModel>
 */
abstract class HasManyOrOne extends Relation
{
    /**
     * @param int[]|string[] $foreignKeys
     * @return array<int, array<TRelatedModel>>|array<string, TRelatedModel>
     * @throws CannotLoadRelatedModelsException
     */
    public function getModels(array $foreignKeys): array
    {
        if (!method_exists($this->relatedModel, 'listAll')) {
            throw new CannotLoadRelatedModelsException($this->relatedModel);
        }

        if (count($foreignKeys) === 0) {
            return [];
        }

        $data = $this->relatedModel::listAll(
            array_merge([$this->localKey => $foreignKeys], $this->filters ?? []),
            $this->sortOrder,
            $this->getCustomFields()
        );

        return $this->keyBy($data, $this->localKey);
    }

    /**
     * @return string[]|null
     */
    private function getCustomFields(): ?array
    {
        if (is_array($this->fields)) {
            if (!in_array($this->localKey, $this->fields)) {
                $this->fields[] = $this->localKey;
            }

            return $this->fields;
        }

        return null;
    }

    /**
     * @param TTargetModel $targetModel
     * @param TRelatedModel[] $relatedModels
     * @return TRelatedModel[]|TRelatedModel|null
     */
    abstract protected function getRelatedModels(DataObject $targetModel, array $relatedModels);
}
