<?php

namespace App\Handlers;

require __DIR__ . "/Classes/BaseHandler.php";

use App\Classes\BaseHandler;

class SearchHandler extends BaseHandler
{
  protected int $cacheTTL = 300;

  public function __construct()
  {
    parent::__construct();
    $this->initializeCache();
  }

  public function handleRequest(): void
  {
    try {
      $query = $this->validateAndSanitizeQuery($_GET["q"] ?? null);
      $types = $this->validateAndSanitizeTypes($_GET["type"] ?? "");
      $page = isset($_GET["page"]) ? intval($_GET["page"]) : 1;
      $pageSize = isset($_GET["pageSize"]) ? intval($_GET["pageSize"]) : 10;
      $offset = ($page - 1) * $pageSize;
      $cacheKey = $this->generateCacheKey($query, $types, $page, $pageSize);
      $results = [];

      $results =
        $this->getCachedResults($cacheKey) ??
        $this->fetchSearchResults($query, $types, $pageSize, $offset);

      if (empty($results) || empty($results["data"])) {
        $this->sendResponse(["results" => [], "total" => 0, "page" => $page, "pageSize" => $pageSize], 200);
        return;
      }

      $this->cacheResults($cacheKey, $results);

      $this->sendResponse(
        [
          "results" => $results["data"],
          "total" => $results["total"],
          "page" => $page,
          "pageSize" => $pageSize,
        ],
        200
      );
    } catch (Exception $e) {
      error_log("Search API Error: " . $e->getMessage());
      $this->sendErrorResponse("Invalid request. Please check your query and try again.", 400);
    }
  }

  private function validateAndSanitizeQuery(?string $query): string
  {
    if (empty($query) || !is_string($query)) throw new Exception("Invalid 'q' parameter. Must be a non-empty string.");

    $query = trim($query);

    if (strlen($query) > 255) throw new Exception(
        "Invalid 'q' parameter. Exceeds maximum length of 255 characters."
      );

    if (!preg_match('/^[a-zA-Z0-9\s\-_\'"]+$/', $query)) throw new Exception(
        "Invalid 'q' parameter. Contains unsupported characters."
      );

    $query = preg_replace("/\s+/", " ", $query);

    return $query;
  }

  private function validateAndSanitizeTypes(string $rawTypes): ?array
  {
    $allowedTypes = ["post", "artist", "genre", "book", "movie", "show"];

    if (empty($rawTypes)) return null;

    $types = array_map(
      fn($type) => strtolower(
        trim(htmlspecialchars($type, ENT_QUOTES, "UTF-8"))
      ),
      explode(",", $rawTypes)
    );
    $invalidTypes = array_diff($types, $allowedTypes);

    if (!empty($invalidTypes)) throw new Exception(
        "Invalid 'type' parameter. Unsupported types: " .
          implode(", ", $invalidTypes)
      );

    return $types;
  }

  private function fetchSearchResults(
    string $query,
    ?array $types,
    int $pageSize,
    int $offset
  ): array {
    $typesParam =
      $types && count($types) > 0 ? "%7B" . implode(",", $types) . "%7D" : "";
    $endpoint = "rpc/search_optimized_index";
    $queryString =
      "search_query=" .
      urlencode($query) .
      "&page_size={$pageSize}&page_offset={$offset}" .
      ($typesParam ? "&types={$typesParam}" : "");

    $data = $this->makeRequest("GET", "{$endpoint}?{$queryString}");

    $total = count($data) > 0 ? $data[0]["total_count"] : 0;
    $results = array_map(function ($item) {
      unset($item["total_count"]);
      return $item;
    }, $data);

    return ["data" => $results, "total" => $total];
  }

  private function generateCacheKey(
    string $query,
    ?array $types,
    int $page,
    int $pageSize
  ): string {
    $typesKey = $types ? implode(",", $types) : "all";
    return sprintf(
      "search:%s:types:%s:page:%d:pageSize:%d",
      md5($query),
      $typesKey,
      $page,
      $pageSize
    );
  }

  private function getCachedResults(string $cacheKey): ?array
  {
    if ($this->cache instanceof \Redis) {
      $cachedData = $this->cache->get($cacheKey);
      return $cachedData ? json_decode($cachedData, true) : null;
    } elseif (is_array($this->cache)) {
      return $this->cache[$cacheKey] ?? null;
    }
    return null;
  }

  private function cacheResults(string $cacheKey, array $results): void
  {
    if ($this->cache instanceof \Redis) {
      $this->cache->set($cacheKey, json_encode($results));
      $this->cache->expire($cacheKey, $this->cacheTTL);
    } elseif (is_array($this->cache)) {
      $this->cache[$cacheKey] = $results;
    }
  }
}

$handler = new SearchHandler();
$handler->handleRequest();