<?php

namespace App\Modules\Lemmy\Services;

use App\Exceptions\PlatformAuthException;
use App\Models\Article;
use App\Models\PlatformAccount;
use App\Models\PlatformChannel;
use App\Services\Auth\LemmyAuthService;
use App\Services\Log\LogSaver;
use Exception;

class LemmyPublisher
{
    private LemmyApiService $api;

    private PlatformAccount $account;

    private ThumbnailUploader $thumbnailUploader;

    public function __construct(PlatformAccount $account)
    {
        $this->api = new LemmyApiService($account->instance_url);
        $this->account = $account;
        $this->thumbnailUploader = new ThumbnailUploader($account->instance_url);
    }

    /**
     * @param  array<string, mixed>  $extractedData
     * @return array<string, mixed>
     *
     * @throws PlatformAuthException
     * @throws Exception
     */
    public function publishToChannel(Article $article, array $extractedData, PlatformChannel $channel): array
    {
        $authService = resolve(LemmyAuthService::class);
        $token = $authService->getToken($this->account);

        $thumbnail = $this->hostedThumbnail($extractedData, $channel, $article, $token);

        try {
            return $this->createPost($token, $extractedData, $channel, $article, $thumbnail);
        } catch (Exception $e) {
            // If the cached token was stale, refresh and retry once
            if (str_contains($e->getMessage(), 'not_logged_in') || str_contains($e->getMessage(), 'Unauthorized')) {
                $token = $authService->refreshToken($this->account);

                return $this->createPost($token, $extractedData, $channel, $article, $thumbnail);
            }
            throw $e;
        }
    }

    /**
     * Uploaded once per publish, outside the stale-token retry: the upload is the expensive
     * part and a retry would otherwise re-download, re-encode and re-log.
     *
     * @param  array<string, mixed>  $extractedData
     */
    private function hostedThumbnail(array $extractedData, PlatformChannel $channel, Article $article, string $token): ?string
    {
        $source = $extractedData['thumbnail'] ?? null;
        $source = is_string($source) && $source !== '' ? $source : null;

        if ($source === null) {
            return null;
        }

        $hosted = $this->thumbnailUploader->upload($source, $token);

        if ($hosted === null) {
            app(LogSaver::class)->warning('Thumbnail upload failed; publishing without one', $channel, [
                'article_id' => $article->id,
                'source' => $source,
            ]);
        }

        return $hosted;
    }

    /**
     * @param  array<string, mixed>  $extractedData
     * @return array<string, mixed>
     */
    private function createPost(string $token, array $extractedData, PlatformChannel $channel, Article $article, ?string $thumbnail = null): array
    {
        $languageId = $extractedData['language_id'] ?? null;

        return $this->api->createPost(
            $token,
            $extractedData['title'] ?? 'Untitled',
            $extractedData['description'] ?? '',
            $channel->channel_id,
            $article->url,
            $thumbnail,
            $languageId
        );
    }
}
