<?php

namespace Tests\Feature;

use App\Models\Order;
use App\Models\Setting;
use App\Models\User;
use App\Models\NotificationLog;
use App\Services\NotificationDispatcher;
use App\Services\SmsService;
use App\Mail\OrderInvoiceMail;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;

class NotificationSystemTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();

        // Clear settings file cache before each test for isolation
        \App\Models\Setting::clearCache();

        // Seed settings required for notifications
        Setting::setValue('site_name', 'Sawdabazar', 'general');
        Setting::setValue('default_sms_gateway', 'bulksmsbd', 'sms');
        Setting::setValue('bulksmsbd_api_key', 'test_api_key', 'sms');
        Setting::setValue('bulksmsbd_sender_id', 'test_sender_id', 'sms');
    }

    /**
     * Test that sending SMS logs results in notification_logs.
     */
    public function test_sending_sms_logs_to_database()
    {
        Http::fake([
            'bulksmsbd.net/*' => Http::response([
                'response_code' => 200,
                'success_message' => 'SMS Sent'
            ], 200)
        ]);

        $order = Order::create([
            'order_number' => 'SB-TEST-123',
            'customer_name' => 'John Doe',
            'customer_phone' => '01712345678',
            'shipping_address' => 'Uttara, Dhaka',
            'billing_address' => 'Uttara, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'pending',
            'status' => 'pending',
        ]);

        SmsService::sendSms('01712345678', 'Test Message Content', $order->id);

        $this->assertDatabaseHas('notification_logs', [
            'order_id' => $order->id,
            'type' => 'sms',
            'channel' => 'bulksmsbd',
            'recipient' => '8801712345678',
            'message_preview' => 'Test Message Content',
            'status' => 'sent',
            'error_message' => null,
        ]);
    }

    /**
     * Test that order placement sends notifications and logs them.
     */
    public function test_order_placement_dispatches_and_logs()
    {
        Mail::fake();
        Http::fake([
            'bulksmsbd.net/*' => Http::response([
                'response_code' => 200,
                'success_message' => 'SMS Sent'
            ], 200)
        ]);

        $admin = User::factory()->create([
            'role' => 'admin',
        ]);

        $order = Order::create([
            'order_number' => 'SB-TEST-123',
            'customer_name' => 'John Doe',
            'customer_phone' => '01712345678',
            'customer_email' => 'customer@example.com',
            'shipping_address' => 'Uttara, Dhaka',
            'billing_address' => 'Uttara, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'pending',
            'status' => 'pending',
        ]);

        NotificationDispatcher::dispatchOrderPlaced($order);

        // Verify customer SMS not logged/sent
        $this->assertDatabaseMissing('notification_logs', [
            'order_id' => $order->id,
            'type' => 'sms',
            'recipient' => '8801712345678',
        ]);

        // Verify customer Email not logged/sent
        $this->assertDatabaseMissing('notification_logs', [
            'order_id' => $order->id,
            'type' => 'email',
            'recipient' => 'customer@example.com',
        ]);

        Mail::assertNothingQueued();

        // Verify Database Notification for admin
        $this->assertDatabaseHas('notifications', [
            'notifiable_id' => $admin->id,
            'notifiable_type' => User::class,
        ]);

        // Verify database notification action contains correct edit order url
        $notification = \Illuminate\Notifications\DatabaseNotification::first();
        $this->assertNotNull($notification);
        $data = $notification->data;
        $this->assertArrayHasKey('actions', $data);
        $this->assertEquals('view', $data['actions'][0]['name']);
        $this->assertEquals('View Order', $data['actions'][0]['label']);
        $this->assertStringContainsString('/admin/orders/' . $order->id . '/edit', $data['actions'][0]['url']);
    }

    /**
     * Test that customer notification toggles are respected.
     */
    public function test_customer_toggles_prevent_notifications()
    {
        Mail::fake();
        Http::fake(); // Should not even be hit

        Setting::setValue('sms_order_placed_enabled', false, 'sms');
        Setting::setValue('email_order_placed_enabled', false, 'sms');

        $order = Order::create([
            'order_number' => 'SB-TEST-123',
            'customer_name' => 'John Doe',
            'customer_phone' => '01712345678',
            'customer_email' => 'customer@example.com',
            'shipping_address' => 'Uttara, Dhaka',
            'billing_address' => 'Uttara, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'pending',
            'status' => 'pending',
        ]);

        NotificationDispatcher::dispatchOrderPlaced($order);

        // Verify no notification logs created
        $this->assertEquals(0, NotificationLog::count());

        Mail::assertNothingQueued();
    }

    /**
     * Test delivery and cancellation notification dispatches.
     */
    public function test_status_transitions_dispatch_notifications()
    {
        Mail::fake();
        Http::fake([
            'bulksmsbd.net/*' => Http::response([
                'response_code' => 200,
                'success_message' => 'SMS Sent'
            ], 200)
        ]);

        $order = Order::create([
            'order_number' => 'SB-TEST-123',
            'customer_name' => 'John Doe',
            'customer_phone' => '01712345678',
            'customer_email' => 'customer@example.com',
            'shipping_address' => 'Uttara, Dhaka',
            'billing_address' => 'Uttara, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'pending',
            'status' => 'pending',
        ]);

        // 1. Delivered
        NotificationDispatcher::dispatchForOrder($order, 'delivered');

        $this->assertDatabaseMissing('notification_logs', [
            'order_id' => $order->id,
            'type' => 'sms',
        ]);

        Mail::assertNothingQueued();

        // 2. Cancelled
        NotificationDispatcher::dispatchForOrder($order, 'cancelled');

        $this->assertDatabaseMissing('notification_logs', [
            'order_id' => $order->id,
            'type' => 'sms',
        ]);

        Mail::assertNothingQueued();
    }

    /**
     * Test admin alert alerts on order placement.
     */
    public function test_admin_alerts_are_sent()
    {
        Mail::fake();
        Http::fake([
            'bulksmsbd.net/*' => Http::response([
                'response_code' => 200,
                'success_message' => 'SMS Sent'
            ], 200)
        ]);

        Setting::setValue('admin_order_placed_sms_enabled', true, 'sms');
        Setting::setValue('admin_order_placed_email_enabled', true, 'sms');
        Setting::setValue('admin_notification_phone', '01799999999', 'sms');
        Setting::setValue('admin_notification_email', 'admin@example.com', 'sms');

        // Turn off customer notifications to isolate admin logs
        Setting::setValue('sms_order_placed_enabled', false, 'sms');
        Setting::setValue('email_order_placed_enabled', false, 'sms');

        $order = Order::create([
            'order_number' => 'SB-TEST-456',
            'customer_name' => 'Jane Smith',
            'customer_phone' => '01712345678',
            'customer_email' => 'customer@example.com',
            'shipping_address' => 'Uttara, Dhaka',
            'billing_address' => 'Uttara, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'pending',
            'status' => 'pending',
        ]);

        NotificationDispatcher::dispatchOrderPlaced($order);

        // Verify admin SMS log
        $this->assertDatabaseHas('notification_logs', [
            'order_id' => $order->id,
            'type' => 'sms',
            'recipient' => '8801799999999',
            'message_preview' => 'New order #SB-TEST-456 placed by Jane Smith. Total: ৳1,080.00.',
        ]);

        // Verify admin Email log
        $this->assertDatabaseHas('notification_logs', [
            'order_id' => $order->id,
            'type' => 'email',
            'recipient' => 'admin@example.com',
            'status' => 'sent',
        ]);

        Mail::assertQueued(OrderInvoiceMail::class, function ($mail) use ($order) {
            return $mail->order->id === $order->id && $mail->isAdminAlert === true;
        });
    }

    /**
     * Test that manually creating an order via Filament dispatches order placed notifications.
     */
    public function test_manual_order_creation_triggers_notifications()
    {
        Mail::fake();
        Http::fake([
            'bulksmsbd.net/*' => Http::response([
                'response_code' => 200,
                'success_message' => 'SMS Sent'
            ], 200)
        ]);

        $admin = User::factory()->create([
            'role' => 'admin',
        ]);

        $product = \App\Models\Product::create([
            'name' => 'Test Product',
            'slug' => 'test-product',
            'sku' => 'TP-001',
            'price' => 500.00,
            'quantity' => 10,
        ]);

        $this->actingAs($admin);
        \Livewire\Livewire::actingAs($admin);
        \Filament\Facades\Filament::setCurrentPanel(\Filament\Facades\Filament::getPanel('admin'));

        \Livewire\Livewire::test(\App\Filament\Resources\Orders\Pages\CreateOrder::class)
            ->fillForm([
                'customer_name' => 'Jane Admin Created',
                'customer_phone' => '01712345679',
                'customer_email' => 'manual@example.com',
                'shipping_address' => 'Uttara, Dhaka',
                'billing_address' => 'Uttara, Dhaka',
                'subtotal' => 500.00,
                'shipping_cost' => 80.00,
                'grand_total' => 580.00,
                'payment_method' => 'cod',
                'payment_status' => 'pending',
                'status' => 'pending',
                'items' => [
                    [
                        'product_id' => $product->id,
                        'quantity' => 1,
                        'price' => 500.00,
                        'product_name' => 'Test Product',
                        'product_sku' => 'TP-001',
                    ]
                ],
            ])
            ->call('create')
            ->assertHasNoFormErrors();

        // Verify order created and notification dispatched
        $order = Order::where('customer_name', 'Jane Admin Created')->first();
        $this->assertNotNull($order);

        $this->assertDatabaseMissing('notification_logs', [
            'order_id' => $order->id,
            'type' => 'sms',
        ]);

        Mail::assertNothingQueued();

        // Verify Database Notification for admin
        $this->assertDatabaseHas('notifications', [
            'notifiable_id' => $admin->id,
            'notifiable_type' => User::class,
        ]);
    }

    /**
     * Test that manually updating payment status to paid via Filament dispatches payment received notification.
     */
    public function test_manual_payment_status_update_triggers_notifications()
    {
        Mail::fake();
        Http::fake([
            'bulksmsbd.net/*' => Http::response([
                'response_code' => 200,
                'success_message' => 'SMS Sent'
            ], 200)
        ]);

        $admin = User::factory()->create([
            'role' => 'admin',
        ]);

        $order = Order::create([
            'order_number' => 'SB-TEST-789',
            'customer_name' => 'Mark Unpaid',
            'customer_phone' => '01712345680',
            'customer_email' => 'unpaid@example.com',
            'shipping_address' => 'Uttara, Dhaka',
            'billing_address' => 'Uttara, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'pending',
            'status' => 'pending',
        ]);

        $this->actingAs($admin);
        \Livewire\Livewire::actingAs($admin);
        \Filament\Facades\Filament::setCurrentPanel(\Filament\Facades\Filament::getPanel('admin'));

        \Livewire\Livewire::test(\App\Filament\Resources\Orders\Pages\EditOrder::class, [
            'record' => $order->getKey(),
        ])
            ->fillForm([
                'payment_status' => 'paid',
            ])
            ->call('save')
            ->assertHasNoFormErrors();

        // Verify customer SMS not logged/sent
        $this->assertDatabaseMissing('notification_logs', [
            'order_id' => $order->id,
            'type' => 'sms',
        ]);

        Mail::assertNothingQueued();

        // Verify Database Notification for admin
        $this->assertDatabaseHas('notifications', [
            'notifiable_id' => $admin->id,
            'notifiable_type' => User::class,
        ]);

        $notification = \Illuminate\Notifications\DatabaseNotification::orderBy('created_at', 'desc')->first();
        $this->assertNotNull($notification);
        $this->assertEquals('Payment Received', $notification->data['title']);
    }

    /**
     * Test that manually updating payment status to refunded triggers database notification.
     */
    public function test_manual_payment_refund_triggers_notifications()
    {
        Mail::fake();
        Http::fake();

        $admin = User::factory()->create([
            'role' => 'admin',
        ]);

        $order = Order::create([
            'order_number' => 'SB-TEST-REF',
            'customer_name' => 'Mark Refund',
            'customer_phone' => '01712345681',
            'customer_email' => 'refund@example.com',
            'shipping_address' => 'Uttara, Dhaka',
            'billing_address' => 'Uttara, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'paid',
            'status' => 'processing',
        ]);

        $this->actingAs($admin);
        \Livewire\Livewire::actingAs($admin);
        \Filament\Facades\Filament::setCurrentPanel(\Filament\Facades\Filament::getPanel('admin'));

        \Livewire\Livewire::test(\App\Filament\Resources\Orders\Pages\EditOrder::class, [
            'record' => $order->getKey(),
        ])
            ->fillForm([
                'payment_status' => 'refunded',
            ])
            ->call('save')
            ->assertHasNoFormErrors();

        // Verify Database Notification for admin
        $this->assertDatabaseHas('notifications', [
            'notifiable_id' => $admin->id,
            'notifiable_type' => User::class,
        ]);

        $notification = \Illuminate\Notifications\DatabaseNotification::orderBy('created_at', 'desc')->first();
        $this->assertNotNull($notification);
        $this->assertEquals('Payment Refunded', $notification->data['title']);
    }

    /**
     * Test that manually updating payment status to failed triggers database notification.
     */
    public function test_manual_payment_failure_triggers_notifications()
    {
        Mail::fake();
        Http::fake();

        $admin = User::factory()->create([
            'role' => 'admin',
        ]);

        $order = Order::create([
            'order_number' => 'SB-TEST-FAIL',
            'customer_name' => 'Mark Fail',
            'customer_phone' => '01712345682',
            'customer_email' => 'fail@example.com',
            'shipping_address' => 'Uttara, Dhaka',
            'billing_address' => 'Uttara, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'pending',
            'status' => 'pending',
        ]);

        $this->actingAs($admin);
        \Livewire\Livewire::actingAs($admin);
        \Filament\Facades\Filament::setCurrentPanel(\Filament\Facades\Filament::getPanel('admin'));

        \Livewire\Livewire::test(\App\Filament\Resources\Orders\Pages\EditOrder::class, [
            'record' => $order->getKey(),
        ])
            ->fillForm([
                'payment_status' => 'failed',
            ])
            ->call('save')
            ->assertHasNoFormErrors();

        // Verify Database Notification for admin
        $this->assertDatabaseHas('notifications', [
            'notifiable_id' => $admin->id,
            'notifiable_type' => User::class,
        ]);

        $notification = \Illuminate\Notifications\DatabaseNotification::orderBy('created_at', 'desc')->first();
        $this->assertNotNull($notification);
        $this->assertEquals('Payment Failed', $notification->data['title']);
    }
}
