<?php
// =========================================================
// AbstractWebController.php  (FINAL)
// - runAction / okResponse / failResponse 내장
// - action_redirect_process: AJAX 방어 + 상태코드 정책 고정
//   * warning/error => 400
//   * critical/alert/emergency => 500
//   * info/notice/debug/default => 200
// - RedirectResponse|ResponseInterface로 엄격 정리
// =========================================================

namespace App\Controllers;

use App\Libraries\AuthContext;
use App\Traits\LogTrait;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
use App\Exceptions\FormValidationException;

abstract class AbstractWebController extends Controller
{
  use LogTrait;

  private array $_action_paths = [];
  private array $_viewDatas = [];
  private ?string $_title = null;

  protected $layouts = [];
  protected $service = null;

  // --- 초기화 및 DI ---
  public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
  {
    parent::initController($request, $response, $logger);
    helper('util');
  }

  final protected function getAuthContext(): AuthContext
  {
    return service('myauth')->getAuthContext();
  }

  protected function getTitle(): string
  {
    if ($this->_title === null) {
      $this->_title = lang("{$this->service->getClassPaths(false)}.title");
    }
    return $this->_title;
  }

  // --- 경로 및 뷰 데이터 관리 ---
  final protected function addActionPaths(string $path): void
  {
    $this->_action_paths[] = $path;
  }

  final protected function getActionPaths($isArray = true, $delimeter = DIRECTORY_SEPARATOR): array|string
  {
    return $isArray ? $this->_action_paths : implode($delimeter, $this->_action_paths);
  }

  final protected function addViewDatas(string $key, mixed $value): void
  {
    $this->_viewDatas[$key] = $value;
  }

  final protected function getViewDatas(?string $key = null): mixed
  {
    if ($key === null)
      return $this->_viewDatas;
    return $this->_viewDatas[$key] ?? null;
  }

  // --- 공통 처리 로직 (Override 가능) ---
  protected function action_init_process(string $action, array $formDatas = []): void
  {
    $this->addViewDatas('action', $action);
    $this->addViewDatas('authContext', $this->getAuthContext());
    $this->addViewDatas('classPath', $this->service->getClassPaths(false));
    $this->addViewDatas('uri', $this->request->getUri());
  }

  /**
   * action_redirect_process
   * ✅ AJAX 요청이면 RedirectResponse 대신 JSON으로 변환(방어)
   *
   * 상태코드 정책(고정):
   * - warning/error => 400
   * - critical/alert/emergency => 500
   * - info/notice/debug/default => 200
   */
  protected function action_redirect_process(string $type, string $message, ?string $redirect_url = null): RedirectResponse|ResponseInterface
  {
    $resolvedRedirect = $redirect_url
      ?? $this->getAuthContext()->popPreviousUrl()
      ?? implode(DIRECTORY_SEPARATOR, $this->getActionPaths());

    if ($this->request->isAJAX()) {
      $error400 = ['warning', 'error'];
      $error500 = ['critical', 'alert', 'emergency'];

      if (in_array($type, $error400, true)) {
        log_message($type, $message);
        return $this->response->setStatusCode(400)->setJSON([
          'ok' => false,
          'message' => $message,
          'redirect' => $resolvedRedirect,
        ]);
      }

      if (in_array($type, $error500, true)) {
        log_message($type, $message);
        return $this->response->setStatusCode(500)->setJSON([
          'ok' => false,
          'message' => $message,
          'redirect' => $resolvedRedirect,
        ]);
      }

      return $this->response->setStatusCode(200)->setJSON([
        'ok' => true,
        'message' => $message,
        'redirect' => $resolvedRedirect,
      ]);
    }

    switch ($type) {
      case 'warning':
      case 'error':
      case 'critical':
      case 'alert':
      case 'emergency':
        log_message($type, $message);
        return redirect()->back()->withInput()->with('message', $message);

      case 'debug':
      case 'info':
      case 'notice':
      default:
        return redirect()->to($resolvedRedirect)->with('message', $message);
    }
  }

  /**
   * 뷰 렌더링
   */
  protected function action_render_process(string $view_file, array $viewDatas, ?string $template_path = null): string
  {
    helper(['form', 'utility']);

    $baseViewPath = trim($viewDatas['layout']['path'], '/');
    if ($template_path)
      $baseViewPath .= '/' . trim($template_path, '/');

    $viewName = $baseViewPath . '/' . ltrim($view_file, '/');

    return view($viewName, [
      'viewDatas' => [
        ...$viewDatas,
        'forms' => [
          'attributes' => ['method' => 'post'],
          'hiddens' => [],
        ],
      ],
    ]);
  }

  // =========================================================
  // 공통화: runAction / okResponse / failResponse
  // =========================================================
  protected function stringifyError(mixed $x): string
  {
    if (is_string($x))
      return $x;

    if ($x instanceof \Throwable) {
      return $x->getMessage();
    }

    if (is_array($x) || is_object($x)) {
      $json = json_encode($x, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
      return $json !== false ? $json : print_r($x, true);
    }

    return (string) $x;
  }

  protected function runAction(string $action, callable $core): mixed
  {
    try {
      return $core();
    } catch (FormValidationException $e) {
      return $this->failResponse($action, $e);
    } catch (\Throwable $e) {
      return $this->failResponse($action, $e);
    }
  }

  protected function okResponse(string $message, array $payload = [], ?string $redirectUrl = null): RedirectResponse|ResponseInterface
  {
    if ($this->request->isAJAX()) {
      return $this->response->setStatusCode(200)->setJSON(array_merge(
        ['ok' => true, 'message' => $message],
        $payload
      ));
    }
    return $this->action_redirect_process('info', $message, $redirectUrl);
  }

  protected function failResponse(string $action, \Throwable $e, ?string $humanPrefix = null): RedirectResponse|ResponseInterface
  {
    if ($e instanceof FormValidationException) {
      if ($this->request->isAJAX()) {
        return $this->response->setStatusCode(422)->setJSON([
          'ok' => false,
          'errors' => $e->errors,
        ]);
      }
      // ✅ redirect에는 string만 넣는다
      return $this->action_redirect_process('error', $e->getMessage());
    }

    if ($this->request->isAJAX()) {
      return $this->response->setStatusCode(500)->setJSON([
        'ok' => false,
        'message' => static::class . '->' . $action . "에서 오류:" . $e->getMessage(),
      ]);
    }

    $msg = $humanPrefix ? ($humanPrefix . $e->getMessage()) : $e->getMessage();
    // ✅ redirect에는 string만 넣는다
    return $this->action_redirect_process('error', $msg);
  }
}
