<?php

declare(strict_types=1);

namespace App\Tests\Integration\Admin;

use App\Domain\Auth\Role;
use App\Domain\Auth\TokenKind;
use App\Tests\Integration\Support\AppTestCase;

final class ConsumersControllerTest extends AppTestCase
{
    public function testCreateAndListConsumer(): void
    {
        $token = $this->createToken(TokenKind::Admin, role: Role::Admin);
        $policyId = (int) $this->db->fetchOne('SELECT id FROM policies WHERE name = :name', ['name' => 'moderate']);

        $created = $this->request(
            'POST',
            '/api/v1/admin/consumers',
            ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
            json_encode(['name' => 'fw-edge-01', 'policy_id' => $policyId]) ?: null,
        );
        self::assertSame(201, $created->getStatusCode());
        $body = $this->decode($created);
        self::assertSame('fw-edge-01', $body['name']);
        self::assertSame($policyId, $body['policy_id']);

        $list = $this->request('GET', '/api/v1/admin/consumers', [
            'Authorization' => 'Bearer ' . $token,
        ]);
        self::assertSame(200, $list->getStatusCode());
        $listBody = $this->decode($list);
        self::assertGreaterThan(0, $listBody['total']);
    }

    public function testCreateRejectsUnknownPolicy(): void
    {
        $token = $this->createToken(TokenKind::Admin, role: Role::Admin);

        $resp = $this->request(
            'POST',
            '/api/v1/admin/consumers',
            ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
            json_encode(['name' => 'bogus', 'policy_id' => 99999]) ?: null,
        );
        self::assertSame(400, $resp->getStatusCode());
        self::assertArrayHasKey('policy_id', $this->decode($resp)['details']);
    }

    public function testPatchTogglesAuditEnabled(): void
    {
        $token = $this->createToken(TokenKind::Admin, role: Role::Admin);
        $policyId = (int) $this->db->fetchOne('SELECT id FROM policies WHERE name = :name', ['name' => 'moderate']);

        $created = $this->request(
            'POST',
            '/api/v1/admin/consumers',
            ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
            json_encode(['name' => 'fw-audit-toggle', 'policy_id' => $policyId]) ?: null,
        );
        self::assertSame(201, $created->getStatusCode());
        $body = $this->decode($created);
        self::assertTrue($body['audit_enabled']);
        $consumerId = (int) $body['id'];

        $patch = $this->request(
            'PATCH',
            "/api/v1/admin/consumers/{$consumerId}",
            ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
            json_encode(['audit_enabled' => false]) ?: null,
        );
        self::assertSame(200, $patch->getStatusCode());
        self::assertFalse($this->decode($patch)['audit_enabled']);
    }

    public function testAuditEnabledToggleEmitsDedicatedAuditRow(): void
    {
        // SEC_REVIEW F41: an admin flipping `audit_enabled` for a consumer
        // must leave a flat alertable trail SOC tooling can match on with
        // `action = 'consumer.audit_toggled'` — without walking into the
        // metadata `changes` blob of the standard `consumer.updated` row.
        $token = $this->createToken(TokenKind::Admin, role: Role::Admin);
        $policyId = (int) $this->db->fetchOne(
            'SELECT id FROM policies WHERE name = :name',
            ['name' => 'moderate'],
        );

        $created = $this->request(
            'POST',
            '/api/v1/admin/consumers',
            ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
            json_encode(['name' => 'fw-audit-signal', 'policy_id' => $policyId]) ?: null,
        );
        $consumerId = (int) $this->decode($created)['id'];

        $this->request(
            'PATCH',
            "/api/v1/admin/consumers/{$consumerId}",
            ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
            json_encode(['audit_enabled' => false]) ?: null,
        );

        $rows = $this->db->fetchAllAssociative(
            "SELECT action, details_json FROM audit_log WHERE target_type = 'consumer' AND target_id = ? ORDER BY id",
            [(string) $consumerId],
        );
        $actions = array_column($rows, 'action');
        self::assertContains('consumer.updated', $actions);
        self::assertContains('consumer.audit_toggled', $actions);

        $toggleRow = null;
        foreach ($rows as $row) {
            if ($row['action'] === 'consumer.audit_toggled') {
                $toggleRow = $row;
                break;
            }
        }
        self::assertNotNull($toggleRow);
        $meta = json_decode((string) $toggleRow['details_json'], true);
        self::assertSame(true, $meta['from'] ?? null);
        self::assertSame(false, $meta['to'] ?? null);
    }

    public function testAuditEnabledNoOpDoesNotEmitDedicatedRow(): void
    {
        $token = $this->createToken(TokenKind::Admin, role: Role::Admin);
        $policyId = (int) $this->db->fetchOne(
            'SELECT id FROM policies WHERE name = :name',
            ['name' => 'moderate'],
        );

        $created = $this->request(
            'POST',
            '/api/v1/admin/consumers',
            ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
            json_encode(['name' => 'fw-noop', 'policy_id' => $policyId]) ?: null,
        );
        $consumerId = (int) $this->decode($created)['id'];

        // PATCH `audit_enabled` to its current value (no-op) — must NOT
        // fire the toggle signal.
        $this->request(
            'PATCH',
            "/api/v1/admin/consumers/{$consumerId}",
            ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
            json_encode(['audit_enabled' => true]) ?: null,
        );

        $actions = $this->db->fetchFirstColumn(
            "SELECT action FROM audit_log WHERE target_type = 'consumer' AND target_id = ?",
            [(string) $consumerId],
        );
        self::assertNotContains('consumer.audit_toggled', $actions);
    }
}
