<?php

namespace App\Models\DTO;

use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class SearchData implements Responsable
{
    private array $extra = [];

    public function __construct(
        public int $page,
        public int $perPage,
        public int $pageCount,
        public int $offset = 0,
        public ?int $sort = null,
        public ?string $field = null,
        public ?string $keyword = null,
        public ?string $startDate = null,
        public ?string $endDate = null,
        array $extra = []
    ) {
        $this->extra = $extra;
    }

    public static function fromRequest(Request $request): static
    {
        $page      = max(1, (int)$request->input('page', 1));
        $perPage   = max(1, (int)$request->input('per_page', $request->input('perPage', DEFAULT_LIST_PER_PAGE)));
        $pageCount = max(1, (int)$request->input('page_count', $request->input('pageCount', DEFAULT_LIST_PAGE_COUNT)));
        $offset    = ($page - 1) * $perPage;

        $dto = new self(
            page: $page,
            perPage: $perPage,
            pageCount: $pageCount,
            offset: $offset,
            sort: (int)$request->input('sort', 1),
            field: $request->input('field'),
            keyword: $request->input('keyword'),
            startDate: $request->input('start_date'),
            endDate: $request->input('end_date')
        );

        $merged = array_merge($request->route()?->parameters() ?? [] , $request->all());

        foreach ($merged as $key => $value) {
            if (!property_exists($dto, $key)) {
                $dto->{$key} = $value;
            }
        }

        return $dto;
    }

    public function __set(string $name, mixed $value): void
    {
        // readonly core 필드 오염 방지
        if (property_exists($this, $name)) {
            throw new \LogicException("Cannot set '{$name}' on SearchData (readonly property).");
        }

        $this->extra[$name] = $value;
    }

    public function __get(string $name): mixed
    {
        return $this->extra[$name] ?? null;
    }

    public function __isset(string $name): bool
    {
        return isset($this->extra[$name]);
    }

    public function toResponse($request): JsonResponse
    {
        return response()->json([
            'page' => $this->page,
            'perPage' => $this->perPage,
            'pageCount' => $this->pageCount,
            'offset' => $this->offset,
            'sort' => $this->sort,
            'field' => $this->field,
            'keyword' => $this->keyword,
            'startDate' => $this->startDate,
            'endDate' => $this->endDate,
            ...$this->extra
        ]);
    }
}
