<?php

namespace Tests\Feature;

use App\Services\Meta\MetaException;
use App\Services\Platforms\Support\Guardrails;
use Tests\TestCase;

/**
 * One platform's allowed account does not authorise another platform's.
 *
 * PLATFORM_ALLOWED_AD_ACCOUNTS is one flat list holding every platform's
 * accounts, and account ids are not namespaced: Meta's 556447014 and Google's
 * 556447014 are different accounts owned by different people.
 *
 * The check briefly accepted a match on any of three spellings, one of which
 * stripped the act_ prefix, which meant a bare id on the list authorised the
 * Meta account with the same digits. setup.sh puts a bare id on that list, so
 * dev ran in that state. This is the only control standing between the product
 * and spending on an account that is not ours, so each direction is asserted
 * rather than assumed.
 */
class AllowedAccountsAreNotSharedAcrossPlatformsTest extends TestCase
{
    private function allowing(array $accounts): Guardrails
    {
        config(['platforms.guardrails.allowed_ad_accounts' => $accounts]);

        return app(Guardrails::class);
    }

    private function permits(Guardrails $guardrails, string $account, string $platform): bool
    {
        try {
            $guardrails->assertAccountAllowed($account, $platform);

            return true;
        } catch (MetaException) {
            return false;
        }
    }

    /** The regression: a bare id must not let the Meta account through. */
    public function test_a_bare_id_does_not_authorise_the_meta_account_with_the_same_digits(): void
    {
        $guardrails = $this->allowing(['act_1111111111', '3460855874']);

        $this->assertFalse(
            $this->permits($guardrails, 'act_3460855874', 'meta'),
            'A Google customer id on the list authorised a Meta ad account.',
        );

        $this->assertFalse(
            $this->permits($guardrails, '3460855874', 'meta'),
            'The same account, given without the prefix, was authorised.',
        );
    }

    /** Nor the other way: Meta's entry is not a Google account. */
    public function test_a_meta_entry_does_not_authorise_the_google_account_with_the_same_digits(): void
    {
        $guardrails = $this->allowing(['act_1111111111']);

        $this->assertFalse($this->permits($guardrails, '1111111111', 'google'));
    }

    public function test_meta_accepts_its_own_account_with_or_without_the_prefix(): void
    {
        $guardrails = $this->allowing(['act_1111111111', '3460855874']);

        $this->assertTrue($this->permits($guardrails, 'act_1111111111', 'meta'));
        $this->assertTrue($this->permits($guardrails, '1111111111', 'meta'));
    }

    public function test_google_accepts_its_own_customer_id_in_either_spelling(): void
    {
        $guardrails = $this->allowing(['act_1111111111', '3460855874']);

        $this->assertTrue($this->permits($guardrails, '3460855874', 'google'));
        $this->assertTrue($this->permits($guardrails, '346-085-5874', 'google'), 'Hyphens are display, not the id.');
    }

    /**
     * LinkedIn is configured as a URN, as a bare id, or both, and an id
     * reaches the guard in either spelling. All four pairings must agree.
     */
    public function test_linkedin_matches_across_the_urn_and_the_bare_id(): void
    {
        foreach ([['urn:li:sponsoredAccount:556447014'], ['556447014']] as $list) {
            $guardrails = $this->allowing($list);

            $this->assertTrue(
                $this->permits($guardrails, '556447014', 'linkedin'),
                'Bare id refused against '.$list[0],
            );
            $this->assertTrue(
                $this->permits($guardrails, 'urn:li:sponsoredAccount:556447014', 'linkedin'),
                'URN refused against '.$list[0],
            );
        }
    }

    /** A LinkedIn URN on the list is not a Meta account. */
    public function test_a_linkedin_urn_does_not_authorise_meta(): void
    {
        $guardrails = $this->allowing(['urn:li:sponsoredAccount:556447014']);

        $this->assertFalse($this->permits($guardrails, 'act_556447014', 'meta'));
    }

    /** An account on nobody's list is still refused, on every platform. */
    public function test_an_unlisted_account_is_refused_everywhere(): void
    {
        $guardrails = $this->allowing(['act_1111111111', '3460855874']);

        foreach (['meta', 'google', 'linkedin'] as $platform) {
            $this->assertFalse($this->permits($guardrails, 'act_9999999999', $platform));
            $this->assertFalse($this->permits($guardrails, '9999999999', $platform));
        }
    }

    /** An empty list allows nothing rather than everything. */
    public function test_an_empty_list_refuses_every_account(): void
    {
        $this->assertFalse($this->permits($this->allowing([]), 'act_1111111111', 'meta'));
    }

    /** Explicit platform prefixes (google:..., linkedin:..., meta:...) disambiguate bare numbers. */
    public function test_explicit_platform_prefixes_disambiguate_accounts(): void
    {
        $guardrails = $this->allowing(['google:3460855874', 'linkedin:556447014', 'meta:11223344']);

        // google:3460855874 only permits Google, never LinkedIn or Meta
        $this->assertTrue($this->permits($guardrails, '3460855874', 'google'));
        $this->assertFalse($this->permits($guardrails, '3460855874', 'linkedin'));
        $this->assertFalse($this->permits($guardrails, 'act_3460855874', 'meta'));

        // linkedin:556447014 only permits LinkedIn, never Google or Meta
        $this->assertTrue($this->permits($guardrails, '556447014', 'linkedin'));
        $this->assertFalse($this->permits($guardrails, '556447014', 'google'));
        $this->assertFalse($this->permits($guardrails, 'act_556447014', 'meta'));

        // meta:11223344 permits Meta, never Google or LinkedIn
        $this->assertTrue($this->permits($guardrails, 'act_11223344', 'meta'));
        $this->assertFalse($this->permits($guardrails, '11223344', 'google'));
        $this->assertFalse($this->permits($guardrails, '11223344', 'linkedin'));
    }
}
