<?php

namespace Tests\Unit\Services;

use App\Exceptions\HiggsfieldRenderFailedException;
use App\Exceptions\HiggsfieldRenderPendingException;
use App\Exceptions\ProviderNotConfiguredException;
use App\Exceptions\VideoPromptFilteredException;
use App\Models\Avatar;
use App\Models\Voice;
use App\Services\AI\HiggsfieldSeedanceVideoAvatarProvider;
use Illuminate\Log\Events\MessageLogged;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use InvalidArgumentException;
use Tests\TestCase;

class HiggsfieldSeedanceVideoAvatarProviderTest extends TestCase
{
    private function script(): array
    {
        return [
            'hook' => 'Tired of slow browsing?',
            'problem' => 'Ads slow you down.',
            'product' => 'Chromium Browser',
            'benefits' => 'Faster pages, no clutter.',
            'cta' => 'Download free today.',
            'spoken_line' => 'Discover faster browsing. Download today.',
        ];
    }

    private function providerConfig(): array
    {
        return [
            'creative.disk' => 'public',
            'creative.higgsfield_seedance.key_id' => 'test-key-id',
            'creative.higgsfield_seedance.key_secret' => 'test-key-secret',
            'creative.higgsfield_seedance.base_url' => 'https://api.higgsfield.ai',
            'creative.higgsfield_model' => 'wan-3.0',
            'creative.higgsfield_resolution' => null,
            'creative.higgsfield_seedance.poll_interval_seconds' => 0,
            'creative.higgsfield_seedance.max_poll_attempts' => 3,
        ];
    }

    public function test_it_throws_when_higgsfield_credentials_are_missing(): void
    {
        config([
            'creative.higgsfield_seedance.key_id' => null,
            'creative.higgsfield_seedance.key_secret' => null,
        ]);

        $this->expectException(ProviderNotConfiguredException::class);

        (new HiggsfieldSeedanceVideoAvatarProvider)->render(
            new Avatar(['name' => 'Ava', 'gender' => 'female', 'accent' => 'American']),
            new Voice(['name' => 'Kore', 'tone' => 'warm', 'accent' => 'American']),
            $this->script(),
            [['scene' => 'Opening shot']],
            duration: 8,
            ratio: '9:16',
        );
    }

    public function test_it_submits_a_seedance_text_to_video_request_and_stores_the_clip(): void
    {
        Storage::fake('public');
        config($this->providerConfig());

        $loggedPrompt = null;
        Log::listen(function (MessageLogged $event) use (&$loggedPrompt): void {
            if ($event->message === 'Higgsfield generation request') {
                $loggedPrompt = $event->context['prompt'] ?? null;
            }
        });

        Http::fake([
            'api.higgsfield.ai/alibaba/wan-3.0/text-to-video' => Http::response([
                'status' => 'queued',
                'request_id' => 'req_seedance',
                'status_url' => 'https://api.higgsfield.ai/requests/req_seedance/status',
            ]),
            'api.higgsfield.ai/requests/req_seedance/status' => Http::sequence()
                ->push(['status' => 'in_progress', 'request_id' => 'req_seedance'])
                ->push([
                    'status' => 'completed',
                    'request_id' => 'req_seedance',
                    'video' => ['url' => 'https://cdn.example.com/seedance-output.mp4'],
                ]),
            'cdn.example.com/seedance-output.mp4' => Http::response('SEEDANCEBYTES', 200),
        ]);

        $result = (new HiggsfieldSeedanceVideoAvatarProvider)->render(
            new Avatar(['name' => 'Ava', 'gender' => 'female', 'accent' => 'American']),
            new Voice(['name' => 'Kore', 'tone' => 'warm', 'accent' => 'American']),
            $this->script(),
            [['scene' => 'Opening shot']],
            duration: 15,
            ratio: '9:16',
        );

        $this->assertSame('higgsfield-seedance', $result->meta['provider']);
        $this->assertSame('higgsfield-seedance', $result->meta['engine']);
        $this->assertSame('alibaba/wan-3.0', $result->meta['model']);
        $this->assertSame('req_seedance', $result->meta['request_id']);
        $this->assertSame(15, $result->meta['duration']);
        $this->assertSame(64, strlen($result->meta['prompt_hash']));
        $this->assertIsString($loggedPrompt);
        $this->assertStringContainsString('Discover faster browsing. Download today.', $loggedPrompt);
        Storage::disk('public')->assertExists($result->assetPath);
        $this->assertSame('SEEDANCEBYTES', Storage::disk('public')->get($result->assetPath));

        Http::assertSent(function ($request): bool {
            return $request->url() === 'https://api.higgsfield.ai/alibaba/wan-3.0/text-to-video'
                && $request->hasHeader('Authorization', 'Key test-key-id:test-key-secret')
                && $request['duration'] === 15
                && $request['aspect_ratio'] === '9:16'
                && $request['resolution'] === '480p'
                && $request['generate_audio'] === true
                && ! array_key_exists('output_format', $request->data())
                && ! array_key_exists('bitrate_mode', $request->data())
                && str_contains($request['prompt'], 'Discover faster browsing. Download today.')
                && ! array_key_exists('image_url', $request->data());
        });
        Http::assertSent(fn ($request): bool => $request->url() === 'https://cdn.example.com/seedance-output.mp4'
            && ! $request->hasHeader('Authorization'));
        Http::assertNotSent(fn ($request): bool => str_contains($request->url(), 'generativelanguage.googleapis.com'));
    }

    public function test_it_uses_seedance_when_that_model_is_configured(): void
    {
        Storage::fake('public');
        config([
            ...$this->providerConfig(),
            'creative.higgsfield_model' => 'seedance-2.5',
        ]);

        Http::fake([
            'api.higgsfield.ai/bytedance/seedance-2.5/text-to-video' => Http::response([
                'status' => 'queued',
                'request_id' => 'req_seedance',
                'status_url' => 'https://api.higgsfield.ai/requests/req_seedance/status',
            ]),
            'api.higgsfield.ai/requests/req_seedance/status' => Http::response([
                'status' => 'completed',
                'request_id' => 'req_seedance',
                'video' => ['url' => 'https://cdn.example.com/seedance-output.mp4'],
            ]),
            'cdn.example.com/seedance-output.mp4' => Http::response('SEEDANCEBYTES', 200),
        ]);

        $result = (new HiggsfieldSeedanceVideoAvatarProvider)->render(
            new Avatar(['name' => 'Ava']),
            new Voice(['name' => 'Kore']),
            $this->script(),
            [['scene' => 'Opening shot']],
            duration: 8,
            ratio: '16:9',
        );

        $this->assertSame('bytedance/seedance-2.5', $result->meta['model']);
        Http::assertSent(fn ($request): bool => $request->url() === 'https://api.higgsfield.ai/bytedance/seedance-2.5/text-to-video'
            && $request['resolution'] === '480p'
            && $request['generate_audio'] === true
            && str_contains($request['prompt'], '- Presenter: "Discover faster browsing. Download today."')
            && str_contains($request['prompt'], 'The last word lands at about 7 seconds.')
            && ! str_contains($request['prompt'], 'SPOKEN LINE'));
    }

    public function test_it_sends_brand_names_in_the_spoken_line_without_spelling_them(): void
    {
        Storage::fake('public');
        config($this->providerConfig());

        Http::fake([
            'api.higgsfield.ai/alibaba/wan-3.0/text-to-video' => Http::response([
                'status' => 'queued',
                'request_id' => 'req_brand',
                'status_url' => 'https://api.higgsfield.ai/requests/req_brand/status',
            ]),
            'api.higgsfield.ai/requests/req_brand/status' => Http::response([
                'status' => 'completed',
                'request_id' => 'req_brand',
                'video' => ['url' => 'https://cdn.example.com/seedance-output.mp4'],
            ]),
            'cdn.example.com/seedance-output.mp4' => Http::response('SEEDANCEBYTES', 200),
        ]);

        $script = $this->script();
        $script['spoken_line'] = 'This PUMA white tee keeps casual styling simple; shop the PUMA.';

        (new HiggsfieldSeedanceVideoAvatarProvider)->render(
            new Avatar(['name' => 'Ava']),
            new Voice(['name' => 'Kore']),
            $script,
            [['scene' => 'Show the PUMA tee.']],
            duration: 15,
            ratio: '9:16',
            context: [
                'project_name' => 'PUMA',
                'brief' => [
                    'product_name' => 'PUMA printed white T-shirt',
                    'visual_description' => 'White short-sleeve tee with a large thin black outline of the leaping cat.',
                ],
            ],
        );

        Http::assertSent(function ($request): bool {
            if ($request->url() !== 'https://api.higgsfield.ai/alibaba/wan-3.0/text-to-video') {
                return false;
            }

            $prompt = (string) $request['prompt'];

            return str_contains($prompt, 'A scroll-stopping, high-energy talking-presenter ad. Cinematic 4K, vertical 9:16.')
                && str_contains($prompt, 'SPOKEN LINE (exactly 11 words, first word "This", last word "PUMA."):')
                && str_contains($prompt, '"This PUMA white tee keeps casual styling simple; shop the PUMA."')
                && str_contains($prompt, 'the very first sound in the video is the word "This"')
                && str_contains($prompt, 'ending on the word "PUMA."')
                && str_contains($prompt, 'the only words in the entire video are the spoken line')
                && str_contains($prompt, 'Shot 1: Show the PUMA tee.')
                && str_contains($prompt, 'Product: Brand: PUMA. PUMA printed white T-shirt.')
                && ! str_contains($prompt, '- Presenter:')
                && ! str_contains($prompt, 'P U M A')
                && ! str_contains($prompt, 'Pronounce separated capital letters individually')
                && ! str_contains($prompt, 'glowing');
        });
    }

    public function test_a_display_brief_keeps_a_heavy_product_out_of_the_presenters_hands(): void
    {
        Storage::fake('public');
        config($this->providerConfig());

        Http::fake([
            'api.higgsfield.ai/alibaba/wan-3.0/text-to-video' => Http::response([
                'status' => 'queued',
                'request_id' => 'req_heavy',
                'status_url' => 'https://api.higgsfield.ai/requests/req_heavy/status',
            ]),
            'api.higgsfield.ai/requests/req_heavy/status' => Http::response([
                'status' => 'completed',
                'request_id' => 'req_heavy',
                'video' => ['url' => 'https://cdn.example.com/seedance-output.mp4'],
            ]),
            'cdn.example.com/seedance-output.mp4' => Http::response('SEEDANCEBYTES', 200),
        ]);

        (new HiggsfieldSeedanceVideoAvatarProvider)->render(
            new Avatar(['name' => 'Ava']),
            new Voice(['name' => 'Kore']),
            $this->script(),
            [['scene' => 'The presenter holds up the printer.']],
            duration: 15,
            ratio: '9:16',
            context: [
                'project_name' => 'HP',
                'brief' => [
                    'product_name' => 'HP Smart Tank printer',
                    'visual_description' => 'A white desktop all-in-one printer.',
                    'handling' => 'display',
                ],
            ],
        );

        Http::assertSent(function ($request): bool {
            if ($request->url() !== 'https://api.higgsfield.ai/alibaba/wan-3.0/text-to-video') {
                return false;
            }

            $prompt = (string) $request['prompt'];

            return str_contains($prompt, 'Handling: the product is heavy and large.')
                && str_contains($prompt, 'The presenter NEVER picks it up')
                && str_contains($prompt, 'gestures toward the')
                && ! str_contains($prompt, 'light and easy to hold')
                && ! str_contains($prompt, 'holds up the printer');
        });
    }

    public function test_it_rejects_durations_outside_the_seedance_range(): void
    {
        config($this->providerConfig());

        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('4 to 30');

        (new HiggsfieldSeedanceVideoAvatarProvider)->render(
            new Avatar(['name' => 'Ava']),
            new Voice(['name' => 'Kore']),
            $this->script(),
            [['scene' => 'Opening shot']],
            duration: 31,
            ratio: '16:9',
        );
    }

    public function test_it_maps_content_moderation_to_the_shared_safety_exception(): void
    {
        config($this->providerConfig());

        Http::fake([
            'api.higgsfield.ai/alibaba/wan-3.0/text-to-video' => Http::response([
                'status' => 'queued',
                'request_id' => 'req_nsfw',
                'status_url' => 'https://api.higgsfield.ai/requests/req_nsfw/status',
            ]),
            'api.higgsfield.ai/requests/req_nsfw/status' => Http::response([
                'status' => 'nsfw',
                'request_id' => 'req_nsfw',
            ]),
        ]);

        $this->expectException(VideoPromptFilteredException::class);

        (new HiggsfieldSeedanceVideoAvatarProvider)->render(
            new Avatar(['name' => 'Ava']),
            new Voice(['name' => 'Kore']),
            $this->script(),
            [['scene' => 'Opening shot']],
            duration: 8,
            ratio: '16:9',
        );
    }

    public function test_it_stays_pending_when_the_poll_window_ends_before_a_terminal_status(): void
    {
        config($this->providerConfig());

        Http::fake([
            'api.higgsfield.ai/alibaba/wan-3.0/text-to-video' => Http::response([
                'status' => 'queued',
                'request_id' => 'req_wait',
                'status_url' => 'https://api.higgsfield.ai/requests/req_wait/status',
            ]),
            'api.higgsfield.ai/requests/req_wait/status' => Http::response([
                'status' => 'queued',
                'request_id' => 'req_wait',
            ]),
        ]);

        try {
            (new HiggsfieldSeedanceVideoAvatarProvider)->render(
                new Avatar(['name' => 'Ava']),
                new Voice(['name' => 'Kore']),
                $this->script(),
                [['scene' => 'Opening shot']],
                duration: 8,
                ratio: '16:9',
            );
            $this->fail('Expected the open Higgsfield request to stay pending.');
        } catch (HiggsfieldRenderPendingException $exception) {
            $this->assertSame('req_wait', $exception->requestId);
            $this->assertSame('https://api.higgsfield.ai/requests/req_wait/status', $exception->statusUrl);
        }
    }

    public function test_it_logs_the_full_vendor_response_when_submission_is_rejected(): void
    {
        config($this->providerConfig());

        Http::fake([
            'api.higgsfield.ai/alibaba/wan-3.0/text-to-video' => Http::response(
                ['detail' => 'Insufficient credits'],
                403,
                ['X-Correlation-ID' => 'corr-123'],
            ),
        ]);

        $logged = null;
        Log::listen(function (MessageLogged $event) use (&$logged): void {
            if ($event->message === 'Higgsfield generation request failed') {
                $logged = $event->context;
            }
        });

        try {
            (new HiggsfieldSeedanceVideoAvatarProvider)->render(
                new Avatar(['name' => 'Ava']),
                new Voice(['name' => 'Kore']),
                $this->script(),
                [['scene' => 'Opening shot']],
                duration: 30,
                ratio: '16:9',
            );
            $this->fail('Expected the rejected submission to throw.');
        } catch (HiggsfieldRenderFailedException $exception) {
            $this->assertSame('credits', $exception->reason);
            $this->assertSame(403, $exception->httpStatus);
            $this->assertSame(['detail' => 'Insufficient credits'], $exception->body);
            $this->assertStringContainsString('Higgsfield generation request failed', $exception->getMessage());
        }

        $this->assertSame(403, $logged['status']);
        $this->assertSame('corr-123', $logged['correlation_id']);
        $this->assertSame(['detail' => 'Insufficient credits'], $logged['body']);
    }
}
