<?php

namespace Tests\Feature;

use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\FeatureTestTrait;

/**
 * Access boundary for Notes and Alerts Manager (ADC-180, AP-130).
 *
 * A native advertiser session is `logged_in` + `adv_id` with no `mngtuser` and
 * `is_superuser` false — what Users::authAdvertiser() and Sso::callback() build.
 * A MNGT session is the same plus `mngtuser`.
 *
 * If testNativeAdvertiser* starts failing, the `adops` gate has regressed and
 * the leak is an authentication defect, not a scoping one.
 */
final class NotesAlertsAccessBoundaryTest extends CIUnitTestCase
{
    use FeatureTestTrait;

    /** Owns campaign 93772. */
    private const ADV_A = 13421;

    /** Owns campaign 93749 (12 notes) — the foreign campaign for ADV_A. */
    private const FOREIGN_CAMPAIGN = 93749;

    /** @var string[] */
    private const ADOPS_ROUTES = ['alerts-manager', 'notes'];

    private int $obLevel = 0;

    protected function setUp(): void
    {
        parent::setUp();
        $this->obLevel = ob_get_level();
    }

    protected function tearDown(): void
    {
        while (ob_get_level() > $this->obLevel) {
            ob_end_clean();
        }
        parent::tearDown();
    }

    private function nativeSession(int $advId, string $role = 'admin'): array
    {
        return [
            'logged_in'    => 1,
            'adv_id'       => $advId,
            'user_id'      => $advId,
            'role'         => $role,
            'is_superuser' => false,
        ];
    }

    private function mngtSession(int $advId): array
    {
        return [
            'logged_in'    => 1,
            'adv_id'       => $advId,
            'user_id'      => 9999,
            'role'         => 'admin',
            'is_superuser' => false,
            'mngtuser'     => 'qa.harness',
        ];
    }

    /* ── The mngt-only surfaces ───────────────────────────────── */

    public function testNativeAdvertiserIsDeniedAdopsSurfaces(): void
    {
        foreach (self::ADOPS_ROUTES as $uri) {
            $res = $this->withSession($this->nativeSession(self::ADV_A))->get($uri);

            $this->assertTrue(
                $res->isRedirect(),
                "native advertiser must not reach /{$uri}; AdOpsFilter should redirect"
            );
        }
    }

    public function testMngtUserReachesAdopsSurfaces(): void
    {
        foreach (self::ADOPS_ROUTES as $uri) {
            $res = $this->withSession($this->mngtSession(self::ADV_A))->get($uri);

            $this->assertFalse(
                $res->isRedirect(),
                "mngt user must still reach /{$uri} — this is the surface AP-130 reported on"
            );
        }
    }

    /* ── The campaign-notes JSON feed ─────────────────────────── */

    public function testNativeAdvertiserIsDeniedCampaignNotesApi(): void
    {
        $res = $this->withSession($this->nativeSession(self::ADV_A))
            ->get('api/notes/campaign/' . self::FOREIGN_CAMPAIGN);

        $body = (string) $res->response()->getBody();

        $this->assertStringNotContainsString(
            '"comment"',
            $body,
            'a native advertiser must not receive notes for a campaign owned by another advertiser'
        );
    }

    public function testMngtUserIsDeniedForeignCampaignNotes(): void
    {
        $res = $this->withSession($this->mngtSession(self::ADV_A))
            ->get('api/notes/campaign/' . self::FOREIGN_CAMPAIGN);

        $body = (string) $res->response()->getBody();

        $this->assertStringNotContainsString(
            '"comment"',
            $body,
            'a mngt user in advertiser A context must not receive notes for advertiser B campaigns'
        );
    }
}
