<?php

use App\Models\AuditLog;
use App\Models\AdminRole;
use App\Models\AdminUser;
use App\Models\Draw;
use App\Models\ReportJob;
use App\Models\RiskPool;
use App\Lottery\ErrorCode;
use App\Models\ReconcileJob;
use App\Services\AuditLogger;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use App\Support\AdminPermissionBridge;
use Database\Seeders\AdminRbacAndUserSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    $this->seed(AdminRbacAndUserSeeder::class);
    $this->artisan('lottery:admin-auth-sync')->assertExitCode(0);
});

function phase15SuperToken(): string
{
    $admin = AdminUser::query()->create([
        'username' => 'phase15_super',
        'name' => 'Phase15',
        'email' => null,
        'password' => Hash::make('secret-strong'),
        'status' => 0,
    ]);
    grantSuperAdminRole($admin);

    return $admin->createToken('test', ['*'], now()->addDay())->plainTextToken;
}

test('report job create list show and audit log index work for super admin', function (): void {
    AuditLogger::record('system', 0, 'bootstrap', 'test', null, null, null, null);

    $token = phase15SuperToken();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/audit-logs?per_page=5')
        ->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value);

    $create = $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/report-jobs', [
            'report_type' => 'wallet_txns_daily',
            'export_format' => 'csv',
            'filter_json' => ['currency_code' => 'NPR'],
        ]);
    $create->assertOk()->assertJsonPath('code', ErrorCode::Success->value);
    $id = (int) $create->json('data.id');
    expect($id)->toBeGreaterThan(0);
    expect(ReportJob::query()->whereKey($id)->exists())->toBeTrue();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/report-jobs/'.$id)
        ->assertOk()
        ->assertJsonPath('data.report_type', 'wallet_txns_daily');

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/report-jobs?per_page=10')
        ->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value);

    expect(AuditLog::query()->where('module_code', 'report_jobs')->exists())->toBeTrue();
});

test('report jobs support module 13 report types and downloadable csv with bom', function (): void {
    $token = phase15SuperToken();

    $create = $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/report-jobs', [
            'report_type' => 'daily_profit_summary',
            'export_format' => 'csv',
            'parameters' => [
                'date_from' => '2026-05-01',
                'date_to' => '2026-05-07',
            ],
        ]);

    $create->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value)
        ->assertJsonPath('data.export_format', 'csv');

    $id = (int) $create->json('data.id');
    expect($id)->toBeGreaterThan(0);

    $row = ReportJob::query()->whereKey($id)->firstOrFail();
    expect($row->output_path)->toContain('每日盈亏汇总_2026-05-01_2026-05-07')
        ->and($row->output_path)->toEndWith('.csv');

    $download = $this->withHeader('Authorization', 'Bearer '.$token)
        ->get('/api/v1/admin/report-jobs/'.$id.'/download');

    $download->assertOk()
        ->assertHeader('content-type', 'text/csv; charset=UTF-8');

    $content = $download->streamedContent();
    expect(substr($content, 0, 3))->toBe("\xEF\xBB\xBF");
});

test('report jobs support xlsx export filename convention', function (): void {
    $token = phase15SuperToken();

    $create = $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/report-jobs', [
            'report_type' => 'audit_operation_report',
            'export_format' => 'xlsx',
            'parameters' => [
                'date_from' => '2026-05-01',
                'date_to' => '2026-05-31',
            ],
        ]);

    $create->assertOk()
        ->assertJsonPath('code', ErrorCode::Success->value)
        ->assertJsonPath('data.export_format', 'xlsx');

    $id = (int) $create->json('data.id');
    $row = ReportJob::query()->whereKey($id)->firstOrFail();
    expect($row->output_path)->toContain('后台操作审计报表_2026-05-01_2026-05-31')
        ->and($row->output_path)->toEndWith('.xlsx');

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->get('/api/v1/admin/report-jobs/'.$id.'/download')
        ->assertOk()
        ->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
});

test('report jobs export draw profit and sold out reports by draw_no', function (): void {
    $token = phase15SuperToken();

    $draw = Draw::query()->create([
        'draw_no' => 'RPT-DRAW-001',
        'business_date' => '2026-05-20',
        'sequence_no' => 1,
        'status' => 'settled',
        'start_time' => now()->subDay(),
        'close_time' => now()->subDay(),
        'draw_time' => now()->subDay(),
        'cooling_end_time' => null,
        'result_source' => null,
        'current_result_version' => 1,
        'settle_version' => 1,
        'is_reopened' => false,
    ]);

    RiskPool::query()->create([
        'draw_id' => $draw->id,
        'normalized_number' => '1234',
        'total_cap_amount' => 10000,
        'locked_amount' => 10000,
        'remaining_amount' => 0,
        'sold_out_status' => 1,
        'version' => 1,
    ]);

    $profit = $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/report-jobs', [
            'report_type' => 'draw_profit_summary',
            'export_format' => 'csv',
            'parameters' => ['draw_no' => 'RPT-DRAW-001'],
        ]);
    $profit->assertOk()->assertJsonPath('code', ErrorCode::Success->value);
    $profitId = (int) $profit->json('data.id');
    expect(ReportJob::query()->find($profitId)?->output_path)->toContain('RPT-DRAW-001');

    $profitCsv = $this->withHeader('Authorization', 'Bearer '.$token)
        ->get('/api/v1/admin/report-jobs/'.$profitId.'/download')
        ->assertOk()
        ->streamedContent();
    expect($profitCsv)->toContain('summary')
        ->and($profitCsv)->toContain('RPT-DRAW-001');

    $soldOut = $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/report-jobs', [
            'report_type' => 'sold_out_number_report',
            'export_format' => 'csv',
            'parameters' => ['draw_id' => $draw->id],
        ]);
    $soldOut->assertOk();
    $soldOutId = (int) $soldOut->json('data.id');
    $soldOutCsv = $this->withHeader('Authorization', 'Bearer '.$token)
        ->get('/api/v1/admin/report-jobs/'.$soldOutId.'/download')
        ->assertOk()
        ->streamedContent();
    expect($soldOutCsv)->toContain('1234')
        ->and($soldOutCsv)->toContain('是');
});

test('reconcile job create with items and nested items index', function (): void {
    $token = phase15SuperToken();

    $resp = $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/reconcile-jobs', [
            'reconcile_type' => 'wallet_transfer',
            'period_start' => '2026-05-01T00:00:00Z',
            'period_end' => '2026-05-02T00:00:00Z',
            'items' => [
                ['side_a_ref' => 'TO-1', 'side_b_ref' => 'MAIN-1', 'difference_amount' => 100, 'status' => 'mismatch'],
                ['side_a_ref' => 'TO-2', 'side_b_ref' => 'MAIN-2', 'difference_amount' => 0, 'status' => 'matched'],
            ],
        ]);
    $resp->assertOk();
    $id = (int) $resp->json('data.id');
    expect($id)->toBeGreaterThan(0);

    $job = ReconcileJob::query()->whereKey($id)->firstOrFail();
    expect((int) $job->admin_user_id)->toBeGreaterThan(0);
    expect($job->items()->count())->toBe(2);

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/reconcile-jobs/'.$id.'/items')
        ->assertOk()
        ->assertJsonPath('data.meta.total', 2);
});

test('admin without report permission receives 403 on report-jobs', function (): void {
    $role = AdminRole::query()->create(['slug' => 'auditor_test', 'name' => 'Auditor Test']);
    $ids = DB::table('admin_menu_actions')
        ->whereIn('permission_code', AdminPermissionBridge::menuActionCodesForLegacy('prd.audit.view'))
        ->where('status', 1)
        ->pluck('id');
    foreach ($ids as $mid) {
        DB::table('admin_role_menu_actions')->insert([
            'role_id' => $role->id,
            'menu_action_id' => (int) $mid,
        ]);
    }

    $user = AdminUser::query()->create([
        'username' => 'auditor_only',
        'name' => 'Auditor',
        'email' => null,
        'password' => Hash::make('pw-audit'),
        'status' => 0,
    ]);
    $siteId = AdminUser::defaultAdminSiteId();
    $user->roles()->sync([
        (int) $role->id => [
            'site_id' => $siteId,
            'granted_at' => now(),
        ],
    ]);

    $token = $user->createToken('test', ['*'], now()->addDay())->plainTextToken;

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->getJson('/api/v1/admin/audit-logs')
        ->assertOk();

    $this->withHeader('Authorization', 'Bearer '.$token)
        ->postJson('/api/v1/admin/report-jobs', ['report_type' => 'x'])
        ->assertStatus(403)
        ->assertJsonPath('code', ErrorCode::AdminForbidden->value);
});
