<?php

namespace Tests\Feature\Controllers;

use App\Notifications\PasswordResetNotification;
use Database\Seeders\Global\GlobalRoleSeed;
use Illuminate\Support\Facades\Notification;
use Mtc\MercuryDataModels\TwoFactorRememberedDevice;
use Mtc\MercuryDataModels\User;
use Tests\DatabaseTestCase;
use Tests\UserForTenant;

class AllUserControllerTest extends DatabaseTestCase
{
    use UserForTenant;

    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testIndex()
    {
        User::factory(5)->create();

        $response = $this->actingAs($this->getUser())
            ->getJson(route('all-users.index', [], false));

        $response->assertStatus(200);
        $this->assertIsArray($response->json('data'));
        $this->assertCount(6, $response->json('data')); // +1 from the initial seed (acting user)
        $this->assertArrayHasKey('email', $response->json('data.0'));
        $this->assertArrayHasKey('name', $response->json('data.0'));
    }

    public function testShow()
    {
        $user = User::factory()->create();

        $response = $this->actingAs($this->getUser())
            ->putJson(route('user-management.reset-two-factor', $user, false));

        $response->assertStatus(200);
        $this->assertEquals($user->id, $response->json('id'));
        $this->assertArrayHasKey('name', $response->json());
        $this->assertArrayHasKey('email', $response->json());
        $this->assertArrayHasKey('requires_password_change', $response->json());
        $this->assertArrayHasKey('two_factor_enabled', $response->json());
    }

    public function testDestroy()
    {
        $user = User::factory()->create();

        $response = $this->actingAs($this->getUser())
            ->deleteJson(route('all-users.destroy', $user, false));

        $response->assertStatus(200);
        $this->assertNull(User::query()->find($user->id));
    }

    public function testReset2FA()
    {
        $user = User::factory()->create();

        TwoFactorRememberedDevice::create([
            'user_id' => $user->id,
            'token' => hash('sha256', 'test-token'),
            'user_agent' => 'Test Browser',
            'ip_address' => '127.0.0.1',
            'expires_at' => now()->addDays(90),
        ]);

        $response = $this->actingAs($this->getUser())
            ->putJson(route('user-management.reset-two-factor', $user, false));

        $response->assertStatus(200);
        $this->assertEquals($user->id, $response->json('id'));
        $this->assertFalse($response->json('two_factor_enabled'));
        $this->assertEquals(0, TwoFactorRememberedDevice::where('user_id', $user->id)->count());
    }

    public function testRequirePasswordChange()
    {
        $user = User::factory()->create();

        $response = $this->actingAs($this->getUser())
            ->putJson(route('user-management.require-password-change', $user, false));

        $response->assertStatus(200);
        $this->assertEquals($user->id, $response->json('id'));
        $this->assertTrue($response->json('requires_password_change'));
    }

    public function testResetPassword()
    {
        Notification::fake();
        $user = User::factory()->create();

        $response = $this->actingAs($this->getUser())
            ->putJson(route('user-management.reset-password', $user, false));

        $response->assertStatus(200);
        $this->assertEquals($user->id, $response->json('id'));
        $this->assertTrue($response->json('requires_password_change'));
        Notification::assertSentTo($user, PasswordResetNotification::class);
    }

    public function testToggleStakeholder()
    {
        $this->getUser()->givePermissionTo('system-owner');
        $this->getUser()->load('permissions');
        $user = User::factory()->create(['is_stakeholder' => false]);

        $response = $this->actingAs($this->getUser())
            ->putJson(route('user-management.toggle-stakeholder', $user, false), ['enabled' => true]);

        $response->assertStatus(200);
        $this->assertTrue($response->json('is_stakeholder'));
        $this->assertTrue($user->fresh()->is_stakeholder);

        $response = $this->actingAs($this->getUser())
            ->putJson(route('user-management.toggle-stakeholder', $user, false), ['enabled' => false]);

        $response->assertStatus(200);
        $this->assertFalse($response->json('is_stakeholder'));
        $this->assertFalse($user->fresh()->is_stakeholder);
    }

    public function testToggleStakeholderForbiddenWithoutSystemOwner()
    {
        $user = User::factory()->create(['is_stakeholder' => false]);

        $response = $this->actingAs($this->getUser())
            ->putJson(route('user-management.toggle-stakeholder', $user, false), ['enabled' => true]);

        $response->assertStatus(403);
        $this->assertFalse($user->fresh()->is_stakeholder);
    }

    public function testExportMailingList()
    {
        $this->getUser()->givePermissionTo('system-owner');
        $this->getUser()->load('permissions');
        User::factory()->create(['is_stakeholder' => true, 'email' => 'contact@client.test']);
        User::factory()->create(['is_stakeholder' => false, 'email' => 'regular@client.test']);

        $response = $this->actingAs($this->getUser())
            ->get(route('user-management.export-mailing-list', [], false));

        $response->assertStatus(200);
        $this->assertStringContainsString('text/csv', $response->headers->get('content-type'));

        $content = $response->streamedContent();
        $this->assertStringContainsString('Name,Email', $content);
        $this->assertStringContainsString('contact@client.test', $content);
        $this->assertStringNotContainsString('regular@client.test', $content);
    }

    public function testExportMailingListForbiddenWithoutSystemOwner()
    {
        $response = $this->actingAs($this->getUser())
            ->get(route('user-management.export-mailing-list', [], false));

        $response->assertStatus(403);
    }

    public function testStoreCreatesMtcUser()
    {
        Notification::fake();

        $actingUser = $this->getUser();
        $actingUser->givePermissionTo('system-owner');
        $actingUser->load('permissions');

        $response = $this->actingAs($actingUser)
            ->postJson(route('all-users.store', [], false), [
                'name' => 'New Admin',
                'email' => 'new-admin@example.com',
            ]);

        $response->assertStatus(201);
        $this->assertEquals('new-admin@example.com', $response->json('email'));
        $this->assertTrue($response->json('has_mtc_role'));
        $this->assertTrue($response->json('requires_password_change'));

        $created = User::query()->where('email', 'new-admin@example.com')->first();
        $this->assertNotNull($created);
        $this->assertTrue($created->hasRole('mtc'));
        Notification::assertSentTo($created, PasswordResetNotification::class);
    }

    public function testStoreValidatesEmailAndName()
    {
        $actingUser = $this->getUser();
        $actingUser->givePermissionTo('system-owner');
        $actingUser->load('permissions');

        $response = $this->actingAs($actingUser)
            ->postJson(route('all-users.store', [], false), [
                'name' => '',
                'email' => 'not-an-email',
            ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['name', 'email']);
    }

    public function testStoreRejectsDuplicateEmail()
    {
        $existing = User::factory()->create();

        $actingUser = $this->getUser();
        $actingUser->givePermissionTo('system-owner');
        $actingUser->load('permissions');

        $response = $this->actingAs($actingUser)
            ->postJson(route('all-users.store', [], false), [
                'name' => 'Another Admin',
                'email' => $existing->email,
            ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['email']);
    }

    public function testStoreForbiddenWithoutSystemOwner()
    {
        $actingUser = $this->getUser();

        $response = $this->actingAs($actingUser)
            ->postJson(route('all-users.store', [], false), [
                'name' => 'Blocked Admin',
                'email' => 'blocked-admin@example.com',
            ]);

        $response->assertStatus(403);
        $this->assertNull(User::query()->where('email', 'blocked-admin@example.com')->first());
    }

    public function testIndexFilterByRole()
    {
        $this->seed(GlobalRoleSeed::class);

        $adminUsers = User::factory(2)->create();
        $editorUsers = User::factory(3)->create();

        foreach ($adminUsers as $user) {
            $user->assignRole('Administrator');
        }
        foreach ($editorUsers as $user) {
            $user->assignRole('Editor');
        }

        $response = $this->actingAs($this->getUser())
            ->getJson(route('all-users.index', ['role' => 'Administrator'], false));

        $response->assertStatus(200);
        $this->assertCount(2, $response->json('data'));

        $response = $this->actingAs($this->getUser())
            ->getJson(route('all-users.index', ['role' => 'Editor'], false));

        $response->assertStatus(200);
        $this->assertCount(3, $response->json('data'));

        $response = $this->actingAs($this->getUser())
            ->getJson(route('all-users.index', ['role' => 'NonExistentRole'], false));

        $response->assertStatus(200);
        $this->assertCount(0, $response->json('data'));
    }
}
