<?php

namespace Entity\Model\Metadata;

/**
 * @implements \Iterator<int|string, mixed>
 */
abstract class EntityMeta implements \Iterator
{
    private string $entity_key;
    private string $entity_id;
    private ?int $struct_id;

    /** @var array<string> */
    private array $mapField;

    /** @var array<string> list of fields */
    private array $toBeDeleted;

//______________________________________________________________________________
// Constructor
    
    /**
     * @param array<string> $mapField value index by field name
     */
    protected function __construct(
        string $entity_key,
        string $entity_id,
        ?int $struct_id,
        array $mapField
    ) {
        $this->entity_key = $entity_key;
        $this->entity_id = $entity_id;
        $this->struct_id = $struct_id;
        $this->mapField = $mapField;
        $this->toBeDeleted = [];
    }

    /**
     * @todo make private and access it only using Relfector
     * @param array<string> $mapField
     * @return \Entity\Model\Metadata\EntityMeta
     */
    public function hydrate(
        string $entity_key,
        string $entity_id,
        ?int $struct_id,
        array $mapField
    ) :self {
        $this->entity_key = $entity_key;
        $this->entity_id = $entity_id;
        $this->struct_id = $struct_id;
        $this->mapField = $mapField;
        $this->toBeDeleted = [];
        return $this;
    }

//______________________________________________________________________________
// Accessor

    public function getEntityType() :string
    {
        return $this->entity_key;
    }

    public function getEntityId() :string
    {
        return $this->entity_id;
    }

    public function getStructId() :?int
    {
        return $this->struct_id;
    }

    /**
     * @return array<string>
     */
    public function tobeDeleted() :array
    {
        return $this->toBeDeleted;
    }

    /**
     * @return array<string>
     */
    protected function getFieldMap()
    {
        return $this->mapField;
    }

//______________________________________________________________________________
// Modifier

    /**
     * @param array<string> $fieldmap
     * @return void
     */
    protected function setFieldMap(array $fieldmap) :void
    {
        $this->mapField = $fieldmap;
    }

//______________________________________________________________________________
// Iterator

    /** @var \ArrayIterator<int|string, mixed>|null */
    private ?\ArrayIterator $iterator = null;

    /**
     * @return \ArrayIterator<int|string, mixed>
     */
    private function getIterator() :\ArrayIterator
    {
        if ($this->iterator === null) {
            $this->iterator = new \ArrayIterator($this->mapField);
        }
        return $this->iterator;
    }

    public function rewind(): void
    {
        $this->iterator = null;
    }

    /**
     * @return mixed
     */
    public function current(): mixed
    {
        return $this->getIterator()->current();
    }

    /**
     * @return string|int|null
     */
    public function key(): string|int|null
    {
        return $this->getIterator()->key();
    }

    public function next(): void
    {
        $this->getIterator()->next();
    }

    public function valid(): bool
    {
        return $this->getIterator()->valid();
    }

//______________________________________________________________________________
// Magic method

    /**
     * @param mixed $value
     * @return void
     */
    public function __set(string $field, $value): void
    {
        if (!is_string($value)) {
            throw new \Exception('Type not supported');
        }
        $this->mapField[ $field ] = $value;
        unset($this->toBeDeleted[ $field ]);
    }

    /**
     * @return mixed
     */
    public function __get(string $field)
    {
        return $this->mapField[ $field ] ?? null;
    }
    public function __isset(string $field): bool
    {
        return array_key_exists($field, $this->mapField);
    }
    public function __unset(string $field): void
    {
        $this->toBeDeleted[ $field ] = $field;
    }
}
