<?php

namespace Tests\Feature;

use App\Agent\Choices;
use App\Agent\ToolRegistry;
use App\Agent\Workspace;
use App\Campaigns\LaunchFlow;
use App\Models\ArtifactFieldSource;
use App\Models\Campaign;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Ai\Tools\Request;
use Tests\TestCase;

/**
 * Google does not ask which account, because there is only one.
 *
 * Found driving the Google flow end to end on 23 Sep. Its first step is
 * ad_account, wired to list_ad_accounts, and that tool serves Meta: for
 * anything else it returned "Google Ads is not connected yet, so it has no ad
 * accounts to list". Google was connected and answering at the time.
 *
 * With nothing on screen the model fell back to prose and asked "Which Google
 * Ads account should this campaign run on?", which the buyer had no way to
 * answer: childAccountOptions() reads available_customer_ids, which the OAuth
 * flow fills in, and there is no OAuth flow any more. The run stopped at step
 * one of nine.
 *
 * Google was rebuilt to use one common internal account from configuration,
 * like the Meta system user token, so the id is not a question. The publisher
 * never wanted it from the campaign anyway: it reads the customer id off the
 * connection. It is written to the campaign because the publish gate blocks on
 * a blank ad_account_id, and because a panel row naming the account beats an
 * empty one.
 */
class GoogleUsesTheSharedAccountTest extends TestCase
{
    use RefreshDatabase;

    private User $user;

    private Campaign $campaign;

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

        config([
            'services.google_ads.refresh_token' => 'a-refresh-token',
            'services.google_ads.customer_id' => '346-085-5874',
            'services.google_ads.login_customer_id' => '8039000543',
            // The account has to be permitted as well as configured. Being
            // configured alone was enough to get it written to the campaign,
            // and the refusal did not arrive until publish; see
            // GoogleAccountIsCheckedBeforeItIsAdoptedTest.
            'platforms.guardrails.allowed_ad_accounts' => ['3460855874'],
        ]);

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

        $this->campaign = Campaign::create([
            'user_id' => $this->user->id,
            'name' => 'Google campaign',
            'platform' => 'google',
            'status' => 'draft',
        ]);

        app(Workspace::class)->ownedBy($this->user);
        app(Workspace::class)->produced($this->campaign);
    }

    /** @return array<string,mixed> */
    private function listAccounts(): array
    {
        foreach (app(ToolRegistry::class)->resolve() as $tool) {
            if ($tool->name() === 'campaign__list_ad_accounts') {
                return json_decode((string) $tool->handle(new Request([])), true);
            }
        }

        $this->fail('list_ad_accounts is not registered.');
    }

    public function test_the_configured_account_is_applied_rather_than_refused(): void
    {
        $result = $this->listAccounts();

        $this->assertTrue($result['ok'], 'Google was told it is not connected: '.($result['error'] ?? ''));
        $this->assertSame('3460855874', $this->campaign->fresh()->ad_account_id);
    }

    /** Hyphens are how a human writes it and not how the API takes it. */
    public function test_the_customer_id_is_stored_without_hyphens(): void
    {
        $this->listAccounts();

        $this->assertStringNotContainsString('-', (string) $this->campaign->fresh()->ad_account_id);
    }

    /** Nothing is put on screen, because there is nothing to choose between. */
    public function test_no_account_question_is_asked(): void
    {
        $this->listAccounts();

        $fields = array_column(app(Choices::class)->all(), 'field');

        $this->assertNotContains('ad_account_id', $fields, 'Google was asked to pick from one account.');
    }

    /** The step counts as answered, or the flow asks for it again forever. */
    public function test_the_step_is_satisfied(): void
    {
        $this->listAccounts();

        $this->assertNotSame(
            'ad_account',
            app(LaunchFlow::class)->next($this->campaign->fresh())['key'] ?? null,
            'The account is set and the flow still asks for it, which is the loop this fixes.',
        );
    }

    /** It came from configuration, not from the buyer and not from a guess. */
    public function test_it_is_recorded_as_the_platforms_own_answer(): void
    {
        $this->listAccounts();

        $this->assertSame(
            ArtifactFieldSource::PLATFORM_RETURNED,
            app(Workspace::class)->sourcesFor($this->campaign->fresh())['ad_account_id'],
        );
    }

    /**
     * Unconfigured says so plainly, and does not ask the buyer to fix it.
     *
     * The old message told them Google was not connected, which sounds like
     * something they could go and connect. Nobody can: it is one internal
     * account set by an administrator.
     */
    public function test_an_unconfigured_google_says_what_is_actually_missing(): void
    {
        config(['services.google_ads.customer_id' => null]);

        $result = $this->listAccounts();

        $this->assertFalse($result['ok']);
        $this->assertStringContainsString('GOOGLE_ADS_CUSTOMER_ID', $result['error']);
        $this->assertStringContainsString('administrator', $result['note']);
    }
}
