<?php

namespace lib\Core;

use lib\Core\Http\Response;

class Router
{
  private array $routes = [];
  private string $currentGroupPrefix = '';
  private array $currentGroupMiddlewares = [];

  /**
   * 라우트 등록 (동적 파라미터 지원)
   */
  public function add(string $method, string $path, callable $callback, array $middlewares = []): void
  {
    // 그룹 접두사와 병합
    $fullPath = trim($this->currentGroupPrefix . '/' . trim($path, '/'), '/');

    $route = [
      'method'      => strtoupper($method),
      'path'        => $fullPath,
      'callback'    => $callback,
      'middlewares' => array_merge($this->currentGroupMiddlewares, $middlewares)
    ];

    // 동적 파라미터({param})가 있으면 정규식 패턴 생성
    if (strpos($fullPath, '{') !== false) {
      $pattern = preg_replace_callback('/\{(\w+)\}/', function ($matches) {
        return '(?P<' . $matches[1] . '>[^/]+)';
      }, $fullPath);
      $route['pattern'] = '/^' . str_replace('/', '\/', $pattern) . '(\/.*)?$/';
    } else {
      // SEO key/value 형태를 위해 접두사 이후의 추가 파라미터를 허용
      $route['pattern'] = '/^' . str_replace('/', '\/', $fullPath) . '(\/(?P<extra>.*))?$/';
    }

    $this->routes[] = $route;
  }

  /**
   * 라우트 그룹: 공통 접두사와 공통 미들웨어를 적용
   */
  public function group(string $prefix, callable $callback, array $middlewares = []): void
  {
    $previousGroupPrefix = $this->currentGroupPrefix;
    $previousGroupMiddlewares = $this->currentGroupMiddlewares;

    $this->currentGroupPrefix = trim($previousGroupPrefix . '/' . trim($prefix, '/'), '/');
    $this->currentGroupMiddlewares = array_merge($previousGroupMiddlewares, $middlewares);

    $callback($this);

    $this->currentGroupPrefix = $previousGroupPrefix;
    $this->currentGroupMiddlewares = $previousGroupMiddlewares;
  }

  /**
   * 라우트 매칭 및 실행
   */
  public function dispatch(string $uri, string $method = 'GET'): mixed
  {
    $uri = trim(parse_url($uri, PHP_URL_PATH), '/');

    // 정적 파일 처리: public 폴더 내에 파일이 있으면 직접 서빙
    $staticFile = DOCUMENTROOT_PATH . $uri;
    if (file_exists($staticFile) && is_file($staticFile)) {
      return $this->serveStaticFile($staticFile);
    }
    foreach ($this->routes as $route) {
      if ($route['method'] !== strtoupper($method)) { //GET, POST 등 HTTP 메소드 체크
        continue;
      }
      if (preg_match($route['pattern'], $uri, $matches)) {
        $params = [];
        // 패턴에 named 그룹이 있다면 추출
        foreach ($matches as $key => $value) {
          if (is_string($key)) {
            $params[$key] = $value;
          }
        }
        //추가 SEO key/value 파라미터로 파싱
        if (isset($params['extra']) && $params['extra'] !== '') {
          $extra = trim($params['extra'], '/');
          $extraSegments = explode('/', $extra);
          // 짝수개여야 key/value 쌍으로 변환 가능
          if (count($extraSegments) % 2 === 0) {
            for ($i = 0; $i < count($extraSegments); $i += 2) {
              $key = $extraSegments[$i];
              $value = $extraSegments[$i + 1];
              $params[$key] = $value;
            }
          }
          unset($params['extra']);
        } else {
          // 패턴에 extra 그룹이 없으면, 기본 GET 파라미터와 병합
          $params = array_merge($_GET, $params);
        }
        return $this->handle($route, $params);
      }
    }
    // 404 응답 객체 반환
    return (new Response('해당 Route 경로를 찾을수 없습니다.' . $uri, 404));
  }

  /**
   * 미들웨어 체인을 거쳐 최종 콜백 실행
   */
  private function handle(array $route, array $params): mixed
  {
    $handler = $route['callback'];
    $middlewares = $route['middlewares'];

    $pipeline = array_reduce(
      array_reverse($middlewares),
      fn($next, $middleware) => fn($params) => (new $middleware)->handle($params, $next),
      $handler
    );
    return $pipeline($params);
  }

  /**
   * 정적 파일 서빙 (이미지, CSS, JS 등)
   */
  private function serveStaticFile(string $file): Response
  {
    return (new Response())
      ->header('Content-Type', mime_content_type($file))
      ->setContent(file_get_contents($file));
  }
}
