<?php

namespace FSPoster\App\Watermark\Api;

use Exception;
use FSPoster\App\Providers\Core\Settings;
use FSPoster\App\Watermark\App\DTOs\Response\WatermarkUsageResult;
use FSPoster\GuzzleHttp\Client;
use FSPoster\GuzzleHttp\Exception\GuzzleException;
use FSPoster\GuzzleHttp\Exception\RequestException;
use FSPoster\Psr\Http\Message\ResponseInterface;
use Throwable;

class Api
{
    private const BASE_URI          = 'https://www.fs-poster.com';
    private const POLL_INTERVAL     = 3;
    private const POLL_MAX_ATTEMPTS = 60;

    private function getClient(): Client
    {
        return new Client([
            'base_uri' => self::BASE_URI,
            'timeout'  => 30,
            'headers'  => [
                'Accept'        => 'application/json',
            ]
        ]);
    }

    /**
     * @throws GuzzleException
     */
    private function request(string $method, string $uri, array $options = []): ResponseInterface
    {
        $defaults = [
            'license_code' => Settings::get('license_code', '', true),
            'domain'       => parse_url(network_site_url(), PHP_URL_HOST),
        ];

        $method = strtoupper($method);

        if ($method === 'GET') {
            $options['query'] = array_merge($defaults, $options['query'] ?? []);
        } else {
            $options['json'] = array_merge($defaults, $options['json'] ?? []);
        }

        try {
            return $this->getClient()->request($method, $uri, $options);
        } catch (RequestException $e) {
            $response = $e->getResponse();

            if ($response !== null) {
                $body = json_decode($response->getBody()->getContents(), true);

                if (!empty($body['message'])) {
                    throw new Exception($body['message'], $e->getCode(), $e);
                }
            }

            throw $e;
        }
    }

    /**
     * @throws GuzzleException
     * @throws Exception
     */
    public function processMedia(array $media, string $socialNetwork, array $rules): array
    {
        $response = $this->request('POST', '/watermark-api', [
            'json' => [
                'media_url'      => $media['url'],
                'media_type'     => $media['type'],
                'rules'         => array_values($rules),
                'social_network' => $socialNetwork,
            ]
        ]);

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

        if (empty($body['jobId'])) {
            throw new Exception($body['message'] ?? fsp__('Watermark API did not return a job ID'));
        }

        $downloadUrl = $this->pollUntilComplete($body['jobId']);

        if (empty($downloadUrl)) {
            throw new Exception(fsp__('Watermark processing failed'));
        }

        return $this->downloadAndReplace($media, $downloadUrl);
    }

    /**
     * @throws GuzzleException
     * @throws Exception
     */
    private function pollUntilComplete(string $jobId): ?string
    {
        $errorMessage = null;
        for ($i = 0; $i < self::POLL_MAX_ATTEMPTS; $i++) {
            sleep(self::POLL_INTERVAL);

            $response = $this->request('GET', '/watermark-api/' . $jobId);
            $body = json_decode($response->getBody()->getContents(), true);
            $status = $body['status'] ?? '';
            $errorMessage = $body['error'] ?? null;

            if ($status === 'done' && !empty($body['download_url'])) {
                return $body['download_url'];
            }

            if ($status === 'failed') {
                break;
            }
        }

        if (!empty($errorMessage)) {
            throw new Exception($errorMessage);
        }

        return null;
    }

    /**
     * @throws GuzzleException
     * @throws Exception
     */
    private function downloadAndReplace(array $media, string $downloadUrl): array
    {
        $uploadDir = wp_upload_dir();
        $watermarkDir = $uploadDir['basedir'] . '/fsp/watermark';
        $watermarkUrl = $uploadDir['baseurl'] . '/fsp/watermark';

        if (!file_exists($watermarkDir)) {
            wp_mkdir_p($watermarkDir);
        }

        $extension = pathinfo(parse_url($media['url'], PHP_URL_PATH), PATHINFO_EXTENSION) ?: 'jpg';
        $filename  = 'watermarked_' . uniqid() . '.' . $extension;
        $filePath  = $watermarkDir . '/' . $filename;

        $this->getClient()->get($downloadUrl, [
            'sink'    => $filePath,
            'timeout' => 120,
        ]);

        $filetype = wp_check_filetype($filename, null);
        $attachment = [
            'post_mime_type' => $filetype['type'],
            'post_title'     => sanitize_file_name($filename),
            'post_content'   => '',
            'post_status'    => 'inherit',
            'post_parent'    => $media['id'] ?? 0, // Link to original media
        ];

        $attach_id = wp_insert_attachment($attachment, $filePath);

        if (is_wp_error($attach_id) || !$attach_id) {
            throw new Exception(fsp__('Failed to create watermarked attachment'));
        }

        require_once ABSPATH . 'wp-admin/includes/image.php';
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/media.php';

        $attach_data = wp_generate_attachment_metadata($attach_id, $filePath);
        wp_update_attachment_metadata($attach_id, $attach_data);

        update_post_meta($attach_id, '_fsp_original_attachment_id', $media['id'] ?? 0);
        update_post_meta($attach_id, '_fsp_is_watermarked', true);

        return array_merge($media, [
            'url'  => wp_get_attachment_url($attach_id),
            'path' => $filePath,
            'id'   => $attach_id,
        ]);
    }

    public function getWatermarkUsage(): WatermarkUsageResult
    {
        try {
            $response = $this->request('GET', '/watermark-api/usage');
            $body = json_decode($response->getBody()->getContents(), true);

            if (!is_array($body)) {
                return WatermarkUsageResult::failure(fsp__('Invalid response from watermark API'));
            }

            $usage = $body['usage'] ?? [];
            $image = isset($usage['image']) ? (int) $usage['image'] : 0;
            $video = isset($usage['video']) ? (int) $usage['video'] : 0;

            return WatermarkUsageResult::success($image, $video);
        } catch (Throwable $e) {
            return WatermarkUsageResult::failure($e->getMessage());
        }
    }
}
