<?php
declare(strict_types=1);

namespace WP_AUX\Entities\Admin\AuxComponents;

use WP_AUX\Services\Admin\AuxComponents\AuxComponentsAuditFieldPropertiesService;
use WP_AUX\Services\Admin\AuxComponents\AuxComponentsAuditFieldService;

class AuxComponentsAuditFieldPropertyEntity
{
  protected string $name;
  protected AuxComponentsAuditFieldEntity $auditField;
  /**
   * @var \ReflectionProperty
   */
  public $property;

  protected \ReflectionNamedType|\ReflectionUnionType|\ReflectionIntersectionType|null $parameterType;
  protected ?string $parameterTypeString = null;
  protected mixed $defaultValue;
  protected mixed $value;
  protected mixed $displayValue;
  protected string $valueType;
  protected ?AuxComponentAuditFileEntity $declarationFile = null;
  protected ?int $declarationLine = null;

  public function __construct(AuxComponentsAuditFieldEntity $auditField, \ReflectionProperty $property)
  {
    $this->auditField = $auditField;
    $this->property = $property;
    $this->name = $property->getName();

    $this->parameterType = $property->getType();
    $this->defaultValue = $property->getDefaultValue();
    $this->value = $property->getValue($auditField->field);

    $this->prepare();
  }

  protected function prepare(): void
  {
    $this->displayValue = $this->value;
    $this->valueType = 'string';

    if (
      is_string($this->value) &&
      'choices' === $this->property->name &&
      method_exists($this->value, 'cases')
    ) {
      $this->valueType = 'options';
    }

    if ($this->value instanceof \UnitEnum) {
      if ('mixed' !== (string) $this->property->getType()) {
        $this->parameterTypeString = 'options';
      }

      $this->displayValue = $this->value->value;
    } elseif (is_array($this->value)) {
      $this->displayValue = empty($this->value) ? '[empty array]' : '[array]';
    } elseif (is_object($this->value)) {
      $this->parameterTypeString = 'object';
      $this->displayValue = '[object]';
    }

    $this->prepareDeclaration();
  }

  public function prepareDeclaration() {
    if (in_array($this->name, AuxComponentsAuditFieldPropertiesService::$automaticFieldValueProperties)) {
      return;
    }

    $fileObject = $this->auditField->file;

    $tryFiles = [
      $this->auditField->file,
    ];

    while ($parent = $fileObject->getParentFile()) {
      $tryFiles[] = $parent;
      $fileObject = $parent;

      if ($fileObject->getPathKey() === 'base') {
        if ($traits = $this->auditField->parentClass->getTraits()) {
          foreach ($traits as $trait) {
            $tryFiles[] = new AuxComponentAuditFileEntity($trait->getFileName());
          }
        }
      }
    };

    foreach ($tryFiles as $try) {
      if ($declarationLine = AuxComponentsAuditFieldPropertiesService::getPropertyDeclarationLine($try->getFilePath(), $this->name)) {
        $this->declarationFile = $try;
        $this->declarationLine = $declarationLine;
        return;
      }
    }
  }

  public function getDeclarationFile(): ?AuxComponentAuditFileEntity
  {
    return $this->declarationFile;
  }

  public function getDeclarationLine(): ?int
  {
    return $this->declarationLine;
  }

  public function getName(): string
  {
    return $this->name;
  }

  public function getValueType(): string
  {
    return $this->valueType;
  }

  public function getValue(): mixed
  {
    return $this->value;
  }

  public function getDisplayValue(): mixed
  {
    return $this->displayValue;
  }

  public function getParameterType(): \ReflectionIntersectionType|\ReflectionNamedType|\ReflectionUnionType|null
  {
    return $this->parameterType;
  }

  public function getParameterTypeString()
  {
    return $this->parameterTypeString ?? (string) $this->parameterType;
  }
}
