<?php

namespace Tests\Feature;

use App\Models\Branch;
use App\Models\Category;
use App\Models\Customer;
use App\Models\Order;
use App\Models\Product;
use App\Models\ProductStock;
use App\Models\User;
use App\Services\CustomerPortalService;
use App\Services\OrderService;
use App\Services\PaymentService;
use App\Services\ProgressService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
use Tests\TestCase;

class LaundryBusinessFlowTest extends TestCase
{
    use RefreshDatabase;

    public function test_checkout_calculates_server_total_and_decreases_retail_stock(): void
    {
        [$branch, $cashier, $customer, $service, $retail] = $this->fixtures();

        $order = app(OrderService::class)->checkout([
            'branch_id' => $branch->id,
            'customer_id' => $customer->id,
            'items' => [
                ['product_id' => $service->id, 'quantity' => 2.5, 'staff_id' => $cashier->id],
                ['product_id' => $retail->id, 'quantity' => 2, 'staff_id' => $cashier->id],
            ],
            'discount_total' => 1000,
            'delivery_fee' => 5000,
            'paid_amount' => 10000,
            'payment_method' => 'cash',
        ], $cashier);

        $this->assertSame(74000.0, (float) $order->grand_total);
        $this->assertSame('dp', $order->payment_status);
        $this->assertSame(8.0, (float) ProductStock::query()->where('product_id', $retail->id)->first()->quantity);
        $this->assertDatabaseHas('order_progress_logs', ['order_id' => $order->id, 'progress_status' => 'order_diterima']);
    }

    public function test_order_cannot_be_taken_before_paid(): void
    {
        [, $cashier, $customer, $service] = $this->fixtures();
        $order = app(OrderService::class)->checkout([
            'customer_id' => $customer->id,
            'items' => [['product_id' => $service->id, 'quantity' => 1]],
        ], $cashier);

        $this->expectException(ValidationException::class);
        app(ProgressService::class)->updateCoreStatus($order, 'diambil', $cashier);
    }

    public function test_paid_order_can_move_through_status_flow(): void
    {
        [, $cashier, $customer, $service] = $this->fixtures();
        $order = app(OrderService::class)->checkout([
            'customer_id' => $customer->id,
            'items' => [['product_id' => $service->id, 'quantity' => 1]],
            'paid_amount' => 8000,
        ], $cashier);

        app(ProgressService::class)->updateCoreStatus($order, 'proses', $cashier);
        app(ProgressService::class)->updateCoreStatus($order->refresh(), 'selesai', $cashier);
        app(ProgressService::class)->updateCoreStatus($order->refresh(), 'diambil', $cashier);

        $this->assertSame('diambil', $order->refresh()->status);
    }

    public function test_reward_free_item_gets_no_staff_bonus(): void
    {
        [$branch, $cashier, $customer, $service] = $this->fixtures();

        Order::factoryLikePaidInvoices($branch->id, $customer->id, 5);

        $order = app(OrderService::class)->checkout([
            'branch_id' => $branch->id,
            'customer_id' => $customer->id,
            'items' => [['product_id' => $service->id, 'quantity' => 1, 'is_reward_free_item' => true]],
        ], $cashier);

        $detail = $order->details()->first();
        $this->assertTrue($detail->is_reward_free_item);
        $this->assertSame(0.0, (float) $detail->bonus_amount);
    }

    public function test_customer_portal_uses_phone_and_pin(): void
    {
        [, , $customer] = $this->fixtures();

        $found = app(CustomerPortalService::class)->authenticate($customer->phone, '000000');

        $this->assertTrue($found->is($customer));
    }

    public function test_pos_page_and_receipt_render_for_cashier(): void
    {
        [, $cashier, $customer, $service] = $this->fixtures();
        $order = app(OrderService::class)->checkout([
            'customer_id' => $customer->id,
            'items' => [['product_id' => $service->id, 'quantity' => 1]],
            'paid_amount' => 8000,
        ], $cashier);

        $this->actingAs($cashier)->get(route('pos.create'))->assertOk()->assertSee('POS Kasir');
        $this->actingAs($cashier)->get(route('orders.receipt', $order))->assertOk()->assertSee($order->invoice_no);
    }

    public function test_order_detail_supports_later_payment_and_item_progress(): void
    {
        [, $cashier, $customer, $service] = $this->fixtures();
        $order = app(OrderService::class)->checkout([
            'customer_id' => $customer->id,
            'items' => [['product_id' => $service->id, 'quantity' => 2]],
            'paid_amount' => 5000,
        ], $cashier);
        $detail = $order->details()->first();

        $this->actingAs($cashier)->post(route('orders.payments.store', $order), [
            'amount' => 11000,
            'method' => 'qris',
            'reference' => 'QR-001',
            'note' => 'Pelunasan',
        ])->assertRedirect();

        $this->assertSame('lunas', $order->refresh()->payment_status);
        $this->assertSame(0.0, (float) $order->balance_due);

        $this->actingAs($cashier)->post(route('orders.details.progress', [$order, $detail]), [
            'progress_status' => 'sedang_finishing',
            'note' => 'Setrika dan lipat',
        ])->assertRedirect();

        $this->assertSame('sedang_finishing', $detail->refresh()->progress_status);
        $this->assertDatabaseHas('order_progress_logs', [
            'order_id' => $order->id,
            'progress_status' => 'sedang_finishing',
        ]);
    }

    private function fixtures(): array
    {
        $branch = Branch::query()->create(['name' => 'Cabang Test', 'code' => 'TST']);
        $cashier = User::query()->create([
            'branch_id' => $branch->id,
            'name' => 'Kasir',
            'email' => 'kasir@example.test',
            'role' => 'kasir',
            'password' => Hash::make('password'),
        ]);
        $customer = Customer::query()->create([
            'branch_id' => $branch->id,
            'name' => 'Customer',
            'phone' => '6281000000000',
            'pin' => Hash::make('000000'),
        ]);
        $serviceCategory = Category::query()->create(['name' => 'Service', 'type' => 'service']);
        $retailCategory = Category::query()->create(['name' => 'Retail', 'type' => 'retail']);
        $service = Product::query()->create([
            'category_id' => $serviceCategory->id,
            'name' => 'Cuci Kering Lipat',
            'sku' => 'SVC',
            'type' => 'service',
            'unit' => 'kg',
            'price' => 8000,
            'bonus_type' => 'percent',
            'bonus_value' => 10,
        ]);
        $retail = Product::query()->create([
            'category_id' => $retailCategory->id,
            'name' => 'Parfum',
            'sku' => 'RTL',
            'type' => 'retail',
            'unit' => 'pcs',
            'price' => 25000,
            'track_stock' => true,
            'minimum_stock' => 2,
        ]);
        ProductStock::query()->create(['branch_id' => $branch->id, 'product_id' => $retail->id, 'quantity' => 10]);

        return [$branch, $cashier, $customer, $service, $retail];
    }
}
