<?php

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

use App\Classes\ApiHandler;
use GuzzleHttp\Client;

class WatchingImportHandler extends ApiHandler
{
  protected string $postgrestUrl;
  protected string $postgrestApiKey;

  private string $tmdbApiKey;
  private string $tmdbImportToken;

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

  private function loadEnvironment(): void
  {
    $this->postgrestUrl = $_ENV["POSTGREST_URL"] ?? getenv("POSTGREST_URL");
    $this->postgrestApiKey =
      $_ENV["POSTGREST_API_KEY"] ?? getenv("POSTGREST_API_KEY");
    $this->tmdbApiKey = $_ENV["TMDB_API_KEY"] ?? getenv("TMDB_API_KEY");
    $this->tmdbImportToken =
      $_ENV["WATCHING_IMPORT_TOKEN"] ?? getenv("WATCHING_IMPORT_TOKEN");
  }

  public function handleRequest(): void
  {
    $input = json_decode(file_get_contents("php://input"), true);

    if (!$input) $this->sendErrorResponse("Invalid or missing JSON body", 400);

    $providedToken = $input["token"] ?? null;

    if (!$providedToken || $providedToken !== $this->tmdbImportToken) $this->sendErrorResponse("Unauthorized access", 401);

    $tmdbId = $input["tmdb_id"] ?? null;
    $mediaType = $input["media_type"] ?? null;

    if (!$tmdbId || !$mediaType) $this->sendErrorResponse("tmdb_id and media_type are required", 400);

    try {
      $mediaData = $this->fetchTMDBData($tmdbId, $mediaType);
      $this->processMedia($mediaData, $mediaType);
      $this->sendResponse("Media imported successfully", 200);
    } catch (Exception $e) {
      $this->sendErrorResponse("Error: " . $e->getMessage(), 500);
    }
  }

  private function fetchTMDBData(string $tmdbId, string $mediaType): array
  {
    $client = new Client();
    $url = "https://api.themoviedb.org/3/{$mediaType}/{$tmdbId}";

    $response = $client->get($url, [
      "query" => ["api_key" => $this->tmdbApiKey],
      "headers" => ["Accept" => "application/json"],
    ]);

    $data = json_decode($response->getBody(), true);

    if (empty($data)) throw new Exception("No data found for TMDB ID: {$tmdbId}");

    return $data;
  }

  private function processMedia(array $mediaData, string $mediaType): void
  {
    $id = $mediaData["id"];
    $title = $mediaType === "movie" ? $mediaData["title"] : $mediaData["name"];
    $year =
      $mediaData["release_date"] ?? ($mediaData["first_air_date"] ?? null);
    $year = $year ? substr($year, 0, 4) : null;
    $description = $mediaData["overview"] ?? "";
    $tags = array_map(
      fn($genre) => strtolower(trim($genre["name"])),
      $mediaData["genres"]
    );
    $slug =
      $mediaType === "movie"
        ? "/watching/movies/{$id}"
        : "/watching/shows/{$id}";
    $payload = [
      "title" => $title,
      "year" => $year,
      "description" => $description,
      "tmdb_id" => $id,
      "slug" => $slug,
    ];
    $response = $this->fetchFromPostgREST(
      $mediaType === "movie" ? "movies" : "shows",
      "",
      "POST",
      $payload
    );

    if (empty($response["id"])) {
      $queryResponse = $this->fetchFromPostgREST(
        $mediaType === "movie" ? "movies" : "shows",
        "tmdb_id=eq.{$id}",
        "GET"
      );
      $response = $queryResponse[0] ?? [];
    }

    if (!empty($response["id"])) {
      $mediaId = $response["id"];
      $existingTagMap = $this->getTagIds($tags);
      $updatedTagMap = $this->insertMissingTags($tags, $existingTagMap);
      $this->associateTagsWithMedia(
        $mediaType,
        $mediaId,
        array_values($updatedTagMap)
      );
    }
  }

  private function getTagIds(array $tags): array
  {
    $existingTagMap = [];
    foreach ($tags as $tag) {
      $query = "name=ilike." . urlencode($tag);
      $existingTags = $this->fetchFromPostgREST("tags", $query, "GET");

      if (!empty($existingTags[0]["id"])) $existingTagMap[strtolower($tag)] = $existingTags[0]["id"];
    }
    return $existingTagMap;
  }

  private function insertMissingTags(array $tags, array $existingTagMap): array
  {
    $newTags = array_diff($tags, array_keys($existingTagMap));
    foreach ($newTags as $newTag) {
      try {
        $response = $this->fetchFromPostgREST("tags", "", "POST", [
          "name" => $newTag,
        ]);
        if (!empty($response["id"])) $existingTagMap[$newTag] = $response["id"];
      } catch (Exception $e) {
        $queryResponse = $this->fetchFromPostgREST(
          "tags",
          "name=eq.{$newTag}",
          "GET"
        );
        if (!empty($queryResponse[0]["id"])) $existingTagMap[$newTag] = $queryResponse[0]["id"];
      }
    }
    return $existingTagMap;
  }

  private function associateTagsWithMedia(
    string $mediaType,
    int $mediaId,
    array $tagIds
  ): void {
    $junctionTable = $mediaType === "movie" ? "movies_tags" : "shows_tags";
    $mediaColumn = $mediaType === "movie" ? "movies_id" : "shows_id";

    foreach ($tagIds as $tagId) {
      $this->fetchFromPostgREST($junctionTable, "", "POST", [
        $mediaColumn => $mediaId,
        "tags_id" => $tagId,
      ]);
    }
  }
}

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