<?php

declare(strict_types=1);

namespace apexl\Io\dto\Relationship;

use apexl\entityCore\collections\EntityCollection;
use apexl\Io\includes\Entity;
use apexl\Io\interfaces\RelationshipInterface;
use Arrayy\Type\StringCollection;

abstract readonly class OneToMany extends AbstractRelationship implements RelationshipInterface
{
    public function hydrate(array $data): EntityCollection
    {
        return EntityCollection::createFromArray(
            array_values(
                array_filter(
                    array_map(
                        function (?array $item): ?Entity {
                            return $item ? $this->freshEntity()->hydrate($item) : null;
                        },
                        $data,
                    )
                )
            )
        );
    }

    public function isDescendedFromOneToMany(): bool
    {
        return true;
    }

    public function valueWhenEmpty(): EntityCollection
    {
        return new EntityCollection();
    }

    public function fieldsSelectString(StringCollection $fields, StringCollection $remaining): string
    {
        $relationFields = $fields->map(fn(string $field): string => sprintf(
            "'%s', %s.%s",
            $field,
            $this->getAliasedRemoteTable(),
            $field
        ));

        $relationFields->append(
            sprintf(
                "'_key', %s.%s",
                $this->getAliasedRemoteTable(),
                $this->getRemoteEntity()->getPrimaryKey(),
            ),
        );

        if ($this->parent) {
            $relationFields->append(
                sprintf(
                    "'_parent', %s.%s",
                    $this->parent->getAliasedRemoteTable(),
                    $this->parent->getRemoteEntity()->getPrimaryKey(),
                ),
            );
        }

        return sprintf(
        /** @lang TEXT */
            <<<'SQL'
                (SELECT
                    IFNULL(JSON_ARRAYAGG(JSON_OBJECT(%1$s)), JSON_ARRAY())
                    FROM %2$s as %7$s
                    WHERE %7$s.%3$s = %4$s.%5$s
                    LIMIT 1
                ) AS _%6$s
                SQL,
            $relationFields->implode(', '),
            $this->getRemoteTable(),
            $this->getRemoteKey(),
            $this->getLocalTable(),
            $this->localKey,
            $this->prefix(),
            $this->getAliasedRemoteTable(),
        );
    }

    public function countSql(?string $table = null): string
    {
        $query = $this->getRemoteEntity()->findMultipleQuery(
            conditions: $this->conditions($table),
            limit: 1,
            fields: [],
            tableAlias: $this->getAliasedRemoteTable()
        );

        $query->resetFields();

        return ($this->queryCallback)($this, $query)->fields('COUNT(*)')->query->getStatement();
    }


    protected function orderBy(): array
    {
        return [];
    }

    protected function toEntityCollection(int|array|Entity|EntityCollection $toResolve): EntityCollection
    {
        if ($toResolve instanceof Entity) {
            $toResolve = [$toResolve];
        } elseif (is_int($toResolve)) {
            $toResolve = [$this->remoteEntity::find($toResolve)];
        }

        if (is_array($toResolve)) {
            foreach ($toResolve as $key => $item) {
                if (is_int($item)) {
                    $toResolve[$key] = $this->remoteEntity::find($item);
                }
            }

            $toResolve = EntityCollection::createFromArray($toResolve);
        }

        return $toResolve;
    }
}
