<?php

namespace Tests\Feature\Controllers;

use App\Models\ContentSync;
use Illuminate\Support\Facades\Storage;
use Mtc\MercuryDataModels\Dealership;
use Tests\DatabaseTestCase;
use Tests\UserForTenant;

class SiteExportControllerTest extends DatabaseTestCase
{
    use UserForTenant;

    public function testTablesRequiresValidToken()
    {
        $response = $this->postJson(
            route('tenant.site-export.tables', ['tenant' => tenant('id')], false)
        );

        $response->assertStatus(403);
    }

    public function testTablesListsOnlySelectedModules()
    {
        $response = $this->postJson(
            route('tenant.site-export.tables', ['tenant' => tenant('id')], false),
            ['modules' => ['dealerships']],
            $this->authHeaders()
        );

        $response->assertStatus(200);

        $names = collect($response->json('tables'))->pluck('name');
        $this->assertTrue($names->contains('dealerships'));
        $this->assertTrue($names->contains('franchises'));
        $this->assertFalse($names->contains('pages'));
        $this->assertFalse($names->contains('vehicles'));
    }

    public function testMediaIsItsOwnModule()
    {
        $response = $this->postJson(
            route('tenant.site-export.tables', ['tenant' => tenant('id')], false),
            ['modules' => ['content']],
            $this->authHeaders()
        );

        $response->assertStatus(200);
        $this->assertFalse(collect($response->json('tables'))->pluck('name')->contains('media'));

        $response = $this->postJson(
            route('tenant.site-export.tables', ['tenant' => tenant('id')], false),
            ['modules' => ['media']],
            $this->authHeaders()
        );

        $response->assertStatus(200);

        $names = collect($response->json('tables'))->pluck('name');
        $this->assertTrue($names->contains('media'));
        $this->assertTrue($names->contains('media_uses'));
        $this->assertFalse($names->contains('pages'));
    }

    public function testTablesRejectsUnknownModule()
    {
        $response = $this->postJson(
            route('tenant.site-export.tables', ['tenant' => tenant('id')], false),
            ['modules' => ['not-a-module']],
            $this->authHeaders()
        );

        $response->assertStatus(422);
    }

    public function testPiiTablesAreSkippedWhenRedactedViewIsMissing()
    {
        // The test schema has no *_redacted views (they are MariaDB-only), so
        // PII tables must fail closed and drop out of the export list.
        $response = $this->postJson(
            route('tenant.site-export.tables', ['tenant' => tenant('id')], false),
            ['modules' => ['customers']],
            $this->authHeaders()
        );

        $response->assertStatus(200);

        $names = collect($response->json('tables'))->pluck('name');
        $this->assertTrue($names->contains('enquiry_statuses'));
        $this->assertFalse($names->contains('customers'));
        $this->assertFalse($names->contains('enquiries'));
        $this->assertFalse($names->contains('invoices'));
    }

    public function testDataRejectsTableOutsideAllowlist()
    {
        $response = $this->postJson(
            route('tenant.site-export.data', ['tenant' => tenant('id'), 'table' => 'users'], false),
            [],
            $this->authHeaders()
        );

        $response->assertStatus(403);
    }

    public function testDataRejectsPiiTableWithoutRedactedView()
    {
        $response = $this->postJson(
            route('tenant.site-export.data', ['tenant' => tenant('id'), 'table' => 'customers'], false),
            [],
            $this->authHeaders()
        );

        $response->assertStatus(403);
    }

    public function testDataReturnsRowsForAllowedTable()
    {
        Dealership::factory(3)->create();

        $response = $this->postJson(
            route('tenant.site-export.data', ['tenant' => tenant('id'), 'table' => 'dealerships'], false),
            [],
            $this->authHeaders()
        );

        $response->assertStatus(200);
        $this->assertEquals('dealerships', $response->json('table'));
        $this->assertCount(3, $response->json('data'));
        $this->assertEquals(3, $response->json('pagination.total'));
    }

    public function testFilesRequiresValidToken()
    {
        $response = $this->postJson(
            route('tenant.site-export.files', ['tenant' => tenant('id')], false)
        );

        $response->assertStatus(403);
    }

    public function testFilesListsOnlyThisTenantsMediaWithSizes()
    {
        $disk = Storage::fake(config('filesystems.default_media'));
        $disk->put(tenant('id') . '/media/one.jpg', 'aaa');
        $disk->put(tenant('id') . '/media/two.png', 'bbbbb');
        // Another tenant's files must never leak into this tenant's list.
        $disk->put('other-tenant/media/three.jpg', 'x');

        $response = $this->postJson(
            route('tenant.site-export.files', ['tenant' => tenant('id')], false),
            [],
            $this->authHeaders()
        );

        $response->assertStatus(200);

        $files = collect($response->json('files'))->keyBy('path');
        $this->assertTrue($files->has(tenant('id') . '/media/one.jpg'));
        $this->assertTrue($files->has(tenant('id') . '/media/two.png'));
        $this->assertFalse($files->has('other-tenant/media/three.jpg'));
        $this->assertEquals(3, $files[tenant('id') . '/media/one.jpg']['size']);
        $this->assertEquals(5, $files[tenant('id') . '/media/two.png']['size']);
        // A downloadable URL is offered so the client can pull from storage
        // directly instead of streaming the bytes through this environment.
        $this->assertArrayHasKey('url', $files[tenant('id') . '/media/one.jpg']);
    }

    public function testFileRejectsPathOutsideTenant()
    {
        $response = $this->postJson(
            route('tenant.site-export.file', ['tenant' => tenant('id')], false),
            ['path' => 'other-tenant/media/secret.jpg'],
            $this->authHeaders()
        );

        $response->assertStatus(403);
    }

    public function testFileRejectsTraversalPath()
    {
        $response = $this->postJson(
            route('tenant.site-export.file', ['tenant' => tenant('id')], false),
            ['path' => tenant('id') . '/../other-tenant/secret.jpg'],
            $this->authHeaders()
        );

        $response->assertStatus(403);
    }

    public function testFileReturns404WhenMissing()
    {
        Storage::fake(config('filesystems.default_media'));

        $response = $this->postJson(
            route('tenant.site-export.file', ['tenant' => tenant('id')], false),
            ['path' => tenant('id') . '/media/missing.jpg'],
            $this->authHeaders()
        );

        $response->assertStatus(404);
    }

    public function testFileStreamsAllowedContents()
    {
        $disk = Storage::fake(config('filesystems.default_media'));
        $disk->put(tenant('id') . '/media/logo.svg', 'hello-bytes');

        $response = $this->post(
            route('tenant.site-export.file', ['tenant' => tenant('id')], false),
            ['path' => tenant('id') . '/media/logo.svg'],
            $this->authHeaders()
        );

        $response->assertStatus(200);
        $this->assertEquals('hello-bytes', $response->streamedContent());
    }

    private function authHeaders(): array
    {
        $sync = ContentSync::factory()->create([
            'user_id' => $this->getUser()->id,
        ]);

        return [
            'x-token' => $sync->token,
            'x-user' => $this->getUser()->email,
        ];
    }
}
