<?php

use App\Enums\Gender;
use App\Enums\PaymentStatus;
use App\Enums\VoucherType;
use App\Livewire\Studio\Manage\Order\Create;
use App\Models\Bottle;
use App\Models\Formula;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Perfume;
use App\Models\Product;
use App\Models\User;
use App\Models\Voucher;
use Illuminate\Support\Facades\DB;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

beforeEach(function () {
    $this->setupUser();

    $role = Role::firstOrCreate(['name' => 'Owner']);
    $permissions = ['view order', 'create order'];
    foreach ($permissions as $p) {
        Permission::firstOrCreate(['name' => $p]);
    }
    $role->syncPermissions($permissions);
    $this->user->assignRole($role);

    // Ensure user has an outlet
    $this->user->outlets()->sync([$this->outlet->id]);

    $this->tier = \App\Models\Tier::factory()->create();

    // Create other roles required by the component logic
    Role::firstOrCreate(['name' => 'Developer']);
    Role::firstOrCreate(['name' => 'Leader']);
});

it('renders the order create page', function () {
    $this->actingAs($this->user)
        ->get(route('studio.manage.order.create'))
        ->assertOk()
        ->assertSeeLivewire(Create::class);
});

it('can add a product to the cart', function () {
    $product = Product::factory()->create(['sale_price' => 50000]);
    $this->outlet->products()->attach($product->id, ['stock' => 100]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->set('form.product_id', $product->id)
        ->set('form.quantity_product', '2')
        ->call('addProduct')
        ->assertHasNoErrors();

    $this->assertDatabaseHas('order_items', [
        'user_id' => $this->user->id,
        'orderable_type' => Product::class,
        'orderable_id' => $product->id,
        'quantity' => 2,
        'unit_price' => 50000,
        'order_id' => null,
    ]);
});

it('can add a new perfume (perfume + bottle) to the cart', function () {
    $perfume = Perfume::factory()->create(['sale_price' => 1000]); // per ml
    $bottle = Bottle::factory()->create(['size' => '30ml', 'sale_price' => 15000]);
    $formula = Formula::factory()->create(['size' => '30ml', 'volume' => 30, 'quality' => 'Premium']);

    $this->outlet->perfumes()->attach($perfume->id, ['stock' => 1000]);
    $this->outlet->bottles()->attach($bottle->id, ['stock' => 50]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->set('tab', 'new')
        ->set('form.perfume_id', $perfume->id)
        ->set('form.bottle_id', $bottle->id)
        ->set('form.quality_id', $formula->id)
        ->call('addPerfume')
        ->assertHasNoErrors();

    // Should add both perfume and bottle
    $this->assertDatabaseHas('order_items', [
        'user_id' => $this->user->id,
        'orderable_type' => Perfume::class,
        'orderable_id' => $perfume->id,
        'quantity' => 30,
        'quality' => 'Premium',
        'order_id' => null,
    ]);

    $this->assertDatabaseHas('order_items', [
        'user_id' => $this->user->id,
        'orderable_type' => Bottle::class,
        'orderable_id' => $bottle->id,
        'quantity' => 1,
        'order_id' => null,
    ]);
});

it('can add a new perfume with custom quality', function () {
    $perfume = Perfume::factory()->create(['sale_price' => 1000]);
    $bottle = Bottle::factory()->create(['size' => 'Custom', 'sale_price' => 5000]);

    $this->outlet->perfumes()->attach($perfume->id, ['stock' => 1000]);
    $this->outlet->bottles()->attach($bottle->id, ['stock' => 50]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->set('tab', 'new')
        ->set('form.perfume_id', $perfume->id)
        ->set('form.bottle_id', $bottle->id)
        ->set('form.quality_id', 'custom')
        ->set('form.bottle_size', '15')
        ->call('addPerfume')
        ->assertHasNoErrors();

    $this->assertDatabaseHas('order_items', [
        'user_id' => $this->user->id,
        'orderable_type' => Perfume::class,
        'orderable_id' => $perfume->id,
        'quantity' => 15,
        'quality' => 'Custom',
        'order_id' => null,
    ]);
});

it('can add a refill perfume with custom quality', function () {
    $perfume = Perfume::factory()->create(['sale_price' => 1000]);

    $this->outlet->perfumes()->attach($perfume->id, ['stock' => 1000]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->set('tab', 'refill')
        ->set('form.perfume_id', $perfume->id)
        ->set('form.quality_id', 'custom')
        ->set('form.bottle_size', '50')
        ->call('addPerfume')
        ->assertHasNoErrors();

    $this->assertDatabaseHas('order_items', [
        'user_id' => $this->user->id,
        'orderable_type' => Perfume::class,
        'orderable_id' => $perfume->id,
        'quantity' => 50,
        'quality' => 'Custom',
        'order_id' => null,
    ]);
});

it('separates items with different qualities', function () {
    $perfume = Perfume::factory()->create(['sale_price' => 1000]);
    $bottle = Bottle::factory()->create(['size' => '30ml']);
    $formula1 = Formula::factory()->create(['size' => '30ml', 'volume' => 30, 'quality' => 'Premium']);
    $formula2 = Formula::factory()->create(['size' => '30ml', 'volume' => 30, 'quality' => 'Super']);

    $this->outlet->perfumes()->attach($perfume->id, ['stock' => 1000]);
    $this->outlet->bottles()->attach($bottle->id, ['stock' => 100]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->set('tab', 'new')
        ->set('form.perfume_id', $perfume->id)
        ->set('form.bottle_id', $bottle->id)
        ->set('form.quality_id', $formula1->id)
        ->call('addPerfume')
        ->set('form.perfume_id', $perfume->id)
        ->set('form.bottle_id', $bottle->id)
        ->set('form.quality_id', $formula2->id) // Switch quality
        ->call('addPerfume');

    $this->assertDatabaseHas('order_items', [
        'orderable_id' => $perfume->id,
        'quality' => 'Premium',
        'quantity' => 30,
    ]);

    $this->assertDatabaseHas('order_items', [
        'orderable_id' => $perfume->id,
        'quality' => 'Super',
        'quantity' => 30,
    ]);

    // Bottle SHOULD aggregated to 2 (since bottle isn't a perfume and logic allows increment)
    $this->assertDatabaseHas('order_items', [
        'orderable_id' => $bottle->id,
        'quantity' => 2,
    ]);

    $this->assertDatabaseCount('order_items', 3); // 2 perfumes (diff quality) + 1 bottle (aggregated)
});

it('can update item quantity in the cart', function () {
    $product = Product::factory()->create(['sale_price' => 50000]);
    $item = OrderItem::factory()->forProduct($product)->create([
        'user_id' => $this->user->id,
        'order_id' => null,
        'quantity' => 1,
        'unit_price' => 50000,
    ]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.item_id', $item->id)
        ->set('form.quantity_edit', '5')
        ->call('updateItem')
        ->assertHasNoErrors();

    $this->assertDatabaseHas('order_items', [
        'id' => $item->id,
        'quantity' => 5,
    ]);
});

it('can delete an item from the cart', function () {
    $product = Product::factory()->create();
    $item = OrderItem::factory()->forProduct($product)->create([
        'user_id' => $this->user->id,
        'order_id' => null,
    ]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->call('deleteItem', $item->id)
        ->assertHasNoErrors();

    $this->assertSoftDeleted('order_items', ['id' => $item->id]);
});

it('can store an order and update outlet stock', function () {
    $product = Product::factory()->create(['sale_price' => 100000, 'cost_price' => 60000]);
    $this->outlet->products()->attach($product->id, ['stock' => 10]);

    OrderItem::factory()->forProduct($product)->create([
        'user_id' => $this->user->id,
        'order_id' => null,
        'quantity' => 2,
        'unit_price' => 100000,
        'cogs' => 60000,
    ]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->set('form.payment_method', '1') // Cash
        ->call('save')
        ->assertHasNoErrors()
        ->assertRedirect();

    $this->assertDatabaseHas('orders', [
        'outlet_id' => $this->outlet->id,
        'user_id' => $this->user->id,
        'total' => 200000,
        'subtotal' => 200000,
    ]);

    // Check payment created
    $order = Order::latest()->first();
    $this->assertDatabaseHas('payments', [
        'order_id' => $order->id,
        'amount' => 200000,
        'status' => PaymentStatus::PAID->value,
    ]);

    // Check stock updated
    $this->assertDatabaseHas('outlet_product', [
        'outlet_id' => $this->outlet->id,
        'product_id' => $product->id,
        'stock' => 8,
    ]);
});

it('can store an order with member and earn points', function () {
    $product = Product::factory()->create(['sale_price' => 100000]);
    $this->outlet->products()->attach($product->id, ['stock' => 10]);

    $memberUser = User::factory()->create();
    $memberUser->customer()->create([
        'name' => 'Member Test',
        'gender' => Gender::MALE->value,
    ]);
    $memberUser->membership()->create([
        'total_spending' => 0,
        'reward_points' => 0,
        'tier_id' => $this->tier->id,
    ]);

    OrderItem::factory()->forProduct($product)->create([
        'user_id' => $this->user->id,
        'order_id' => null,
        'quantity' => 1,
        'unit_price' => 100000,
    ]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->set('form.member_id', $memberUser->customer->id)
        ->call('save')
        ->assertHasNoErrors();

    // Check points earned (100000 / 5000 = 20 points)
    $this->assertDatabaseHas('memberships', [
        'user_id' => $memberUser->id,
        'reward_points' => 20,
        'total_spending' => 100000,
    ]);

    $this->assertDatabaseHas('point_records', [
        'user_id' => $memberUser->id,
        'change' => 20,
        'is_addition' => true,
    ]);
});

it('can store an order with voucher', function () {
    $product = Product::factory()->create(['sale_price' => 100000]);
    $this->outlet->products()->attach($product->id, ['stock' => 10]);

    $memberUser = User::factory()->create();
    $memberUser->customer()->create([
        'name' => 'Voucher User',
        'gender' => Gender::MALE->value,
    ]);
    $memberUser->membership()->create(['tier_id' => $this->tier->id]);

    $voucher = Voucher::factory()->create([
        'type' => VoucherType::FIXED->value,
        'discount_amount' => 20000,
    ]);

    // Assign voucher to user
    DB::table('user_voucher')->insert([
        'user_id' => $memberUser->id,
        'voucher_id' => $voucher->id,
        'quantity' => 1,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    OrderItem::factory()->forProduct($product)->create([
        'user_id' => $this->user->id,
        'order_id' => null,
        'quantity' => 1,
        'unit_price' => 100000,
    ]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->set('form.member_id', $memberUser->customer->id)
        ->set('form.voucher_id', $voucher->id)
        ->call('save')
        ->assertHasNoErrors();

    $this->assertDatabaseHas('orders', [
        'voucher_id' => $voucher->id,
        'total' => 80000,
        'discount' => 20000,
    ]);

    // Check voucher used up
    $this->assertDatabaseMissing('user_voucher', [
        'user_id' => $memberUser->id,
        'voucher_id' => $voucher->id,
    ]);
});

it('cannot create order with empty cart', function () {
    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', $this->outlet->id)
        ->call('save')
        ->assertHasNoErrors(); // Shows toast

    $this->assertDatabaseCount('orders', 0);
});

it('validates required fields', function () {
    // Add something to cart first to pass empty cart check
    OrderItem::factory()->forProduct()->create(['user_id' => $this->user->id, 'order_id' => null]);

    Livewire::actingAs($this->user)
        ->test(Create::class)
        ->set('form.outlet_id', '')
        ->call('save')
        ->assertHasErrors(['form.outlet_id' => 'required']);
});

it('cannot access order create without permission', function () {
    $this->user->roles()->detach();
    $this->user->permissions()->detach();

    $this->actingAs($this->user)
        ->get(route('studio.manage.order.create'))
        ->assertForbidden();
});
