<?php

namespace App\Livewire\Admin;

use App\Models\Customer;
use App\Models\Product;
use App\Services\OrderService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
use Livewire\Component;

class PosCart extends Component
{
    public array $cart = [];
    public ?int $customerId = null;
    public string $customerSearch = '';
    public ?string $selectedCustomerLabel = null;
    public ?string $estimatedDoneAt = null;
    public float $discountTotal = 0;
    public float $deliveryFee = 0;
    public float $paidAmount = 0;
    public string $paymentMethod = 'cash';
    public ?string $note = null;
    public bool $showCustomerForm = false;
    public array $newCustomer = [
        'name' => '',
        'phone' => '',
        'address' => '',
    ];

    protected function rules(): array
    {
        return [
            'customerId' => ['nullable', 'exists:customers,id'],
            'estimatedDoneAt' => ['nullable', 'date'],
            'discountTotal' => ['numeric', 'min:0'],
            'deliveryFee' => ['numeric', 'min:0'],
            'paidAmount' => ['numeric', 'min:0'],
            'paymentMethod' => ['required', 'in:cash,transfer,qris'],
            'note' => ['nullable', 'string', 'max:1000'],
            'cart' => ['required', 'array', 'min:1'],
            'cart.*.product_id' => ['required', 'exists:products,id'],
            'cart.*.quantity' => ['required', 'numeric', 'min:0.001'],
            'cart.*.discount' => ['nullable', 'numeric', 'min:0'],
            'cart.*.is_reward_free_item' => ['boolean'],
            'cart.*.staff_id' => ['nullable', 'exists:users,id'],
        ];
    }

    public function addProduct(int $productId): void
    {
        $product = Product::query()
            ->with(['stocks' => fn ($query) => $query->where('branch_id', $this->branchId())])
            ->findOrFail($productId);

        $existingQuantity = (float) ($this->cart[$productId]['quantity'] ?? 0);

        $this->cart[$productId] = [
            'product_id' => $product->id,
            'name' => $product->name,
            'type' => $product->type,
            'unit' => $product->unit,
            'track_stock' => (bool) $product->track_stock,
            'stock' => (float) ($product->stocks->first()?->quantity ?? 0),
            'quantity' => $existingQuantity + 1,
            'price' => (float) $product->price,
            'discount' => (float) ($this->cart[$productId]['discount'] ?? 0),
            'is_reward_free_item' => (bool) ($this->cart[$productId]['is_reward_free_item'] ?? false),
            'staff_id' => Auth::id(),
        ];
    }

    public function removeProduct(int $productId): void
    {
        unset($this->cart[$productId]);
    }

    public function increment(int $productId): void
    {
        if (isset($this->cart[$productId])) {
            $this->cart[$productId]['quantity'] = round((float) $this->cart[$productId]['quantity'] + 1, 3);
        }
    }

    public function decrement(int $productId): void
    {
        if (! isset($this->cart[$productId])) {
            return;
        }

        $next = round((float) $this->cart[$productId]['quantity'] - 1, 3);

        if ($next <= 0) {
            $this->removeProduct($productId);
            return;
        }

        $this->cart[$productId]['quantity'] = $next;
    }

    public function toggleReward(int $productId): void
    {
        if (! isset($this->cart[$productId]) || $this->cart[$productId]['type'] !== 'service') {
            return;
        }

        $this->cart[$productId]['is_reward_free_item'] = ! (bool) $this->cart[$productId]['is_reward_free_item'];
        $this->cart[$productId]['discount'] = 0;
    }

    public function saveCustomer(): void
    {
        $data = $this->validate([
            'newCustomer.name' => ['required', 'string', 'max:120'],
            'newCustomer.phone' => ['required', 'string', 'max:30', 'unique:customers,phone'],
            'newCustomer.address' => ['nullable', 'string', 'max:1000'],
        ]);

        $customer = Customer::query()->create([
            'branch_id' => $this->branchId(),
            'name' => $data['newCustomer']['name'],
            'phone' => $data['newCustomer']['phone'],
            'address' => $data['newCustomer']['address'] ?: null,
            'pin' => Hash::make('000000'),
        ]);

        $this->customerId = $customer->id;
        $this->selectedCustomerLabel = trim($customer->name.' - '.$customer->phone);
        $this->customerSearch = '';
        $this->newCustomer = ['name' => '', 'phone' => '', 'address' => ''];
        $this->showCustomerForm = false;
        session()->flash('status', 'Pelanggan baru ditambahkan.');
    }

    public function selectCustomer(int $customerId): void
    {
        $customer = Customer::query()
            ->where(fn ($query) => $query->whereNull('branch_id')->orWhere('branch_id', $this->branchId()))
            ->findOrFail($customerId);

        $this->customerId = $customer->id;
        $this->selectedCustomerLabel = trim($customer->name.' - '.$customer->phone);
        $this->customerSearch = '';
    }

    public function clearCustomer(): void
    {
        $this->customerId = null;
        $this->selectedCustomerLabel = null;
        $this->customerSearch = '';
    }

    public function checkout(OrderService $orderService)
    {
        $validated = $this->validate();

        try {
            $order = $orderService->checkout([
                'branch_id' => $this->branchId(),
                'customer_id' => $validated['customerId'] ?? null,
                'estimated_done_at' => $validated['estimatedDoneAt'] ?? null,
                'discount_total' => $validated['discountTotal'],
                'delivery_fee' => $validated['deliveryFee'],
                'paid_amount' => $validated['paidAmount'],
                'payment_method' => $validated['paymentMethod'],
                'note' => $validated['note'] ?? null,
                'items' => array_values($validated['cart']),
            ], Auth::user());
        } catch (ValidationException $exception) {
            foreach ($exception->errors() as $field => $messages) {
                $this->addError($field, $messages[0]);
            }

            return null;
        }

        session()->flash('status', 'Transaksi berhasil dibuat.');

        return $this->redirectRoute('orders.receipt', $order, navigate: false);
    }

    public function total(): float
    {
        return collect($this->cart)->sum(fn ($item) => $this->lineTotal($item));
    }

    public function grandTotal(): float
    {
        return max(0, $this->total() - $this->discountTotal + $this->deliveryFee);
    }

    public function balanceDue(): float
    {
        return max(0, $this->grandTotal() - $this->paidAmount);
    }

    public function rewardProgress(): int
    {
        if (! $this->customerId) {
            return 0;
        }

        return min(5, app(OrderService::class)->paidInvoiceCountForReward($this->customerId));
    }

    private function lineTotal(array $item): float
    {
        if ($item['is_reward_free_item'] ?? false) {
            return 0;
        }

        return max(0, ((float) $item['quantity'] * (float) $item['price']) - (float) ($item['discount'] ?? 0));
    }

    private function branchId(): int
    {
        return (int) Auth::user()->branch_id;
    }

    public function render()
    {
        return view('livewire.admin.pos-cart', [
            'products' => Product::query()
                ->with(['stocks' => fn ($query) => $query->where('branch_id', $this->branchId())])
                ->where('is_active', true)
                ->orderBy('type')
                ->orderBy('name')
                ->get(),
            'customers' => $this->customerOptions(),
            'staff' => \App\Models\User::query()
                ->where(fn ($query) => $query->whereNull('branch_id')->orWhere('branch_id', $this->branchId()))
                ->whereIn('role', ['owner', 'admin_cabang', 'manager', 'kasir'])
                ->orderBy('name')
                ->get(),
            'rewardProgress' => $this->rewardProgress(),
        ]);
    }

    private function customerOptions()
    {
        $query = trim($this->customerSearch);

        if (mb_strlen($query) < 2) {
            return collect();
        }

        return Customer::query()
            ->where(fn ($builder) => $builder->whereNull('branch_id')->orWhere('branch_id', $this->branchId()))
            ->where(fn ($builder) => $builder
                ->where('name', 'like', "%{$query}%")
                ->orWhere('phone', 'like', "%{$query}%"))
            ->orderBy('name')
            ->limit(12)
            ->get();
    }
}
