<?php

declare(strict_types=1);

use App\Models\BalanceTransaction;
use App\Models\Deal;
use App\Models\LeadCharge;
use App\Models\Tenant;
use App\Models\User;
use Database\Seeders\PricingTierSeeder;
use Illuminate\Foundation\Testing\DatabaseTransactions;

/**
 * Tenant-scoped read-only charges ledger (Plan 4 Task 11).
 *
 * GET /api/billing/charges — paginated 20/page (sorted by charged_at desc).
 * POST /api/billing/charges/export — StreamedResponse CSV (BOM + chunkById 500).
 * Filters: period (current_month / last_month / 90d) + charge_source (prepaid / rub).
 *
 * RLS изоляция через SetTenantContext middleware (auth:sanctum + tenant). Тесты
 * используют postgres superuser BYPASSRLS, поэтому RLS-isolation проверяется
 * через `auth()->user()->tenant_id` фильтр — другие tenant'ы видны только если
 * мы сами их INSERT'нули, но фильтрация теста на $this->tenant->id всё равно
 * остаётся защитой.
 *
 * Spec: docs/superpowers/specs/2026-05-11-plan4-billing-csv-admin-design.md §6.3
 */
uses(DatabaseTransactions::class);

beforeEach(function () {
    // PricingTierSeeder идемпотентен (updateOrCreate); seed безопасно.
    $this->seed(PricingTierSeeder::class);

    $this->tenant = Tenant::factory()->create();
    $this->user = User::factory()->create(['tenant_id' => $this->tenant->id]);
    $this->actingAs($this->user);
});

function makeChargeFor(Tenant $tenant, array $overrides = []): LeadCharge
{
    $deal = Deal::factory()->create([
        'tenant_id' => $tenant->id,
        'received_at' => now(),
    ]);

    return LeadCharge::factory()->create(array_merge([
        'tenant_id' => $tenant->id,
        'deal_id' => $deal->id,
        'deal_received_at' => $deal->received_at,
        'charged_at' => now(),
    ], $overrides));
}

it('GET /api/billing/charges returns paginated list for current tenant only (RLS)', function () {
    makeChargeFor($this->tenant);
    makeChargeFor($this->tenant);

    $otherTenant = Tenant::factory()->create();
    makeChargeFor($otherTenant);

    $response = $this->getJson('/api/billing/charges');
    $response->assertOk();
    expect($response->json('data'))->toHaveCount(2);
});

it('filters by charge_source=prepaid', function () {
    makeChargeFor($this->tenant, ['charge_source' => 'rub', 'price_per_lead_kopecks' => 50000]);
    makeChargeFor($this->tenant, ['charge_source' => 'prepaid', 'price_per_lead_kopecks' => 0]);
    makeChargeFor($this->tenant, ['charge_source' => 'prepaid', 'price_per_lead_kopecks' => 0]);

    $response = $this->getJson('/api/billing/charges?charge_source=prepaid');
    expect($response->json('data'))->toHaveCount(2);
});

it('filters by period=current_month / last_month / 90d', function () {
    makeChargeFor($this->tenant, ['charged_at' => now()]);
    makeChargeFor($this->tenant, ['charged_at' => now()->subMonth()]);
    makeChargeFor($this->tenant, ['charged_at' => now()->subDays(60)]);
    makeChargeFor($this->tenant, ['charged_at' => now()->subDays(120)]);

    $this->getJson('/api/billing/charges?period=current_month')->assertJsonCount(1, 'data');
    $this->getJson('/api/billing/charges?period=last_month')->assertJsonCount(1, 'data');
    $this->getJson('/api/billing/charges?period=90d')->assertJsonCount(3, 'data');
});

it('returns 401 без auth', function () {
    auth()->logout();
    $this->getJson('/api/billing/charges')->assertStatus(401);
});

it('pagination: ?page=2 returns next slice', function () {
    for ($i = 0; $i < 30; $i++) {
        makeChargeFor($this->tenant);
    }

    $page1 = $this->getJson('/api/billing/charges?page=1');
    $page2 = $this->getJson('/api/billing/charges?page=2');

    expect($page1->json('data'))->toHaveCount(20);
    expect($page2->json('data'))->toHaveCount(10);
});

it('POST /export streams CSV via StreamedResponse', function () {
    makeChargeFor($this->tenant);

    $response = $this->postJson('/api/billing/charges/export', ['period' => '90d']);
    $response->assertOk();
    $response->assertHeader('Content-Type', 'text/csv; charset=UTF-8');

    $body = $response->streamedContent();
    expect($body)->toContain('charged_at,deal_id,tier_no,charge_source,price_rub,balance_rub_after');
});

it('export заполняет balance_rub_after из balance_transactions JOIN', function () {
    $deal = Deal::factory()->create([
        'tenant_id' => $this->tenant->id,
        'received_at' => now(),
    ]);

    LeadCharge::factory()->create([
        'tenant_id' => $this->tenant->id,
        'deal_id' => $deal->id,
        'deal_received_at' => $deal->received_at,
        'tier_no' => 1,
        'price_per_lead_kopecks' => 50000,
        'charge_source' => 'rub',
        'charged_at' => now(),
    ]);

    BalanceTransaction::create([
        'tenant_id' => $this->tenant->id,
        'type' => BalanceTransaction::TYPE_LEAD_CHARGE,
        'amount_rub' => '-500.00',
        'amount_leads' => null,
        'balance_rub_after' => '4500.00',
        'related_type' => Deal::class,
        'related_id' => $deal->id,
        'created_at' => now(),
    ]);

    $response = $this->postJson('/api/billing/charges/export');
    $response->assertOk();

    $csv = $response->streamedContent();
    expect($csv)->toContain('4500.00');
});

test('TenantChargesController::export emits charged_at in ISO-8601 format (regression A.10 fix)', function () {
    $deal = Deal::factory()->create([
        'tenant_id' => $this->tenant->id,
        'received_at' => now(),
    ]);
    LeadCharge::create([
        'tenant_id' => $this->tenant->id,
        'deal_id' => $deal->id,
        'deal_received_at' => $deal->received_at,
        'tier_no' => 1,
        'price_per_lead_kopecks' => 50000,
        'charge_source' => 'rub',
        'charged_at' => now(),
    ]);

    $body = $this->post('/api/billing/charges/export')->streamedContent();

    // ISO-8601 marker: "T" between date and time, and trailing "+" or "Z" timezone.
    expect($body)->toMatch('/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}([+-]\d{2}:\d{2}|Z)/');
});
