<?php

namespace Tests\Feature;

use App\Mail\AdminTestMail;
use App\Models\AdminPermission;
use App\Models\AdminUser;
use App\Models\Category;
use App\Models\Coupon;
use App\Models\Product;
use App\Models\SiteSetting;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class AdminSettingsTest extends TestCase
{
    use RefreshDatabase;

    public function test_admin_can_upload_product_fallback_image_setting(): void
    {
        Storage::fake('public');

        $admin = $this->settingsAdmin();

        $this->actingAs($admin, 'admin')
            ->post('/admin/settings', [
                'product_fallback_image' => UploadedFile::fake()->image('fallback.jpg', 900, 900),
                'currency_symbol' => 'Rs',
                'currency_position' => 'left',
                'shipping_charge' => '250',
                'free_shipping_minimum' => '5000',
                'admin_order_email_1' => 'orders@example.com',
                'admin_order_email_2' => 'owner@example.com',
                'sitemap_enabled' => '1',
                'ibft_enabled' => '1',
                'cod_enabled' => '1',
                'ibft_iban' => 'PK00BANK0000000000000000',
                'ibft_account_title' => 'StayCart Pvt Ltd',
                'ibft_bank_name' => 'Demo Bank',
            ])
            ->assertSessionHas('status');

        $path = SiteSetting::value(SiteSetting::PRODUCT_FALLBACK_IMAGE);

        $this->assertNotNull($path);
        Storage::disk('public')->assertExists($path);
        $this->assertSame('Rs', SiteSetting::value(SiteSetting::CURRENCY_SYMBOL));
        $this->assertSame('left', SiteSetting::value(SiteSetting::CURRENCY_POSITION));
        $this->assertSame('250', SiteSetting::value(SiteSetting::SHIPPING_CHARGE));
        $this->assertSame('5000', SiteSetting::value(SiteSetting::FREE_SHIPPING_MINIMUM));
        $this->assertSame(['orders@example.com', 'owner@example.com'], SiteSetting::adminOrderEmails());
        $this->assertTrue(SiteSetting::sitemapEnabled());
        $this->assertTrue(SiteSetting::ibftEnabled());
        $this->assertTrue(SiteSetting::codEnabled());
        $this->assertSame('PK00BANK0000000000000000', SiteSetting::value(SiteSetting::IBFT_IBAN));
        $this->assertSame('StayCart Pvt Ltd', SiteSetting::value(SiteSetting::IBFT_ACCOUNT_TITLE));
        $this->assertSame('Demo Bank', SiteSetting::value(SiteSetting::IBFT_BANK_NAME));
    }

    public function test_storefront_uses_uploaded_fallback_image_when_product_has_no_media(): void
    {
        Storage::fake('public');

        $path = UploadedFile::fake()
            ->image('fallback.jpg', 900, 900)
            ->store('settings', 'public');
        SiteSetting::put(SiteSetting::PRODUCT_FALLBACK_IMAGE, $path);

        $category = Category::query()->create([
            'name' => 'Home',
            'slug' => 'home',
            'is_active' => true,
        ]);

        Product::query()->create([
            'category_id' => $category->id,
            'name' => 'No Image Product',
            'slug' => 'no-image-product',
            'price' => 44,
            'stock' => 10,
            'rating' => 4.1,
            'is_active' => true,
        ]);

        $this->get('/')
            ->assertOk()
            ->assertSee("/storage/{$path}", false);
    }

    public function test_public_storage_route_serves_uploaded_files_without_symlink(): void
    {
        Storage::fake('public');

        $path = UploadedFile::fake()
            ->image('fallback.jpg', 900, 900)
            ->store('settings', 'public');

        $this->get("/storage/{$path}")
            ->assertOk()
            ->assertHeader('content-type', 'image/jpeg');
    }

    public function test_admin_without_settings_permission_cannot_update_settings(): void
    {
        $admin = AdminUser::query()->create([
            'name' => 'Catalog Admin',
            'email' => 'catalog-only-settings@example.com',
            'password' => Hash::make('secret-password'),
            'is_active' => true,
        ]);

        $permission = AdminPermission::query()->create(['name' => 'Manage Products', 'slug' => 'products.manage']);
        $admin->permissions()->attach($permission);

        $this->actingAs($admin, 'admin')
            ->post('/admin/settings', [
                'product_fallback_image' => UploadedFile::fake()->image('fallback.jpg'),
                'currency_symbol' => 'Rs',
                'currency_position' => 'left',
                'shipping_charge' => '250',
                'free_shipping_minimum' => '5000',
            ])
            ->assertForbidden();
    }

    public function test_default_currency_is_rs_on_left_and_can_move_to_right(): void
    {
        $this->assertSame('Rs 1,500', SiteSetting::formatPrice(1500));

        SiteSetting::put(SiteSetting::CURRENCY_SYMBOL, 'PKR');
        SiteSetting::put(SiteSetting::CURRENCY_POSITION, 'right');

        $this->assertSame('1,500 PKR', SiteSetting::formatPrice(1500));
    }

    public function test_shipping_settings_calculate_checkout_shipping(): void
    {
        SiteSetting::put(SiteSetting::SHIPPING_CHARGE, '250');
        SiteSetting::put(SiteSetting::FREE_SHIPPING_MINIMUM, '5000');

        $this->assertSame(250.0, SiteSetting::calculateShipping(4999));
        $this->assertSame(0.0, SiteSetting::calculateShipping(5000));
    }

    public function test_admin_can_send_test_email_to_order_recipients(): void
    {
        Mail::fake();

        $admin = $this->settingsAdmin();
        SiteSetting::put(SiteSetting::ADMIN_ORDER_EMAIL_1, 'orders@example.com');
        SiteSetting::put(SiteSetting::ADMIN_ORDER_EMAIL_2, 'owner@example.com');

        $this->actingAs($admin, 'admin')
            ->post('/admin/settings/test-email')
            ->assertSessionHas('status');

        Mail::assertSent(AdminTestMail::class, fn (AdminTestMail $mail) => $mail->hasTo('orders@example.com'));
        Mail::assertSent(AdminTestMail::class, fn (AdminTestMail $mail) => $mail->hasTo('owner@example.com'));
    }

    public function test_sitemap_route_and_generator_include_dynamic_urls(): void
    {
        $admin = $this->settingsAdmin();
        SiteSetting::put(SiteSetting::SITEMAP_ENABLED, '1');

        $category = Category::query()->create([
            'name' => 'SEO Category',
            'slug' => 'seo-category',
            'is_active' => true,
        ]);

        Product::query()->create([
            'category_id' => $category->id,
            'name' => 'SEO Product',
            'slug' => 'seo-product',
            'price' => 100,
            'stock' => 5,
            'is_active' => true,
        ]);

        $this->get('/sitemap.xml')
            ->assertOk()
            ->assertHeader('content-type', 'application/xml')
            ->assertSee(route('storefront.categories.show', $category), false)
            ->assertSee('/products/seo-product', false);

        $this->actingAs($admin, 'admin')
            ->post('/admin/settings/generate-sitemap')
            ->assertSessionHas('status');

        $this->assertFileExists(public_path('sitemap.xml'));
    }

    public function test_admin_can_create_update_toggle_and_delete_coupon(): void
    {
        $admin = $this->settingsAdmin();

        $this->actingAs($admin, 'admin')
            ->post('/admin/coupons', [
                'code' => 'save10',
                'type' => Coupon::TYPE_PERCENTAGE,
                'value' => '10',
                'minimum_order_amount' => '500',
                'expires_at' => now()->addWeek()->format('Y-m-d'),
                'is_active' => '1',
            ])
            ->assertRedirect(route('admin.coupons.index'));

        $coupon = Coupon::query()->where('code', 'SAVE10')->firstOrFail();

        $this->assertSame(Coupon::TYPE_PERCENTAGE, $coupon->type);
        $this->assertSame(100.0, $coupon->discountFor(1000));

        $this->actingAs($admin, 'admin')
            ->patch("/admin/coupons/{$coupon->id}", [
                'code' => 'SAVE200',
                'type' => Coupon::TYPE_FIXED,
                'value' => '200',
                'minimum_order_amount' => '1000',
                'is_active' => '1',
            ])
            ->assertRedirect(route('admin.coupons.index'));

        $this->assertDatabaseHas('coupons', [
            'id' => $coupon->id,
            'code' => 'SAVE200',
            'type' => Coupon::TYPE_FIXED,
        ]);

        $this->actingAs($admin, 'admin')
            ->patch("/admin/coupons/{$coupon->id}/toggle")
            ->assertSessionHas('status');

        $this->assertFalse($coupon->fresh()->is_active);

        $this->actingAs($admin, 'admin')
            ->delete("/admin/coupons/{$coupon->id}")
            ->assertSessionHas('status');

        $this->assertDatabaseMissing('coupons', ['id' => $coupon->id]);
    }

    private function settingsAdmin(): AdminUser
    {
        $admin = AdminUser::query()->create([
            'name' => 'Settings Admin',
            'email' => 'settings.admin@example.com',
            'password' => Hash::make('secret-password'),
            'is_active' => true,
        ]);

        $admin->permissions()->attach([
            AdminPermission::query()->create(['name' => 'View Dashboard', 'slug' => 'dashboard.view'])->id,
            AdminPermission::query()->create(['name' => 'Manage Settings', 'slug' => 'settings.manage'])->id,
        ]);

        return $admin;
    }
}
