<?php

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

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

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

  private string $mastodonAccessToken;
  private string $rssFeedUrl;
  private string $baseUrl;

  private const MASTODON_API_STATUS = "https://follow.coryd.dev/api/v1/statuses";

  private Client $httpClient;

  public function __construct(?Client $httpClient = null)
  {
    parent::__construct();
    $this->ensureCliAccess();
    $this->loadEnvironment();
    $this->validateAuthorization();
    $this->httpClient = $httpClient ?: new Client();
  }

  private function loadEnvironment(): void
  {
    $this->postgrestUrl =
      getenv("POSTGREST_URL") ?: $_ENV["POSTGREST_URL"] ?? "";
    $this->postgrestApiKey =
      getenv("POSTGREST_API_KEY") ?: $_ENV["POSTGREST_API_KEY"] ?? "";
    $this->mastodonAccessToken =
      getenv("MASTODON_ACCESS_TOKEN") ?: $_ENV["MASTODON_ACCESS_TOKEN"] ?? "";
    $this->rssFeedUrl = "https://www.coryd.dev/feeds/syndication.xml";
    $this->baseUrl = "https://www.coryd.dev";
  }

  private function validateAuthorization(): void
  {
    $authHeader = $_SERVER["HTTP_AUTHORIZATION"] ?? "";
    $expectedToken = "Bearer " . getenv("MASTODON_SYNDICATION_TOKEN");

    if ($authHeader !== $expectedToken) {
      http_response_code(401);
      echo json_encode(["error" => "Unauthorized."]);
      exit();
    }
  }

  public function handlePost(): void
  {
    if (!$this->isDatabaseAvailable()) {
      echo "Database is unavailable. Exiting.\n";
      return;
    }

    $latestItems = $this->fetchRSSFeed($this->rssFeedUrl);

    foreach (array_reverse($latestItems) as $item) {
      $existingPost = $this->fetchFromPostgREST("mastodon_posts", "link=eq." . urlencode($item["link"]));

      if (!empty($existingPost)) continue;

      $content = $this->truncateContent(
        $item["title"],
        str_replace(array("\n", "\r"), '', strip_tags($item["description"])),
        $item["link"],
        500
      );
      $timestamp = date("Y-m-d H:i:s");

      if (!$this->storeInDatabase($item["link"], $timestamp)) {
        echo "Skipping post: database write failed for {$item["link"]}\n";
        continue;
      }

      $mastodonPostUrl = $this->postToMastodon($content, $item["image"] ?? null);

      if ($mastodonPostUrl) {
        if (strpos($item["link"], $this->baseUrl . "/posts") !== false) {
          $slug = str_replace($this->baseUrl, "", $item["link"]);
          echo "Posted and stored URL: {$item["link"]}\n";
        }
      } else {
        echo "Failed to post to Mastodon. Skipping database update.\n";
      }
    }

    echo "RSS processed successfully.\n";
  }

  private function fetchRSSFeed(string $rssFeedUrl): array
  {
    $rssText = file_get_contents($rssFeedUrl);

    if (!$rssText) throw new Exception("Failed to fetch RSS feed.");

    $rss = new \SimpleXMLElement($rssText);
    $items = [];

    foreach ($rss->channel->item as $item) {
      $imageUrl = null;

      if ($item->enclosure && isset($item->enclosure['url'])) $imageUrl = (string) $item->enclosure['url'];

      $items[] = [
        "title" => (string) $item->title,
        "link" => (string) $item->link,
        "description" => (string) $item->description,
        "image" => $imageUrl,
      ];
    }

    return $items;
  }

  private function uploadImageToMastodon(string $imageUrl): ?string
  {
    $headers = [
      "Authorization" => "Bearer {$this->mastodonAccessToken}"
    ];

    $tempFile = tempnam(sys_get_temp_dir(), "mastodon_img");
    file_put_contents($tempFile, file_get_contents($imageUrl));

    $response = $this->httpClient->request("POST", "https://follow.coryd.dev/api/v2/media", [
      "headers" => $headers,
      "multipart" => [
        [
          "name" => "file",
          "contents" => fopen($tempFile, "r"),
          "filename" => basename($imageUrl)
        ]
      ]
    ]);

    unlink($tempFile);

    $statusCode = $response->getStatusCode();

    if ($statusCode !== 200) throw new Exception("Image upload failed with status $statusCode.");

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

    return $responseBody["id"] ?? null;
  }

  private function postToMastodon(string $content, ?string $imageUrl = null): ?string
  {
    $headers = [
      "Authorization" => "Bearer {$this->mastodonAccessToken}",
      "Content-Type" => "application/json",
    ];

    $mediaIds = [];

    if ($imageUrl) {
      try {
        $mediaId = $this->uploadImageToMastodon($imageUrl);
        if ($mediaId) $mediaIds[] = $mediaId;
      } catch (Exception $e) {
        echo "Image upload failed: " . $e->getMessage() . "\n";
      }
    }

    $postData = ["status" => $content];

    if (!empty($mediaIds)) $postData["media_ids"] = $mediaIds;

    $response = $this->httpRequest(
        self::MASTODON_API_STATUS,
        "POST",
        $headers,
        $postData
    );

    return $response["url"] ?? null;
  }

  private function storeInDatabase(string $link, string $timestamp): bool
  {
    $data = [
      "link" => $link,
      "created_at" => $timestamp,
    ];

    try {
      $this->fetchFromPostgREST("mastodon_posts", "", "POST", $data);
      return true;
    } catch (Exception $e) {
      echo "Error storing post in database: " . $e->getMessage() . "\n";
      return false;
    }
  }

  private function isDatabaseAvailable(): bool
  {
    try {
      $response = $this->fetchFromPostgREST("mastodon_posts", "limit=1");
      return is_array($response);
    } catch (Exception $e) {
      echo "Database check failed: " . $e->getMessage() . "\n";
      return false;
    }
  }

  private function truncateContent(
    string $title,
    string $description,
    string $link,
    int $maxLength
  ): string {
    $baseLength = strlen("$title\n\n$link");
    $availableSpace = $maxLength - $baseLength - 4;

    if (strlen($description) > $availableSpace) {
      $description = substr($description, 0, $availableSpace);
      $description = preg_replace('/\s+\S*$/', "", $description) . "...";
    }

    return "$title\n\n$description\n\n$link";
  }

  private function httpRequest(
    string $url,
    string $method = "GET",
    array $headers = [],
    ?array $data = null
  ): array {
    $options = ["headers" => $headers];

    if ($data) $options["json"] = $data;

    $response = $this->httpClient->request($method, $url, $options);
    $statusCode = $response->getStatusCode();

    if ($statusCode >= 400) throw new Exception("HTTP error $statusCode: " . $response->getBody());

    $responseBody = $response->getBody()->getContents();

    if (empty($responseBody)) return [];

    $decodedResponse = json_decode($responseBody, true);

    if (!is_array($decodedResponse)) return [];

    return $decodedResponse;
  }

  private function getPostgRESTHeaders(): array
  {
    return [
      "Authorization" => "Bearer {$this->postgrestApiKey}",
      "Content-Type" => "application/json",
    ];
  }
}

try {
  $handler = new MastodonPostHandler();
  $handler->handlePost();
} catch (Exception $e) {
  http_response_code(500);
  echo json_encode(["error" => $e->getMessage()]);
}