<?php

use App\Enums\Permission as PermissionEnum;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->seed(RolePermissionSeeder::class);
    Storage::fake('public');
});

// ─── Helper ───────────────────────────────────────────────

function createSettingUserWithPermission(PermissionEnum ...$permissions): User
{
    $user = User::factory()->create();

    $user->givePermissionTo(
        array_merge(
            [PermissionEnum::DASHBOARD_VIEW->value],
            array_map(fn (PermissionEnum $p) => $p->value, $permissions)
        )
    );

    $user->forgetCachedPermissions();

    return $user;
}

function validSystemPayload(): array
{
    return [
        'app_name' => 'DST Collection',
        'about_app' => 'Tentang aplikasi kami',
        'email' => 'info@dstcollection.com',
        'phone' => '081234567890',
        'address' => 'Jl. Merdeka No. 1',
    ];
}

function validMarketplaceFeeRule(): array
{
    return [
        'scope' => 'product',
        'value_type' => 'percent',
        'value' => 5.0,
    ];
}

function allMarketplaceFeeFields(): array
{
    $fields = [
        'tiktok_shop_platform_commission',
        'tiktok_shop_logistics_service_fee',
        'tiktok_shop_dynamic_commission',
        'tiktok_shop_order_processing_fee',
        'tiktok_shop_affiliate',
        'tiktok_shop_pre_order_service_fee',
        'shopee_admin_fee',
        'shopee_program_fee',
        'shopee_shipping_savings',
        'shopee_premium',
        'shopee_service_fee',
        'shopee_order_processing_fee',
        'shopee_ams_commission_fee',
        'shopee_pre_order',
        'shopee_live_extra',
    ];

    return collect($fields)->mapWithKeys(fn (string $key) => [$key => validMarketplaceFeeRule()])->all();
}

// ─── Index ────────────────────────────────────────────────

describe('Setting Index', function () {
    test('authenticated user with permission can view settings page', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW);

        $this->actingAs($user)
            ->get(route('admin.system.settings.index'))
            ->assertOk();
    });

    test('guest is redirected to login', function () {
        $this->get(route('admin.system.settings.index'))
            ->assertRedirect(route('login'));
    });

    test('user without permission is forbidden', function () {
        $user = User::factory()->create();

        $this->actingAs($user)
            ->get(route('admin.system.settings.index'))
            ->assertForbidden();
    });
});

// ─── Update System ────────────────────────────────────────

describe('Setting Update System', function () {
    test('authenticated user with permission can update system settings', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['logo'] = UploadedFile::fake()->image('logo.jpg', 100, 100);
        $payload['favicon'] = UploadedFile::fake()->image('favicon.png', 32, 32);
        $payload['login_cover'] = UploadedFile::fake()->image('login-cover.jpg', 800, 600);

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertRedirect(route('admin.system.settings.index'));

        $this->assertDatabaseHas('settings', [
            'group' => 'system',
            'name' => 'app_name',
        ]);
    });

    test('guest cannot update system settings', function () {
        $this->put(route('admin.system.settings.system.update'), validSystemPayload())
            ->assertRedirect(route('login'));
    });

    test('user without update permission is forbidden', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW);

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), validSystemPayload())
            ->assertForbidden();
    });

    test('app_name is required', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['app_name'] = '';

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('app_name');
    });

    test('app_name must not exceed 100 characters', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['app_name'] = str_repeat('a', 101);

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('app_name');
    });

    test('email must be valid', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['email'] = 'not-an-email';

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('email');
    });

    test('email is required', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['email'] = '';

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('email');
    });

    test('phone must be valid phone number', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['phone'] = '123invalid';

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('phone');
    });

    test('phone is required', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['phone'] = '';

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('phone');
    });

    test('about_app is required', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['about_app'] = '';

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('about_app');
    });

    test('address is required', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['address'] = '';

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('address');
    });

    test('logo must be an image when provided', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['logo'] = UploadedFile::fake()->create('document.pdf', 100);

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('logo');
    });

    test('favicon must be an image when provided', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = validSystemPayload();
        $payload['favicon'] = UploadedFile::fake()->create('document.pdf', 100);

        $this->actingAs($user)
            ->put(route('admin.system.settings.system.update'), $payload)
            ->assertSessionHasErrors('favicon');
    });
});

// ─── Update Social Media ──────────────────────────────────

describe('Setting Update Social Media', function () {
    test('authenticated user can update social media settings', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.social_media.update'), [
                'instagram_url' => 'https://instagram.com/dstcollection',
                'facebook_url' => 'https://facebook.com/dstcollection',
                'tiktok_url' => 'https://tiktok.com/@dstcollection',
            ])
            ->assertRedirect(route('admin.system.settings.index'));

        $this->assertDatabaseHas('settings', [
            'group' => 'social_media',
            'name' => 'instagram_url',
        ]);
    });

    test('guest cannot update social media settings', function () {
        $this->put(route('admin.system.settings.social_media.update'), [
            'instagram_url' => 'https://instagram.com/test',
        ])->assertRedirect(route('login'));
    });

    test('user without update permission is forbidden', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW);

        $this->actingAs($user)
            ->put(route('admin.system.settings.social_media.update'), [
                'instagram_url' => 'https://instagram.com/test',
            ])
            ->assertForbidden();
    });

    test('social media urls can be null', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.social_media.update'), [
                'instagram_url' => null,
                'facebook_url' => null,
                'tiktok_url' => null,
            ])
            ->assertRedirect(route('admin.system.settings.index'));

        $this->assertDatabaseHas('settings', [
            'group' => 'social_media',
            'name' => 'instagram_url',
        ]);
    });

    test('instagram_url must be a valid url', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.social_media.update'), [
                'instagram_url' => 'not-a-url',
            ])
            ->assertSessionHasErrors('instagram_url');
    });

    test('facebook_url must be a valid url', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.social_media.update'), [
                'facebook_url' => 'not-a-url',
            ])
            ->assertSessionHasErrors('facebook_url');
    });

    test('tiktok_url must be a valid url', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.social_media.update'), [
                'tiktok_url' => 'not-a-url',
            ])
            ->assertSessionHasErrors('tiktok_url');
    });
});

// ─── Update Marketplace ───────────────────────────────────

describe('Setting Update Marketplace', function () {
    test('authenticated user can update marketplace settings', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = allMarketplaceFeeFields();

        $this->actingAs($user)
            ->put(route('admin.system.settings.marketplace.update'), $payload)
            ->assertRedirect(route('admin.system.settings.index'));

        $this->assertDatabaseHas('settings', [
            'group' => 'marketplace',
            'name' => 'tiktok_shop_platform_commission',
        ]);
    });

    test('guest cannot update marketplace settings', function () {
        $this->put(route('admin.system.settings.marketplace.update'), allMarketplaceFeeFields())
            ->assertRedirect(route('login'));
    });

    test('user without update permission is forbidden', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW);

        $this->actingAs($user)
            ->put(route('admin.system.settings.marketplace.update'), allMarketplaceFeeFields())
            ->assertForbidden();
    });

    test('marketplace fee scope must be valid enum', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = allMarketplaceFeeFields();
        $payload['tiktok_shop_platform_commission']['scope'] = 'invalid';

        $this->actingAs($user)
            ->put(route('admin.system.settings.marketplace.update'), $payload)
            ->assertSessionHasErrors('tiktok_shop_platform_commission.scope');
    });

    test('marketplace fee value_type must be valid enum', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = allMarketplaceFeeFields();
        $payload['tiktok_shop_platform_commission']['value_type'] = 'invalid';

        $this->actingAs($user)
            ->put(route('admin.system.settings.marketplace.update'), $payload)
            ->assertSessionHasErrors('tiktok_shop_platform_commission.value_type');
    });

    test('marketplace fee value must be numeric', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = allMarketplaceFeeFields();
        $payload['tiktok_shop_platform_commission']['value'] = 'not-a-number';

        $this->actingAs($user)
            ->put(route('admin.system.settings.marketplace.update'), $payload)
            ->assertSessionHasErrors('tiktok_shop_platform_commission.value');
    });

    test('marketplace fee percent value must not exceed 100', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = allMarketplaceFeeFields();
        $payload['tiktok_shop_platform_commission']['value_type'] = 'percent';
        $payload['tiktok_shop_platform_commission']['value'] = 150.0;

        $this->actingAs($user)
            ->put(route('admin.system.settings.marketplace.update'), $payload)
            ->assertSessionHasErrors('tiktok_shop_platform_commission.value');
    });

    test('marketplace flat fee must not have decimals', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $payload = allMarketplaceFeeFields();
        $payload['shopee_admin_fee']['value_type'] = 'flat';
        $payload['shopee_admin_fee']['value'] = 1500.5;

        $this->actingAs($user)
            ->put(route('admin.system.settings.marketplace.update'), $payload)
            ->assertSessionHasErrors('shopee_admin_fee.value');
    });
});

// ─── Update HR ────────────────────────────────────────────

describe('Setting Update HR', function () {
    test('authenticated user can update HR settings', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.hr.update'), [
                'scheduled_check_in_time' => '09:00',
                'scheduled_check_out_time' => '18:00',
                'late_penalty_amount' => 50000,
                'absent_penalty_amount' => 100000,
            ])
            ->assertRedirect(route('admin.system.settings.index'));

        $this->assertDatabaseHas('settings', [
            'group' => 'hr',
            'name' => 'scheduled_check_in_time',
        ]);

        $this->assertDatabaseHas('settings', [
            'group' => 'hr',
            'name' => 'late_penalty_amount',
        ]);
    });

    test('guest cannot update HR settings', function () {
        $this->put(route('admin.system.settings.hr.update'), [
            'scheduled_check_in_time' => '09:00',
            'scheduled_check_out_time' => '18:00',
            'late_penalty_amount' => 0,
            'absent_penalty_amount' => 0,
        ])->assertRedirect(route('login'));
    });

    test('user without update permission is forbidden', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW);

        $this->actingAs($user)
            ->put(route('admin.system.settings.hr.update'), [
                'scheduled_check_in_time' => '09:00',
                'scheduled_check_out_time' => '18:00',
                'late_penalty_amount' => 0,
                'absent_penalty_amount' => 0,
            ])
            ->assertForbidden();
    });

    test('scheduled_check_in_time is required', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.hr.update'), [
                'scheduled_check_in_time' => '',
                'scheduled_check_out_time' => '18:00',
                'late_penalty_amount' => 0,
                'absent_penalty_amount' => 0,
            ])
            ->assertSessionHasErrors('scheduled_check_in_time');
    });

    test('scheduled_check_in_time must match HH:MM format', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.hr.update'), [
                'scheduled_check_in_time' => '9:00',
                'scheduled_check_out_time' => '18:00',
                'late_penalty_amount' => 0,
                'absent_penalty_amount' => 0,
            ])
            ->assertSessionHasErrors('scheduled_check_in_time');
    });

    test('scheduled_check_out_time is required', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.hr.update'), [
                'scheduled_check_in_time' => '09:00',
                'scheduled_check_out_time' => '',
                'late_penalty_amount' => 0,
                'absent_penalty_amount' => 0,
            ])
            ->assertSessionHasErrors('scheduled_check_out_time');
    });

    test('late_penalty_amount must be integer', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.hr.update'), [
                'scheduled_check_in_time' => '09:00',
                'scheduled_check_out_time' => '18:00',
                'late_penalty_amount' => 'not-a-number',
                'absent_penalty_amount' => 0,
            ])
            ->assertSessionHasErrors('late_penalty_amount');
    });

    test('late_penalty_amount must not be negative', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.hr.update'), [
                'scheduled_check_in_time' => '09:00',
                'scheduled_check_out_time' => '18:00',
                'late_penalty_amount' => -1000,
                'absent_penalty_amount' => 0,
            ])
            ->assertSessionHasErrors('late_penalty_amount');
    });

    test('absent_penalty_amount must be integer', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.hr.update'), [
                'scheduled_check_in_time' => '09:00',
                'scheduled_check_out_time' => '18:00',
                'late_penalty_amount' => 0,
                'absent_penalty_amount' => 'not-a-number',
            ])
            ->assertSessionHasErrors('absent_penalty_amount');
    });
});

// ─── Update Homepage ──────────────────────────────────────

describe('Setting Update Homepage', function () {
    test('authenticated user can update homepage settings', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.homepage.update'), [])
            ->assertRedirect(route('admin.system.settings.index'));
    });

    test('guest cannot update homepage settings', function () {
        $this->put(route('admin.system.settings.homepage.update'), [])
            ->assertRedirect(route('login'));
    });

    test('user without update permission is forbidden', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW);

        $this->actingAs($user)
            ->put(route('admin.system.settings.homepage.update'), [])
            ->assertForbidden();
    });

    test('hero_image must be an image when provided', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.homepage.update'), [
                'hero_image' => UploadedFile::fake()->create('document.pdf', 100),
            ])
            ->assertSessionHasErrors('hero_image');
    });

    test('about_image must be an image when provided', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.homepage.update'), [
                'about_image' => UploadedFile::fake()->create('document.pdf', 100),
            ])
            ->assertSessionHasErrors('about_image');
    });

    test('gallery_images must be an array of images', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $this->actingAs($user)
            ->put(route('admin.system.settings.homepage.update'), [
                'gallery_images' => ['not-a-file'],
            ])
            ->assertSessionHasErrors('gallery_images.0');
    });

    test('gallery_images must not exceed 10 items', function () {
        $user = createSettingUserWithPermission(PermissionEnum::SETTINGS_VIEW, PermissionEnum::SETTINGS_UPDATE);

        $images = [];
        for ($i = 0; $i < 11; $i++) {
            $images[] = UploadedFile::fake()->image("photo-{$i}.jpg");
        }

        $this->actingAs($user)
            ->put(route('admin.system.settings.homepage.update'), [
                'gallery_images' => $images,
            ])
            ->assertSessionHasErrors('gallery_images');
    });
});
