<?php

namespace lib\Core;

use lib\Core\Controller;

abstract class Route
{
  private string $_url = "";
  protected ?Controller $_controller;
  protected string $_method = "";
  protected array $_segments = [];

  public function __construct(string $url)
  {
    $this->_url = $url;
    $this->init();
  }
  final public function getURL(): string
  {
    return $this->_url;
  }
  private function init(): void
  {
    $path = parse_url($this->getURL(), PHP_URL_PATH);
    $arguments = explode('/', trim($path, '/'));

    // 컨트롤러와 메서드를 설정
    $this->setController($arguments);
    $this->setMethod($arguments);

    // 남은 세그먼트를 파라미터로 설정
    $this->setSegments($arguments);
  }
  final protected function getController(): Controller
  {
    return $this->_controller;
  }

  protected function routeController(string $route): string
  {
    return $route;
  }
  private function setController(array &$segments): void
  {
    $route     =  $segments[0] ? $segments[0] : 'HomeController';
    // echo $route;
    // exit;
    $controller = "lib\\Controllers\\" . ucfirst($this->routeController($route));
    if (class_exists($controller)) {
      $this->_controller = new $controller();
    } else {
      throw new \Exception("[{$controller}] 해당 Controller를 찾을수 없습니다.");
    }
    array_shift($segments);
  }
  final protected function getMethod(): string
  {
    return $this->_method;
  }
  protected function routeMethod(string $route): string
  {
    return $route;
  }
  final protected function setMethod(array &$segments): void
  {
    //$segments[0]인이유는 setController에서 $segments를 array_shift했기때문
    $route     = count($segments) ? $segments[0] : 'index';
    // echo $route;
    // exit;    
    $this->_method = strtolower($this->routeMethod($route));
    array_shift($segments);
  }

  final public function getSegments(): array
  {
    return $this->_segments;
  }
  final protected function setSegments(array &$segments): void
  {
    // 세그먼트 배열을 key/value 쌍으로 변환
    $params = [];
    for ($i = 0; $i < count($segments); $i += 2) {
      if (isset($segments[$i + 1])) {
        $params[$segments[$i]] = $segments[$i + 1];
      }
    }
    $this->_segments = $params;
  }
  final public function run(): mixed
  {
    $controller = $this->getController();
    $method = $this->getMethod();

    if (!method_exists($controller, $method)) {
      throw new \Exception("해당 함수[{$method}]를 " . get_class($this->_controller) . "에서 찾을수 없습니다.");
    }

    // 컨트롤러에 세그먼트 전달
    $controller->setSegments($this->getSegments());

    // 키/값 형태로 세그먼트 처리
    $params = [];
    $segments = $this->getSegments();

    // 메서드의 매개변수 정보 가져오기
    $reflectionMethod = new \ReflectionMethod($controller, $method);
    $parameters = $reflectionMethod->getParameters();

    if (count($parameters) > 0) {
      // 매개변수가 존재하면 URL 세그먼트를 순서대로 매개변수에 매핑
      foreach ($parameters as $i => $param) {
        // 매개변수 이름을 가져와서 세그먼트에서 값을 찾기
        $paramName = $param->getName();
        if (isset($segments[$paramName])) {
          $params[] = $segments[$paramName];
        } else if (isset($segments[$i])) {
          $params[] = $segments[$i];
        } else if ($param->isDefaultValueAvailable()) {
          $params[] = $param->getDefaultValue();
        } else {
          $params[] = null;
        }
      }
    } else {
      // 매개변수가 없으면 빈 배열 전달
      $params = [];
    }

    return call_user_func_array([$controller, $method], $params);
  }
}
