<?php

namespace Tests\Feature;

use App\Models\User;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\ReturnRequest;
use App\Models\ReturnRequestItem;
use App\Models\ExchangeRequestItem;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use Livewire\Livewire;
use App\Filament\Resources\Orders\Pages\EditOrder;

class OrderExchangeActionTest extends TestCase
{
    use RefreshDatabase;

    private User $admin;
    private Order $order;
    private Product $returnedProduct;
    private Product $newProduct;

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

        \App\Observers\OrderItemObserver::$enableInConsole = true;

        $this->admin = User::create([
            'name' => 'Admin User',
            'email' => 'admin@sawdabazar.com',
            'password' => bcrypt('password123'),
            'role' => 'admin',
        ]);

        $category = \App\Models\Category::create([
            'name' => 'General',
            'slug' => 'general',
            'is_visible' => true,
        ]);

        $this->returnedProduct = Product::create([
            'name' => 'Old Product',
            'slug' => 'old-product',
            'sku' => 'OLD-SKU',
            'price' => 1000.00,
            'compare_at_price' => 1200.00,
            'cost_price' => 700.00,
            'quantity' => 10,
            'is_visible' => true,
            'published_at' => now(),
            'category_id' => $category->id,
        ]);

        $this->newProduct = Product::create([
            'name' => 'New Product',
            'slug' => 'new-product',
            'sku' => 'NEW-SKU',
            'price' => 1200.00,
            'compare_at_price' => 1500.00,
            'cost_price' => 800.00,
            'quantity' => 20,
            'is_visible' => true,
            'published_at' => now(),
            'category_id' => $category->id,
        ]);

        // Create delivered order to be exchanged
        $this->order = Order::create([
            'user_id' => $this->admin->id,
            'customer_name' => 'John Customer',
            'customer_email' => 'john@customer.com',
            'customer_phone' => '01711122233',
            'shipping_address' => 'Mirpur, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'payment_method' => 'cod',
            'payment_status' => 'paid',
            'status' => 'delivered',
        ]);

        $this->order->items()->create([
            'product_id' => $this->returnedProduct->id,
            'product_name' => $this->returnedProduct->name,
            'product_sku' => $this->returnedProduct->sku,
            'price' => 1000.00,
            'quantity' => 1,
        ]);
    }

    /**
     * Test that an admin can trigger the create_exchange action.
     */
    public function test_admin_can_create_exchange_order(): void
    {
        Livewire::actingAs($this->admin);
        \Filament\Facades\Filament::setCurrentPanel(\Filament\Facades\Filament::getPanel('admin'));

        $orderItem = $this->order->items->first();

        Livewire::test(EditOrder::class, [
            'record' => $this->order->id,
        ])
            ->callAction('create_exchange', data: [
                'returned_items' => [
                    [
                        'order_item_id' => $orderItem->id,
                        'product_name' => $orderItem->product_name,
                        'price' => $orderItem->price,
                        'ordered_qty' => $orderItem->quantity,
                        'return_qty' => 1,
                    ]
                ],
                'exchange_items' => [
                    [
                        'product_id' => $this->newProduct->id,
                        'product_variant_id' => null,
                        'quantity' => 1,
                    ]
                ],
                'exchange_shipping_cost' => 80.00,
                'exchange_discount' => 50.00,
                'reason' => 'size_issue',
                'reason_details' => 'Color was wrong',
            ])
            ->assertHasNoActionErrors();

        // 1. Verify ReturnRequest got created and approved
        $returnRequest = ReturnRequest::where('order_id', $this->order->id)->first();
        $this->assertNotNull($returnRequest);
        $this->assertEquals('approved', $returnRequest->status);
        $this->assertEquals('return', $returnRequest->type);
        $this->assertEquals('size_issue', $returnRequest->reason);
        $this->assertStringContainsString('Color was wrong', $returnRequest->customer_notes);

        // Verify return items
        $returnItem = ReturnRequestItem::where('return_request_id', $returnRequest->id)->first();
        $this->assertNotNull($returnItem);
        $this->assertEquals($orderItem->id, $returnItem->order_item_id);
        $this->assertEquals(1, $returnItem->quantity);

        // Verify exchange items logged
        $exchangeLogItem = ExchangeRequestItem::where('return_request_id', $returnRequest->id)->first();
        $this->assertNotNull($exchangeLogItem);
        $this->assertEquals($this->newProduct->id, $exchangeLogItem->product_id);
        $this->assertEquals(1, $exchangeLogItem->quantity);

        // 2. Verify Exchange Order was created
        $exchangeOrder = Order::where('parent_order_id', $this->order->id)->first();
        $this->assertNotNull($exchangeOrder);
        $this->assertTrue($exchangeOrder->is_exchange);
        $this->assertEquals('pending', $exchangeOrder->status);
        $this->assertEquals('cod', $exchangeOrder->payment_method);
        $this->assertEquals('pending', $exchangeOrder->payment_status);

        // Verify exchange order items
        $exchangeOrderItem = $exchangeOrder->items->first();
        $this->assertNotNull($exchangeOrderItem);
        $this->assertEquals($this->newProduct->id, $exchangeOrderItem->product_id);
        $this->assertEquals(1, $exchangeOrderItem->quantity);
        $this->assertEquals(1200.00, $exchangeOrderItem->price);

        // Verify financial calculations:
        // Exchange subtotal = 1200
        // Returned discount = 1000 + 50 (additional discount) = 1050
        // Shipping = 80
        // Grand total = (1200 - 1050) + 80 = 230
        $this->assertEquals(1200.00, $exchangeOrder->subtotal);
        $this->assertEquals(1050.00, $exchangeOrder->discount);
        $this->assertEquals(80.00, $exchangeOrder->shipping_cost);
        $this->assertEquals(230.00, $exchangeOrder->grand_total);

        // Verify inventory updates
        // Returned product stock is NOT restocked yet since status is 'approved'
        $this->assertEquals(9, $this->returnedProduct->fresh()->quantity);
        // Exchanged product stock: 20 - 1 (sent) = 19
        $this->assertEquals(19, $this->newProduct->fresh()->quantity);
    }

    /**
     * Test that variant options are successfully appended to the product name inside the modal.
     */
    public function test_exchange_modal_populates_returned_item_variant_options(): void
    {
        $variant = \App\Models\ProductVariant::create([
            'product_id' => $this->returnedProduct->id,
            'price' => 1000.00,
            'quantity' => 10,
            'sku' => 'OLD-SKU-VAR',
            'options' => ['Size' => 'M', 'Color' => 'Red'],
        ]);

        $orderWithVariant = Order::create([
            'user_id' => $this->admin->id,
            'customer_name' => 'John Customer',
            'customer_phone' => '01711122233',
            'shipping_address' => 'Mirpur, Dhaka',
            'subtotal' => 1000.00,
            'shipping_cost' => 80.00,
            'grand_total' => 1080.00,
            'status' => 'delivered',
        ]);

        $itemWithVariant = $orderWithVariant->items()->create([
            'product_id' => $this->returnedProduct->id,
            'product_variant_id' => $variant->id,
            'product_name' => $this->returnedProduct->name,
            'product_sku' => $variant->sku,
            'price' => 1000.00,
            'quantity' => 1,
        ]);

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

        $res = Livewire::test(EditOrder::class, [
            'record' => $orderWithVariant->id,
        ])
            ->mountAction('create_exchange');

        $mountedActions = $res->instance()->mountedActions;
        $returnedItems = $mountedActions[0]['data']['returned_items'] ?? [];
        $firstItem = collect($returnedItems)->first();

        $this->assertNotNull($firstItem);
        $this->assertEquals('Old Product', $firstItem['product_name']);
        $this->assertEquals('Size: M, Color: Red', $firstItem['variant_details']);
    }

    /**
     * Test that the exchange modal summary placeholder dynamically calculates financial metrics correctly.
     */
    public function test_exchange_modal_calculates_summary_accurately(): void
    {
        Livewire::actingAs($this->admin);
        \Filament\Facades\Filament::setCurrentPanel(\Filament\Facades\Filament::getPanel('admin'));

        $orderItem = $this->order->items->first();

        $res = Livewire::test(EditOrder::class, [
            'record' => $this->order->id,
        ])
            ->mountAction('create_exchange')
            ->setActionData([
                'returned_items' => [
                    [
                        'order_item_id' => $orderItem->id,
                        'return_qty' => 1,
                    ]
                ],
                'exchange_items' => [
                    [
                        'product_id' => $this->newProduct->id,
                        'product_variant_id' => null,
                        'quantity' => 1,
                    ]
                ],
                'exchange_shipping_cost' => 80.00,
                'exchange_discount' => 50.00,
            ]);

        $component = $res->instance();
        $reflector = new \ReflectionClass($component);
        $method = $reflector->getMethod('getMountedActionSchema');
        $method->setAccessible(true);
        
        /** @var \Filament\Schemas\Schema $schema */
        $schema = $method->invoke($component);
        $components = $schema->getComponents();

        $summaryPlaceholder = collect($components)->first(function ($comp) {
            return $comp instanceof \Filament\Forms\Components\Placeholder && $comp->getName() === 'summary';
        });

        $this->assertNotNull($summaryPlaceholder);

        // Evaluate the content of the placeholder directly
        $html = $summaryPlaceholder->getContent();

        $this->assertStringContainsString('Total Returned Value (Credit):', (string) $html);
        $this->assertStringContainsString('৳1,000.00', (string) $html);
        $this->assertStringContainsString('Total Exchanged Value (New Items):', (string) $html);
        $this->assertStringContainsString('৳1,200.00', (string) $html);
        $this->assertStringContainsString('Exchange Delivery Charge:', (string) $html);
        $this->assertStringContainsString('৳80.00', (string) $html);
        $this->assertStringContainsString('Additional Discount:', (string) $html);
        $this->assertStringContainsString('৳50.00', (string) $html);
        $this->assertStringContainsString('Total COD to Collect:', (string) $html);
        $this->assertStringContainsString('৳230.00', (string) $html);
    }

    /**
     * Test that the exchange modal summary placeholder calculates and displays Refundable Amount.
     */
    public function test_exchange_modal_calculates_refundable_amount_accurately(): void
    {
        // 1. Create a cheaper product (600 BDT)
        $cheaperProduct = Product::create([
            'name' => 'Cheaper Product',
            'slug' => 'cheaper-product',
            'sku' => 'CHEAP-SKU-2',
            'price' => 600.00,
            'quantity' => 20,
            'is_visible' => true,
            'published_at' => now(),
            'category_id' => $this->returnedProduct->category_id,
        ]);

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

        $orderItem = $this->order->items->first();

        $res = Livewire::test(EditOrder::class, [
            'record' => $this->order->id,
        ])
            ->mountAction('create_exchange')
            ->setActionData([
                'returned_items' => [
                    [
                        'order_item_id' => $orderItem->id,
                        'return_qty' => 1,
                    ]
                ],
                'exchange_items' => [
                    [
                        'product_id' => $cheaperProduct->id,
                        'product_variant_id' => null,
                        'quantity' => 1,
                    ]
                ],
                'exchange_shipping_cost' => 80.00,
                'exchange_discount' => 50.00,
            ]);

        $component = $res->instance();
        $reflector = new \ReflectionClass($component);
        $method = $reflector->getMethod('getMountedActionSchema');
        $method->setAccessible(true);
        
        /** @var \Filament\Schemas\Schema $schema */
        $schema = $method->invoke($component);
        $components = $schema->getComponents();

        $summaryPlaceholder = collect($components)->first(function ($comp) {
            return $comp instanceof \Filament\Forms\Components\Placeholder && $comp->getName() === 'summary';
        });

        $this->assertNotNull($summaryPlaceholder);

        // Evaluate the content of the placeholder directly
        $html = $summaryPlaceholder->getContent();

        $this->assertStringContainsString('Total Returned Value (Credit):', (string) $html);
        $this->assertStringContainsString('৳1,000.00', (string) $html);
        $this->assertStringContainsString('Total Exchanged Value (New Items):', (string) $html);
        $this->assertStringContainsString('৳600.00', (string) $html);
        $this->assertStringContainsString('Exchange Delivery Charge:', (string) $html);
        $this->assertStringContainsString('৳80.00', (string) $html);
        $this->assertStringContainsString('Additional Discount:', (string) $html);
        $this->assertStringContainsString('৳50.00', (string) $html);
        // Under Option B:
        // discount = returnedValue (1000) + additional (50) = 1050
        // exchange subtotal (600) + shipping (80) = 680
        // refund = 1050 - 680 = 370
        // cod = 0
        $this->assertStringContainsString('Refundable Amount:', (string) $html);
        $this->assertStringContainsString('৳370.00', (string) $html);
        $this->assertStringContainsString('Total COD to Collect:', (string) $html);
        $this->assertStringContainsString('৳0.00', (string) $html);
    }

    public function test_exchange_action_falls_back_to_product_price_when_variant_price_is_null(): void
    {
        // 1. Create a variant with price = null
        $variant = \App\Models\ProductVariant::create([
            'product_id' => $this->newProduct->id,
            'price' => null,
            'quantity' => 10,
            'sku' => 'NEW-SKU-VAR-NULLPRICE',
            'options' => ['Size' => 'L', 'Color' => 'Black'],
        ]);

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

        $orderItem = $this->order->items->first();

        // 2. Perform exchange using variant with null price
        Livewire::test(EditOrder::class, [
            'record' => $this->order->id,
        ])
            ->callAction('create_exchange', data: [
                'returned_items' => [
                    [
                        'order_item_id' => $orderItem->id,
                        'product_name' => $orderItem->product_name,
                        'price' => $orderItem->price,
                        'ordered_qty' => $orderItem->quantity,
                        'return_qty' => 1,
                    ]
                ],
                'exchange_items' => [
                    [
                        'product_id' => $this->newProduct->id,
                        'product_variant_id' => $variant->id,
                        'quantity' => 1,
                    ]
                ],
                'exchange_shipping_cost' => 80.00,
                'exchange_discount' => 50.00,
                'reason' => 'size_issue',
            ])
            ->assertHasNoActionErrors();

        // 3. Verify Exchange Order was created with the fallback price (from newProduct = 1200)
        $exchangeOrder = Order::where('parent_order_id', $this->order->id)->first();
        $this->assertNotNull($exchangeOrder);
        $this->assertTrue($exchangeOrder->is_exchange);

        // Verify exchange order item price falls back to parent product price (1200)
        $exchangeOrderItem = $exchangeOrder->items->first();
        $this->assertNotNull($exchangeOrderItem);
        $this->assertEquals(1200.00, $exchangeOrderItem->price);
        $this->assertEquals(1200.00, $exchangeOrder->subtotal);
    }

    public function test_exchange_order_generates_correct_refund_amount_when_new_product_is_cheaper(): void
    {
        // 1. Create a cheaper product (600 BDT)
        $cheaperProduct = Product::create([
            'name' => 'Cheaper Product',
            'slug' => 'cheaper-product',
            'sku' => 'CHEAP-SKU',
            'price' => 600.00,
            'quantity' => 20,
            'is_visible' => true,
            'published_at' => now(),
            'category_id' => $this->returnedProduct->category_id,
        ]);

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

        $orderItem = $this->order->items->first();

        // 2. Perform exchange with cheaper product
        Livewire::test(EditOrder::class, [
            'record' => $this->order->id,
        ])
            ->callAction('create_exchange', data: [
                'returned_items' => [
                    [
                        'order_item_id' => $orderItem->id,
                        'product_name' => $orderItem->product_name,
                        'price' => $orderItem->price, // 1000.00
                        'ordered_qty' => $orderItem->quantity,
                        'return_qty' => 1,
                    ]
                ],
                'exchange_items' => [
                    [
                        'product_id' => $cheaperProduct->id,
                        'product_variant_id' => null,
                        'quantity' => 1,
                    ]
                ],
                'exchange_shipping_cost' => 80.00,
                'exchange_discount' => 50.00, // additional discount
                'reason' => 'size_issue',
            ])
            ->assertHasNoActionErrors();

        // 3. Verify ReturnRequest has the correct refund_amount and 'approved' status
        $returnRequest = ReturnRequest::where('order_id', $this->order->id)->first();
        $this->assertNotNull($returnRequest);
        // returned_value (1000) + additional (50) = 1050 discount
        // subtotal (600) + shipping (80) = 680 total cost
        // refund_amount = 1050 - 680 = 370
        $this->assertEquals(370.00, $returnRequest->refund_amount);
        $this->assertEquals('approved', $returnRequest->status);

        // 4. Verify Exchange Order has grand_total = 0.00 (Option B)
        $exchangeOrder = Order::where('parent_order_id', $this->order->id)->first();
        $this->assertNotNull($exchangeOrder);
        $this->assertEquals(0.00, $exchangeOrder->grand_total);
        $this->assertEquals(600.00, $exchangeOrder->subtotal);
        $this->assertEquals(1050.00, $exchangeOrder->discount);
    }

    /**
     * Test that returned items are not restocked until the request transitions to processing.
     */
    public function test_exchange_returns_are_only_restocked_on_reception(): void
    {
        Livewire::actingAs($this->admin);
        \Filament\Facades\Filament::setCurrentPanel(\Filament\Facades\Filament::getPanel('admin'));

        $orderItem = $this->order->items->first();

        // 1. Create exchange order
        Livewire::test(EditOrder::class, [
            'record' => $this->order->id,
        ])
            ->callAction('create_exchange', data: [
                'returned_items' => [
                    [
                        'order_item_id' => $orderItem->id,
                        'product_name' => $orderItem->product_name,
                        'price' => $orderItem->price,
                        'ordered_qty' => $orderItem->quantity,
                        'return_qty' => 1,
                    ]
                ],
                'exchange_items' => [
                    [
                        'product_id' => $this->newProduct->id,
                        'product_variant_id' => null,
                        'quantity' => 1,
                    ]
                ],
                'exchange_shipping_cost' => 80.00,
                'exchange_discount' => 0.00,
                'reason' => 'size_issue',
            ])
            ->assertHasNoActionErrors();

        // 2. Verify ReturnRequest starts as 'approved'
        $returnRequest = ReturnRequest::where('order_id', $this->order->id)->first();
        $this->assertNotNull($returnRequest);
        $this->assertEquals('approved', $returnRequest->status);

        // 3. Verify stock is NOT restocked yet (remains 9)
        $this->assertEquals(9, $this->returnedProduct->fresh()->quantity);

        // 4. Transition status to 'processing' (simulate delivery man bringing product back)
        $returnRequest->update(['status' => 'processing']);

        // 5. Verify stock IS now restocked (+1 to 10)
        $this->assertEquals(10, $this->returnedProduct->fresh()->quantity);
    }

    /**
     * Test that completing an exchange return request defaults to the correct Option B refund amount.
     */
    public function test_exchange_return_request_completes_with_correct_option_b_refund(): void
    {
        // 1. Create a cheaper product (600 BDT)
        $cheaperProduct = Product::create([
            'name' => 'Cheaper Product',
            'slug' => 'cheaper-product',
            'sku' => 'CHEAP-SKU-3',
            'price' => 600.00,
            'quantity' => 20,
            'is_visible' => true,
            'published_at' => now(),
            'category_id' => $this->returnedProduct->category_id,
        ]);

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

        $orderItem = $this->order->items->first();

        // 2. Perform exchange
        Livewire::test(EditOrder::class, [
            'record' => $this->order->id,
        ])
            ->callAction('create_exchange', data: [
                'returned_items' => [
                    [
                        'order_item_id' => $orderItem->id,
                        'product_name' => $orderItem->product_name,
                        'price' => $orderItem->price,
                        'ordered_qty' => $orderItem->quantity,
                        'return_qty' => 1,
                    ]
                ],
                'exchange_items' => [
                    [
                        'product_id' => $cheaperProduct->id,
                        'product_variant_id' => null,
                        'quantity' => 1,
                    ]
                ],
                'exchange_shipping_cost' => 80.00,
                'exchange_discount' => 50.00,
                'reason' => 'size_issue',
            ])
            ->assertHasNoActionErrors();

        $returnRequest = ReturnRequest::where('order_id', $this->order->id)->first();
        $this->assertNotNull($returnRequest);
        // returned_value (1000) + additional (50) = 1050 discount
        // subtotal (600) + shipping (80) = 680 cost
        // refund_amount = 1050 - 680 = 370
        $this->assertEquals(370.00, $returnRequest->refund_amount);

        // 3. Mark return request status as processing (so complete action is visible)
        $returnRequest->update(['status' => 'processing']);

        $exchangeOrder = Order::where('parent_order_id', $this->order->id)->first();
        $this->assertNotNull($exchangeOrder);

        // 4. Hit Complete action in EditReturnRequest and verify the default refund amount
        $res = Livewire::test(\App\Filament\Resources\ReturnRequestResource\Pages\EditReturnRequest::class, [
            'record' => $returnRequest->id,
        ])
            ->assertFormFieldDisabled('type')
            ->assertSee($this->order->order_number)
            ->assertSee($exchangeOrder->order_number)
            ->mountAction('complete');

        $mountedActions = $res->instance()->mountedActions;
        $defaultRefund = $mountedActions[0]['data']['refund_amount'] ?? null;
        $this->assertEquals(370.00, (float) $defaultRefund); // Verify it defaults to Option B refund, NOT 1000 (old product price)!

        // Call the action
        $res->callMountedAction([
            'refund_amount' => 370.00,
            'refund_method' => 'cash',
        ])
            ->assertHasNoActionErrors();

        // 5. Verify the return request is completed with the correct refund amount
        $this->assertEquals('completed', $returnRequest->fresh()->status);
        $this->assertEquals(370.00, $returnRequest->fresh()->refund_amount);

        // 6. Verify the parent order status transitions to returned but payment status stays paid
        $this->assertEquals('returned', $this->order->fresh()->status);
        $this->assertEquals('paid', $this->order->fresh()->payment_status);
    }
}
