<?php

namespace App\DTOs;

use ReflectionClass;
use ReflectionNamedType;

abstract class CommonDTO
{
  protected function __construct(array $datas = [])
  {
    if (empty($datas)) return;

    $reflection = new ReflectionClass($this);

    foreach ($datas as $key => $value) {
      if (!$reflection->hasProperty($key)) continue;

      $property = $reflection->getProperty($key);
      $type = $property->getType();
      $assignValue = $value;

      // 1. 빈 문자열('') 처리 로직 개선
      if ($value === '') {
        // 프로퍼티가 null을 허용하는 경우에만 null 할당
        if ($type instanceof ReflectionNamedType && $type->allowsNull()) {
          $assignValue = null;
        } else {
          // null을 허용하지 않는 경우, 타입별 기본값 지정
          $typeName = ($type instanceof ReflectionNamedType) ? $type->getName() : '';
          $assignValue = ($typeName === 'int' || $typeName === 'float') ? 0 : '';
        }
      }
      // 2. 값이 존재할 때의 타입별 캐스팅 로직
      elseif ($type instanceof ReflectionNamedType) {
        $typeName = $type->getName();

        // 타입이 array이고 입력값이 문자열인 경우 (CSV -> Array)
        if ($typeName === 'array' && is_string($value)) {
          $assignValue = explode(DEFAULTS["DELIMITER_ROLE"], $value);
        }
        // 타입이 int이고 입력값이 숫자형인 경우
        elseif ($typeName === 'int' && is_numeric($value)) {
          $assignValue = (int) $value;
        }
        // 타입이 float이고 입력값이 숫자형인 경우
        elseif ($typeName === 'float' && is_numeric($value)) {
          $assignValue = (float) $value;
        }
      }

      // 최종 값 할당
      $this->{$key} = $assignValue;
    }
  }

  // [중요] final 해제 또는 로직 변경
  public function toArray(): array
  {
    // get_object_vars는 protected를 가져오지 못하므로 
    // Reflection을 사용하여 모든 프로퍼티를 가져오되, 
    // 값을 가져올 때는 $this->{$name}을 통해 Getter나 매직메서드가 작동하게 합니다.
    $reflection = new ReflectionClass($this);
    $properties = $reflection->getProperties();
    $result = [];

    foreach ($properties as $property) {
      $name = $property->getName();
      // $this->$name 처리를 통해 자식의 __get()이 호출되도록 유도
      $result[$name] = $this->{$name};
    }

    return $result;
  }
}
