<?php

namespace Tests\Feature;

use App\DTOs\LandingPageAnalysis;
use App\Models\Brief;
use App\Models\Campaign;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

/**
 * The brief: what is being advertised, independent of where it runs.
 *
 * Every field a campaign needs used to live on `campaigns`, and `campaigns` has
 * a `platform` column, so advertising the same thing on a second platform would
 * ask for all of it again. The brief is where an answer that is true regardless
 * of platform lives.
 *
 * What these pin: a brief can hold the shared answers, several platforms'
 * campaigns can hang off one, and an adopted campaign - one already running on
 * the platform, pulled in to be edited - keeps working with no brief at all.
 */
class BriefTest extends TestCase
{
    use RefreshDatabase;

    protected User $user;

    protected function setUp(): void
    {
        parent::setUp();

        $this->user = User::factory()->create();
    }

    private function brief(array $attributes = []): Brief
    {
        return Brief::create([
            'user_id' => $this->user->id,
            'conversation_id' => 'conv-brief-0001',
            ...$attributes,
        ]);
    }

    private function campaign(string $platform, ?Brief $brief = null): Campaign
    {
        return Campaign::create([
            'user_id' => $this->user->id,
            'brief_id' => $brief?->id,
            'name' => ucfirst($platform).' campaign',
            'platform' => $platform,
            'status' => 'draft',
        ]);
    }

    public function test_it_holds_the_answers_that_do_not_change_by_platform(): void
    {
        $brief = $this->brief([
            'landing_url' => 'https://example.com/shoes',
            'promoting' => 'Trail running shoes',
            'outcome' => 'sales',
            'locations' => ['countries' => ['US', 'CA']],
            'budget' => 50,
            'budget_mode' => 'daily',
            'currency' => 'USD',
        ]);

        $fresh = $brief->fresh();

        $this->assertSame('Trail running shoes', $fresh->promoting);
        $this->assertSame(['countries' => ['US', 'CA']], $fresh->locations);
        $this->assertEquals(50, $fresh->budget);
    }

    /**
     * The outcome is the plain answer, never one platform's enum.
     *
     * Translating "sales" into OUTCOME_SALES or a Google conversion goal is the
     * platform's job. Storing a platform's word here would make the brief mean
     * something different depending on who reads it.
     */
    public function test_the_outcome_is_stored_in_plain_words(): void
    {
        $brief = $this->brief(['outcome' => 'sales']);

        $this->assertSame('sales', $brief->fresh()->outcome);
    }

    public function test_one_brief_carries_campaigns_on_several_platforms(): void
    {
        $brief = $this->brief();
        $this->campaign('meta', $brief);
        $this->campaign('google', $brief);

        $this->assertCount(2, $brief->fresh()->campaigns);
        $this->assertEqualsCanonicalizing(['meta', 'google'], $brief->fresh()->platforms());
    }

    public function test_the_money_needs_splitting_only_when_there_is_more_than_one_platform(): void
    {
        $brief = $this->brief(['budget' => 50]);
        $this->campaign('meta', $brief);

        $this->assertFalse($brief->fresh()->needsBudgetSplit());

        $this->campaign('google', $brief);

        $this->assertTrue($brief->fresh()->needsBudgetSplit());
    }

    /**
     * And stops needing it once it has been done.
     *
     * The question is whether the division is outstanding, not whether there
     * are several platforms. Answering the second one meant this stayed true
     * forever, so nothing could use it to tell a split budget from an unsplit
     * one, which is the only thing it is good for.
     */
    public function test_the_money_no_longer_needs_splitting_once_every_platform_holds_a_share(): void
    {
        $brief = $this->brief(['budget' => 50]);
        $this->campaign('meta', $brief)->update(['budget' => 25]);
        $this->campaign('google', $brief)->update(['budget' => 25]);

        $this->assertFalse($brief->fresh()->needsBudgetSplit());
    }

    /**
     * An adopted campaign has no brief, and editing it must still work.
     *
     * Adopted campaigns are real campaigns already running on the platform,
     * pulled in so a buyer can change them here. They arrive carrying their own
     * objective, budget, bidding, schedule and targeting, and were never the
     * product of a brief. That is the reason brief_id is nullable, and the
     * reason nothing may assume it is set.
     */
    public function test_an_adopted_campaign_has_no_brief_and_keeps_its_own_values(): void
    {
        $campaign = Campaign::create([
            'user_id' => $this->user->id,
            'name' => 'Imported campaign',
            'platform' => 'meta',
            'status' => 'published',
            'external_campaign_id' => '23850000000000001',
            'objective' => 'OUTCOME_SALES',
            'budget' => 40,
            'budget_mode' => 'daily',
            'locations' => ['countries' => ['US']],
        ]);

        $fresh = $campaign->fresh();

        $this->assertNull($fresh->brief_id);
        $this->assertNull($fresh->brief);

        // Everything it needs is on the row itself.
        $this->assertSame('OUTCOME_SALES', $fresh->objective);
        $this->assertEquals(40, $fresh->budget);
        $this->assertSame(['countries' => ['US']], $fresh->locations);
    }

    public function test_deleting_a_brief_leaves_its_campaigns_standing(): void
    {
        $brief = $this->brief();
        $campaign = $this->campaign('meta', $brief);

        $brief->delete();

        $this->assertNotNull($campaign->fresh(), 'The campaign was deleted with the brief.');
        $this->assertNull($campaign->fresh()->brief_id);
    }

    /** The page is fetched once and suggested from many times. */
    public function test_the_page_analysis_round_trips(): void
    {
        $brief = $this->brief();

        $brief->rememberPageAnalysis(new LandingPageAnalysis(
            url: 'https://example.com/shoes',
            title: 'Acme Running Shoes',
            description: 'Lightweight trail shoes.',
            headings: ['Acme Running Shoes', 'Built for distance'],
            text: 'Cushioned trail running shoes.',
            keywords: ['running', 'shoes', 'trail'],
        ));

        $restored = $brief->fresh()->pageAnalysis();

        $this->assertNotNull($restored);
        $this->assertSame('Acme Running Shoes', $restored->title);
        $this->assertSame(['running', 'shoes', 'trail'], $restored->keywords);
        $this->assertSame('https://example.com/shoes', $brief->fresh()->landing_url);
    }

    public function test_a_brief_with_no_analysis_yet_returns_nothing(): void
    {
        $this->assertNull($this->brief()->pageAnalysis());
    }
}
