<?php

namespace Tests\Feature;

use App\Contracts\PromptLandingPageBriefResolver;
use App\Contracts\PromptLandingPageHtmlGenerator;
use App\DTOs\GeneratedLandingPageHtml;
use App\DTOs\PromptLandingPageBrief;
use App\Enums\PromptLandingPageVersionKind;
use App\Jobs\GeneratePromptLandingPageJob;
use App\Models\PromptLandingPage;
use App\Models\PromptLandingPageAttachment;
use App\Services\PromptLandingPageEditor\RecordPublishedVersion;
use App\Services\PromptLandingPageHtmlSanitizer;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Ai\StructuredAnonymousAgent;
use Tests\TestCase;

class GeneratePromptLandingPageJobTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_generates_a_single_landing_page_from_the_prompt(): void
    {
        StructuredAnonymousAgent::fake([
            $this->briefPayload([
                'logo_present' => true,
                'logo_type' => 'combination-mark',
                'logo_wordmark_text' => 'Austin Yoga',
                'logo_description' => 'A teal lotus icon beside the wordmark Austin Yoga.',
            ]),
            [
                'title' => 'Austin Yoga',
                'html' => '<section><h1>Yoga with us</h1></section>',
                'css' => 'body { margin: 0; }',
                'js' => '',
            ],
        ]);

        $page = PromptLandingPage::factory()->create(['status' => 'queued']);
        $page->messages()->create([
            'role' => 'user',
            'content' => 'Landing page for a yoga studio in Austin.',
        ]);

        (new GeneratePromptLandingPageJob($page->id))->handle(
            app(PromptLandingPageBriefResolver::class),
            app(PromptLandingPageHtmlGenerator::class),
            app(PromptLandingPageHtmlSanitizer::class),
            app(RecordPublishedVersion::class),
        );

        $page->refresh();

        $this->assertSame('pending_approval', $page->status);
        $this->assertStringContainsString('Yoga with us', $page->html);
        $this->assertNotEmpty($page->css);
        $this->assertNotEmpty($page->brief);
        $this->assertSame('#0f766e', $page->brief['primary_color']);
        $this->assertSame('#f7fffc', $page->brief['background_color']);
        $this->assertTrue($page->brief['logo_present']);
        $this->assertSame('combination-mark', $page->brief['logo_type']);
        $this->assertSame('Austin Yoga', $page->brief['logo_wordmark_text']);
        $this->assertSame('A teal lotus icon beside the wordmark Austin Yoga.', $page->brief['logo_description']);
        $this->assertStringContainsString('mobile-first', $page->prompt_used);
        $this->assertStringContainsString('@media queries for 767px and 991px', $page->prompt_used);
        $this->assertStringContainsString('Do not set html or body to height: 100% with overflow: hidden', $page->prompt_used);
        $this->assertStringContainsString('#0f766e', $page->prompt_used);
        $this->assertStringContainsString('#0d9488', $page->prompt_used);
        $this->assertStringContainsString('#f7fffc', $page->prompt_used);
        $this->assertStringContainsString('Theme / accent color is the theme', $page->prompt_used);
        $this->assertStringContainsString('A teal lotus icon beside the wordmark Austin Yoga.', $page->prompt_used);
        $this->assertStringContainsString('draw a simple inline SVG header logo', $page->prompt_used);
        $this->assertTrue($page->messages()->where('role', 'assistant')->exists());
        $this->assertDatabaseHas('prompt_landing_page_versions', [
            'prompt_landing_page_id' => $page->id,
            'version' => 1,
            'kind' => 'published',
        ]);
        $this->assertStringContainsString('Yoga with us', (string) $page->versions()->where('version', 1)->value('html'));
        $this->assertSame(1, $page->versions()->count());
    }

    public function test_it_resolves_the_brief_from_the_saved_website_and_purpose(): void
    {
        $page = PromptLandingPage::factory()->configured()->create(['status' => 'queued']);
        $page->messages()->create([
            'role' => 'user',
            'content' => 'Landing page for a yoga studio in Austin.',
        ]);

        $this->mock(PromptLandingPageBriefResolver::class, function ($mock) {
            $mock->shouldReceive('resolve')
                ->once()
                ->withArgs(function (string $prompt, array $attachments, ?string $websiteUrl, ?string $purpose): bool {
                    return $prompt === 'Landing page for a yoga studio in Austin.'
                        && $websiteUrl === 'https://yoga.example.com'
                        && $purpose === 'Leads Page';
                })
                ->andReturn(PromptLandingPageBrief::fromArray($this->briefPayload()));
        });

        $this->mock(PromptLandingPageHtmlGenerator::class, function ($mock) {
            $mock->shouldReceive('generate')
                ->once()
                ->andReturn(new GeneratedLandingPageHtml(
                    title: 'Austin Yoga',
                    html: '<section><h1>Yoga with us</h1></section>',
                    css: 'body { margin: 0; }',
                    js: '',
                    prompt: 'generate',
                ));
        });

        (new GeneratePromptLandingPageJob($page->id))->handle(
            app(PromptLandingPageBriefResolver::class),
            app(PromptLandingPageHtmlGenerator::class),
            app(PromptLandingPageHtmlSanitizer::class),
            app(RecordPublishedVersion::class),
        );

        $page->refresh();

        $this->assertSame('pending_approval', $page->status);
    }

    public function test_it_revises_existing_html_without_resolving_a_new_brief(): void
    {
        $page = PromptLandingPage::factory()->pendingApproval()->create([
            'brief' => $this->briefPayload(),
        ]);
        $page->messages()->create([
            'role' => 'user',
            'content' => 'Make the CTA green.',
        ]);

        $this->mock(PromptLandingPageBriefResolver::class, function ($mock) {
            $mock->shouldReceive('resolve')->never();
        });

        $this->mock(PromptLandingPageHtmlGenerator::class, function ($mock) {
            $mock->shouldReceive('generate')
                ->once()
                ->andReturn(new GeneratedLandingPageHtml(
                    title: 'Updated yoga',
                    html: '<section><h1>Green CTA</h1></section>',
                    css: 'a { color: green; }',
                    js: '',
                    prompt: 'revise',
                ));
        });

        (new GeneratePromptLandingPageJob($page->id))->handle(
            app(PromptLandingPageBriefResolver::class),
            app(PromptLandingPageHtmlGenerator::class),
            app(PromptLandingPageHtmlSanitizer::class),
            app(RecordPublishedVersion::class),
        );

        $page->refresh();

        $this->assertSame('pending_approval', $page->status);
        $this->assertStringContainsString('Green CTA', $page->html);
        $this->assertSame('Updated yoga', $page->title);
        $this->assertSame(2, $page->versions()->count());
        $this->assertStringContainsString('Yoga studio', (string) $page->versions()->where('version', 1)->value('html'));
        $this->assertSame(PromptLandingPageVersionKind::Published, $page->versions()->where('version', 1)->first()?->kind);
        $this->assertStringContainsString('Green CTA', (string) $page->versions()->where('version', 2)->value('html'));
        $this->assertSame(PromptLandingPageVersionKind::Published, $page->versions()->where('version', 2)->first()?->kind);
    }

    public function test_it_marks_the_page_as_failed_when_generation_throws(): void
    {
        $page = PromptLandingPage::factory()->create(['status' => 'queued']);
        $page->messages()->create([
            'role' => 'user',
            'content' => 'Make a page',
        ]);

        $this->mock(PromptLandingPageBriefResolver::class, function ($mock) {
            $mock->shouldReceive('resolve')->once()->andThrow(new \RuntimeException('Provider unavailable'));
        });

        try {
            (new GeneratePromptLandingPageJob($page->id))->handle(
                app(PromptLandingPageBriefResolver::class),
                app(PromptLandingPageHtmlGenerator::class),
                app(PromptLandingPageHtmlSanitizer::class),
                app(RecordPublishedVersion::class),
            );
            $this->fail('Expected exception was not thrown.');
        } catch (\RuntimeException $e) {
            $this->assertSame('Provider unavailable', $e->getMessage());
        }

        $page->refresh();

        $this->assertSame('failed', $page->status);
        $this->assertSame('Provider unavailable', $page->failure_reason);
        $this->assertTrue($page->messages()->where('role', 'assistant')->exists());
    }

    public function test_it_strips_attachment_tokens_from_generated_html(): void
    {
        $page = PromptLandingPage::factory()->create([
            'status' => 'queued',
            'brief' => $this->briefPayload(),
        ]);
        $page->messages()->create([
            'role' => 'user',
            'content' => 'Use the attached brief.',
        ]);
        PromptLandingPageAttachment::factory()->create([
            'prompt_landing_page_id' => $page->id,
            'path' => 'prompt-landing-pages/9/client-brief-pack.pdf',
            'original_name' => 'client-brief-pack.pdf',
            'included_in_brief' => true,
        ]);

        $this->mock(PromptLandingPageBriefResolver::class, function ($mock) {
            $mock->shouldReceive('resolve')->never();
        });

        $this->mock(PromptLandingPageHtmlGenerator::class, function ($mock) {
            $mock->shouldReceive('generate')->once()->andReturn(new GeneratedLandingPageHtml(
                title: 'Yoga',
                html: '<section><img src="/storage/prompt-landing-pages/9/client-brief-pack.pdf"></section>',
                css: 'body { background: url(client-brief-pack.pdf); }',
                js: '',
                prompt: 'generate',
            ));
        });

        (new GeneratePromptLandingPageJob($page->id))->handle(
            app(PromptLandingPageBriefResolver::class),
            app(PromptLandingPageHtmlGenerator::class),
            app(PromptLandingPageHtmlSanitizer::class),
            app(RecordPublishedVersion::class),
        );

        $page->refresh();

        $this->assertStringNotContainsString('prompt-landing-pages/', $page->html);
        $this->assertStringNotContainsString('client-brief-pack.pdf', $page->html);
        $this->assertStringNotContainsString('client-brief-pack.pdf', $page->css);
    }

    public function test_it_does_not_change_an_approved_page(): void
    {
        $page = PromptLandingPage::factory()->approved()->create([
            'html' => '<section><h1>Keep me</h1></section>',
        ]);

        $this->mock(PromptLandingPageBriefResolver::class, function ($mock) {
            $mock->shouldReceive('resolve')->never();
        });

        $this->mock(PromptLandingPageHtmlGenerator::class, function ($mock) {
            $mock->shouldReceive('generate')->never();
        });

        (new GeneratePromptLandingPageJob($page->id))->handle(
            app(PromptLandingPageBriefResolver::class),
            app(PromptLandingPageHtmlGenerator::class),
            app(PromptLandingPageHtmlSanitizer::class),
            app(RecordPublishedVersion::class),
        );

        $page->refresh();

        $this->assertSame('approved', $page->status);
        $this->assertStringContainsString('Keep me', $page->html);
    }

    /**
     * @param  array<string, mixed>  $overrides
     * @return array<string, mixed>
     */
    private function briefPayload(array $overrides = []): array
    {
        return array_merge([
            'brand_name' => 'Austin Yoga',
            'offering' => 'Studio membership',
            'headline' => 'Find your calm in Austin',
            'features' => ['Small classes'],
            'benefits' => ['Less stress'],
            'pricing' => '$99 / month',
            'audience' => 'Busy professionals',
            'usps' => ['Downtown location'],
            'pain_points' => ['Crowded gyms'],
            'tone' => 'Calm and warm',
            'cta' => 'Book a class',
            'primary_color' => '#0f766e',
            'secondary_color' => '#14b8a6',
            'accent_color' => '#0d9488',
            'background_color' => '#f7fffc',
            'text_color' => '#0f172a',
            'logo_present' => false,
            'logo_type' => 'unknown',
            'logo_wordmark_text' => '',
            'logo_description' => '',
            'notes' => 'Earth tones',
        ], $overrides);
    }
}
