<?php

namespace app\controller\Api;

use app\service\Search\OpenSearchSearchService;
use InvalidArgumentException;
use JsonException;
use support\Request;
use support\Response;
use Throwable;

class SearchController
{
    public function fulltext(Request $request): Response
    {
        try {
            $payload = $this->jsonPayload($request);
            $data = (new OpenSearchSearchService())->fulltext($payload);
        } catch (JsonException $exception) {
            return $this->jsonResponse([
                'code' => 400,
                'message' => 'Invalid JSON body.',
                'errors' => ['body' => $exception->getMessage()],
            ], 400);
        } catch (InvalidArgumentException $exception) {
            return $this->jsonResponse([
                'code' => 422,
                'message' => 'Search request validation failed.',
                'errors' => ['query' => $exception->getMessage()],
            ], 422);
        } catch (Throwable $exception) {
            return $this->jsonResponse([
                'code' => 500,
                'message' => 'Full-text search failed.',
                'errors' => ['search' => $exception->getMessage()],
            ], 500);
        }

        return $this->jsonResponse([
            'code' => 0,
            'message' => 'Full-text search completed.',
            'data' => $data,
        ], 200);
    }

    public function vector(Request $request): Response
    {
        try {
            $payload = $this->jsonPayload($request);
            $data = (new OpenSearchSearchService())->vector($payload);
        } catch (JsonException $exception) {
            return $this->jsonResponse([
                'code' => 400,
                'message' => 'Invalid JSON body.',
                'errors' => ['body' => $exception->getMessage()],
            ], 400);
        } catch (InvalidArgumentException $exception) {
            return $this->jsonResponse([
                'code' => 422,
                'message' => 'Search request validation failed.',
                'errors' => ['query' => $exception->getMessage()],
            ], 422);
        } catch (Throwable $exception) {
            return $this->jsonResponse([
                'code' => 500,
                'message' => 'Vector search failed.',
                'errors' => ['search' => $exception->getMessage()],
            ], 500);
        }

        return $this->jsonResponse([
            'code' => 0,
            'message' => 'Vector search completed.',
            'data' => $data,
        ], 200);
    }

    public function hybrid(Request $request): Response
    {
        try {
            $payload = $this->jsonPayload($request);
            $data = (new OpenSearchSearchService())->hybrid($payload);
        } catch (JsonException $exception) {
            return $this->jsonResponse([
                'code' => 400,
                'message' => 'Invalid JSON body.',
                'errors' => ['body' => $exception->getMessage()],
            ], 400);
        } catch (InvalidArgumentException $exception) {
            return $this->jsonResponse([
                'code' => 422,
                'message' => 'Search request validation failed.',
                'errors' => ['query' => $exception->getMessage()],
            ], 422);
        } catch (Throwable $exception) {
            return $this->jsonResponse([
                'code' => 500,
                'message' => 'Hybrid search failed.',
                'errors' => ['search' => $exception->getMessage()],
            ], 500);
        }

        return $this->jsonResponse([
            'code' => 0,
            'message' => 'Hybrid search completed.',
            'data' => $data,
        ], 200);
    }

    /**
     * @throws JsonException
     */
    private function jsonPayload(Request $request): array
    {
        $rawBody = trim($request->rawBody());
        if ($rawBody === '') {
            return $request->post();
        }

        $payload = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
        return is_array($payload) ? $payload : [];
    }

    private function jsonResponse(array $data, int $status): Response
    {
        return response(
            json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR),
            $status,
            ['Content-Type' => 'application/json']
        );
    }
}
