<?php

namespace Tests\Feature;

use App\Models\AuditLog;
use App\Models\ActivityLog;
use App\Models\Order;
use App\Models\Product;
use App\Models\Coupon;
use App\Models\User;
use App\Models\Setting;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;
use Exception;

class AuditActivityLogsTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Test that order events (created, updated, deleted) correctly trigger activity logs.
     */
    public function test_order_events_trigger_activity_logs()
    {
        $admin = User::factory()->create(['role' => 'admin']);
        $this->actingAs($admin);

        // 1. Create order (with required non-null fields)
        $order = Order::create([
            'customer_name' => 'John Doe',
            'customer_phone' => '01711111111',
            'shipping_address' => 'Dhaka, Bangladesh',
            'status' => 'pending',
            'payment_status' => 'pending',
            'subtotal' => 120.00,
            'shipping_cost' => 80.00,
            'grand_total' => 200.00,
        ]);

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Order::class,
            'loggable_id' => $order->id,
            'action' => 'created',
        ]);

        // 2. Update order
        $order->update([
            'status' => 'processing',
        ]);

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Order::class,
            'loggable_id' => $order->id,
            'action' => 'updated',
        ]);

        $updatedLog = ActivityLog::where('loggable_type', Order::class)
            ->where('loggable_id', $order->id)
            ->where('action', 'updated')
            ->first();

        $this->assertNotNull($updatedLog);
        $this->assertEquals('pending', $updatedLog->properties['old']['status'] ?? null);
        $this->assertEquals('processing', $updatedLog->properties['attributes']['status'] ?? null);

        // 3. Delete order
        $orderId = $order->id;
        $order->delete();

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Order::class,
            'loggable_id' => $orderId,
            'action' => 'deleted',
        ]);
    }

    /**
     * Test that product events trigger activity logs.
     */
    public function test_product_events_trigger_activity_logs()
    {
        $admin = User::factory()->create(['role' => 'admin']);
        $this->actingAs($admin);

        // Create product (with required non-null fields, e.g. sku)
        $product = Product::create([
            'name' => 'Sample Product',
            'slug' => 'sample-product',
            'sku' => 'SAMPLE-SKU',
            'price' => 100.00,
            'quantity' => 10,
        ]);

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Product::class,
            'loggable_id' => $product->id,
            'action' => 'created',
        ]);

        // Update product price
        $product->update([
            'price' => 120.00,
        ]);

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Product::class,
            'loggable_id' => $product->id,
            'action' => 'updated',
        ]);

        // Delete product
        $productId = $product->id;
        $product->delete();

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Product::class,
            'loggable_id' => $productId,
            'action' => 'deleted',
        ]);
    }

    /**
     * Test that coupon events trigger activity logs.
     */
    public function test_coupon_events_trigger_activity_logs()
    {
        $admin = User::factory()->create(['role' => 'admin']);
        $this->actingAs($admin);

        // Create coupon
        $coupon = Coupon::create([
            'code' => 'DISCOUNT50',
            'type' => 'fixed',
            'value' => 50.00,
            'active' => true,
        ]);

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Coupon::class,
            'loggable_id' => $coupon->id,
            'action' => 'created',
        ]);

        // Update coupon value
        $coupon->update([
            'value' => 60.00,
        ]);

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Coupon::class,
            'loggable_id' => $coupon->id,
            'action' => 'updated',
        ]);

        // Delete coupon
        $couponId = $coupon->id;
        $coupon->delete();

        $this->assertDatabaseHas('activity_logs', [
            'loggable_type' => Coupon::class,
            'loggable_id' => $couponId,
            'action' => 'deleted',
        ]);
    }

    /**
     * Test that saving settings triggers audit logs, with sensitive fields masked.
     */
    public function test_settings_save_triggers_audit_logs_with_masked_sensitive_fields()
    {
        $admin = User::factory()->create(['role' => 'admin']);
        $this->actingAs($admin);
        \Filament\Facades\Filament::setCurrentPanel(\Filament\Facades\Filament::getPanel('admin'));

        // Initialize setting values in the database first
        Setting::setValue('site_name', 'Old Store Name', 'general');
        Setting::setValue('currency_symbol', '৳', 'general');
        Setting::setValue('default_courier', 'steadfast', 'courier');
        Setting::setValue('steadfast_api_key', 'old-api-key', 'courier');

        // Test Livewire component of settings page
        Livewire::actingAs($admin)
            ->test(\App\Filament\Pages\ManageSettings::class)
            ->fillForm([
                'site_name' => 'New Store Name',
                'currency_symbol' => '৳',
                'default_courier' => 'steadfast',
                'steadfast_api_key' => 'new-secret-api-key',
            ])
            ->call('save')
            ->assertHasNoFormErrors();

        // Assert audit log exists in database
        $auditLog = AuditLog::where('event_type', 'settings_updated')->first();
        $this->assertNotNull($auditLog);
        
        $this->assertStringContainsString('site_name', $auditLog->description);
        $this->assertStringContainsString('steadfast_api_key', $auditLog->description);

        // Verify values masking
        $oldValues = $auditLog->old_values;
        $newValues = $auditLog->new_values;

        // Unsensitive key
        $this->assertEquals('Old Store Name', $oldValues['site_name']);
        $this->assertEquals('New Store Name', $newValues['site_name']);

        // Sensitive key (must be masked)
        $this->assertEquals('********', $oldValues['steadfast_api_key']);
        $this->assertEquals('********', $newValues['steadfast_api_key']);
    }

    /**
     * Test that audit logs are write-protected (immutable/append-only).
     */
    public function test_audit_logs_are_read_only_and_cannot_be_updated_or_deleted()
    {
        $log = AuditLog::create([
            'event_type' => 'test_event',
            'description' => 'Test event description',
        ]);

        $this->assertNotNull($log->id);

        // Try updating
        try {
            $log->update(['description' => 'Modified description']);
            $this->fail('Expected Exception was not thrown when updating an Audit Log.');
        } catch (Exception $e) {
            $this->assertStringContainsString('cannot be updated', $e->getMessage());
        }

        // Try deleting
        try {
            $log->delete();
            $this->fail('Expected Exception was not thrown when deleting an Audit Log.');
        } catch (Exception $e) {
            $this->assertStringContainsString('cannot be deleted', $e->getMessage());
        }

        // Verify it still exists unchanged
        $this->assertDatabaseHas('audit_logs', [
            'id' => $log->id,
            'description' => 'Test event description',
        ]);
    }

    /**
     * Test that user updates (password, role) and login events trigger audit logs.
     */
    public function test_user_password_change_and_auth_events_trigger_audit_logs()
    {
        $user = User::factory()->create([
            'role' => 'staff',
            'password' => bcrypt('old-password'),
        ]);

        // 1. Trigger login success event manually
        event(new \Illuminate\Auth\Events\Login('web', $user, false));

        $this->assertDatabaseHas('audit_logs', [
            'event_type' => 'login_success',
            'user_id' => $user->id,
        ]);

        // 2. Trigger login failed event manually
        event(new \Illuminate\Auth\Events\Failed('web', $user, ['email' => $user->email]));

        $this->assertDatabaseHas('audit_logs', [
            'event_type' => 'login_failed',
            'description' => 'Failed login attempt for email: ' . $user->email,
        ]);

        // 3. Update password
        $user->update([
            'password' => bcrypt('new-password'),
        ]);

        $this->assertDatabaseHas('audit_logs', [
            'event_type' => 'password_changed',
            'description' => 'Password updated for: ' . $user->name . ' (' . $user->email . ')',
        ]);
        
        // Assert password masking in database
        $log = AuditLog::where('event_type', 'password_changed')->first();
        $this->assertNotNull($log);
        $this->assertEquals('********', $log->old_values['password'] ?? null);
        $this->assertEquals('********', $log->new_values['password'] ?? null);

        // 4. Update role
        $user->update([
            'role' => 'admin',
        ]);

        $this->assertDatabaseHas('audit_logs', [
            'event_type' => 'user_role_changed',
            'description' => 'Role updated for: ' . $user->name . ' (' . $user->email . ')',
        ]);
    }
}
