<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\Entity;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
use Mautic\CoreBundle\Entity\CommonEntity;

class DealField extends CommonEntity
{
    public const TABLE_NAME = 'dealflow_deal_fields';

    public const TYPE_TEXT = 'text';
    public const TYPE_TEXTAREA = 'textarea';
    public const TYPE_NUMBER = 'number';
    public const TYPE_BOOLEAN = 'boolean';
    public const TYPE_SELECT = 'select';
    public const TYPE_DATE = 'date';

    private ?int $id = null;

    private string $label = '';

    private string $alias = '';

    private string $fieldType = self::TYPE_TEXT;

    /**
     * @var list<string>
     */
    private array $options = [];

    private ?string $defaultValue = null;

    private bool $isRequired = false;

    private bool $showInList = false;

    private int $sortOrder = 0;

    private bool $isActive = true;

    private \DateTimeInterface $createdAt;

    private \DateTimeInterface $updatedAt;

    public function __construct()
    {
        $this->createdAt = new \DateTimeImmutable();
        $this->updatedAt = new \DateTimeImmutable();
    }

    /** @param ORM\ClassMetadata<static> $metadata */
    public static function loadMetadata(ORM\ClassMetadata $metadata): void
    {
        $builder = new ClassMetadataBuilder($metadata);

        $builder->setTable(self::TABLE_NAME)
            ->setCustomRepositoryClass(DealFieldRepository::class)
            ->addUniqueConstraint(['alias'], 'dealflow_field_alias_unique')
            ->addIndex(['sort_order'], 'dealflow_field_sort_idx')
            ->addIndex(['is_active'], 'dealflow_field_active_idx');

        $builder->addId();

        $builder->createField('label', Types::STRING)
            ->length(191)
            ->build();

        $builder->createField('alias', Types::STRING)
            ->length(191)
            ->build();

        $builder->createField('fieldType', Types::STRING)
            ->columnName('field_type')
            ->length(32)
            ->build();

        $builder->createField('options', Types::JSON)
            ->columnName('options_json')
            ->build();

        $builder->createField('defaultValue', Types::STRING)
            ->columnName('default_value')
            ->nullable()
            ->length(191)
            ->build();

        $builder->createField('isRequired', Types::BOOLEAN)
            ->columnName('is_required')
            ->build();

        $builder->createField('showInList', Types::BOOLEAN)
            ->columnName('show_in_list')
            ->build();

        $builder->createField('sortOrder', Types::INTEGER)
            ->columnName('sort_order')
            ->build();

        $builder->createField('isActive', Types::BOOLEAN)
            ->columnName('is_active')
            ->build();

        $builder->createField('createdAt', 'datetime_immutable')
            ->columnName('created_at')
            ->build();

        $builder->createField('updatedAt', 'datetime_immutable')
            ->columnName('updated_at')
            ->build();
    }

    /**
     * @return array<string, string>
     */
    public static function getTypeChoices(): array
    {
        return [
            'Single line text' => self::TYPE_TEXT,
            'Long text' => self::TYPE_TEXTAREA,
            'Number' => self::TYPE_NUMBER,
            'Yes / No' => self::TYPE_BOOLEAN,
            'Select' => self::TYPE_SELECT,
            'Date' => self::TYPE_DATE,
        ];
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getLabel(): string
    {
        return $this->label;
    }

    public function setLabel(?string $label): self
    {
        $label = trim((string) $label); // coerce null to '' — PropertyAccessor runs before NotBlank validation
        $this->isChanged('label', $label);
        $this->label = $label;

        return $this;
    }

    public function getAlias(): string
    {
        return $this->alias;
    }

    public function setAlias(?string $alias): self
    {
        $alias = trim((string) $alias);
        $this->isChanged('alias', $alias);
        $this->alias = $alias;

        return $this;
    }

    public function getFieldType(): string
    {
        return $this->fieldType;
    }

    public function setFieldType(string $fieldType): self
    {
        $this->isChanged('fieldType', $fieldType);
        $this->fieldType = $fieldType;

        if (!$this->supportsOptions()) {
            $this->setOptions([]);
        }

        if (self::TYPE_BOOLEAN !== $fieldType) {
            $this->setDefaultValue(null);
        }

        return $this;
    }

    /**
     * @return list<string>
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    /**
     * @param list<string> $options
     */
    public function setOptions(array $options): self
    {
        $normalized = [];
        foreach ($options as $option) {
            $option = trim((string) $option);
            if ('' === $option || in_array($option, $normalized, true)) {
                continue;
            }

            $normalized[] = $option;
        }

        $this->isChanged('options', $normalized);
        $this->options = $normalized;

        return $this;
    }

    public function getOptionList(): string
    {
        return implode("\n", $this->options);
    }

    public function setOptionList(?string $optionList): self
    {
        $lines = preg_split('/\r\n|\r|\n/', trim((string) $optionList)) ?: [];

        return $this->setOptions(array_values($lines));
    }

    public function getDefaultValue(): ?string
    {
        return $this->defaultValue;
    }

    public function setDefaultValue(?string $defaultValue): self
    {
        $normalized = null;

        if (null !== $defaultValue && '' !== trim($defaultValue)) {
            $normalized = trim($defaultValue);
        }

        $this->isChanged('defaultValue', $normalized);
        $this->defaultValue = $normalized;

        return $this;
    }

    public function getBooleanDefaultValue(): ?bool
    {
        return match ($this->defaultValue) {
            '1', 'true', 'yes', 'on' => true,
            '0', 'false', 'no', 'off' => false,
            default => null,
        };
    }

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

    public function setIsRequired(bool $isRequired): self
    {
        $this->isChanged('isRequired', $isRequired);
        $this->isRequired = $isRequired;

        return $this;
    }

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

    public function setShowInList(bool $showInList): self
    {
        $this->isChanged('showInList', $showInList);
        $this->showInList = $showInList;

        return $this;
    }

    public function getSortOrder(): int
    {
        return $this->sortOrder;
    }

    public function setSortOrder(int $sortOrder): self
    {
        $this->isChanged('sortOrder', $sortOrder);
        $this->sortOrder = $sortOrder;

        return $this;
    }

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

    public function setIsActive(bool $isActive): self
    {
        $this->isChanged('isActive', $isActive);
        $this->isActive = $isActive;

        return $this;
    }

    public function getCreatedAt(): \DateTimeInterface
    {
        return $this->createdAt;
    }

    public function setCreatedAt(\DateTimeInterface $createdAt): self
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getUpdatedAt(): \DateTimeInterface
    {
        return $this->updatedAt;
    }

    public function setUpdatedAt(\DateTimeInterface $updatedAt): self
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    public function supportsOptions(): bool
    {
        return self::TYPE_SELECT === $this->fieldType;
    }
}
