<?php

namespace App\Services;

use App\Models\BonusDisbursement;
use App\Models\BonusDisbursementItem;
use App\Models\OperationalExpense;
use App\Models\OrderDetail;
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class BonusService
{
    public function calculateDetailBonus(OrderDetail $detail): float
    {
        if ($detail->is_reward_free_item) {
            return 0;
        }

        $product = $detail->product;
        $bonusBase = $this->bonusBaseAmount($detail);

        return match ($product?->bonus_type) {
            'percent' => round($bonusBase * ((float) $product->bonus_value / 100), 2),
            'nominal' => round(((float) $product->bonus_value) * ((float) $detail->quantity), 2),
            default => 0,
        };
    }

    public function payableDetails(?int $branchId, string $startDate, string $endDate, ?int $staffId = null): Collection
    {
        return OrderDetail::query()
            ->with(['order', 'staff', 'product'])
            ->where('bonus_amount', '>', 0)
            ->whereNull('bonus_paid_at')
            ->when($staffId, fn ($q) => $q->where('staff_id', $staffId))
            ->whereHas('order', function ($query) use ($branchId, $startDate, $endDate) {
                $query->where('payment_status', 'lunas')
                    ->whereBetween('created_at', [$startDate.' 00:00:00', $endDate.' 23:59:59'])
                    ->when($branchId, fn ($q) => $q->where('branch_id', $branchId));
            })
            ->where('is_reward_free_item', false)
            ->get();
    }

    public function summaryByStaff(?int $branchId, string $startDate, string $endDate): Collection
    {
        return $this->payableDetails($branchId, $startDate, $endDate)
            ->groupBy('staff_id')
            ->map(function (Collection $details) {
                $staff = $details->first()->staff;

                return [
                    'staff_id' => $staff?->id,
                    'staff_name' => $staff?->name ?? 'Tanpa staff',
                    'items' => $details->count(),
                    'orders' => $details->pluck('order_id')->unique()->count(),
                    'bonus_total' => (float) $details->sum('bonus_amount'),
                ];
            })
            ->sortByDesc('bonus_total')
            ->values();
    }

    public function disburse(User $owner, int $branchId, string $startDate, string $endDate, ?int $staffId = null, ?string $note = null): BonusDisbursement
    {
        return DB::transaction(function () use ($owner, $branchId, $startDate, $endDate, $staffId, $note): BonusDisbursement {
            $details = $this->payableDetails($branchId, $startDate, $endDate, $staffId);
            $total = (float) $details->sum('bonus_amount');

            $expense = OperationalExpense::query()->create([
                'branch_id' => $branchId,
                'user_id' => $owner->id,
                'expense_date' => now()->toDateString(),
                'name' => "Pencairan bonus {$startDate} - {$endDate}",
                'amount' => $total,
                'type' => 'bonus_payout',
                'description' => 'Auto-generated dari pencairan bonus karyawan.',
            ]);

            $disbursement = BonusDisbursement::query()->create([
                'branch_id' => $branchId,
                'staff_id' => $staffId,
                'disbursed_by' => $owner->id,
                'operational_expense_id' => $expense->id,
                'disbursement_no' => $this->nextDisbursementNo($branchId),
                'period_start' => $startDate,
                'period_end' => $endDate,
                'disbursed_at' => now()->toDateString(),
                'total_amount' => $total,
                'note' => $note,
            ]);

            foreach ($details as $detail) {
                BonusDisbursementItem::query()->create([
                    'bonus_disbursement_id' => $disbursement->id,
                    'order_detail_id' => $detail->id,
                    'bonus_amount' => $detail->bonus_amount,
                ]);
            }

            OrderDetail::query()
                ->whereIn('id', $details->pluck('id'))
                ->update(['bonus_paid_at' => now()]);

            return $disbursement->load(['items.orderDetail.order', 'items.orderDetail.staff', 'staff', 'disburser', 'branch', 'expense']);
        });
    }

    public function personalSummary(int $staffId, ?int $branchId = null): array
    {
        $start = now()->startOfMonth()->toDateString();
        $end = now()->toDateString();
        $details = $this->payableDetails($branchId, $start, $end, $staffId);

        return [
            'period_start' => $start,
            'period_end' => $end,
            'items' => $details->count(),
            'orders' => $details->pluck('order_id')->unique()->count(),
            'bonus_total' => (float) $details->sum('bonus_amount'),
        ];
    }

    private function bonusBaseAmount(OrderDetail $detail): float
    {
        $product = $detail->product;

        if ($detail->product_type === 'retail' && ($product?->bonus_base ?? 'margin') === 'margin') {
            return max(0, (float) $detail->line_total - ((float) $product?->cost_price * (float) $detail->quantity));
        }

        return (float) $detail->line_total;
    }

    private function nextDisbursementNo(int $branchId): string
    {
        return 'BON-'.now()->format('Ymd').'-'.$branchId.'-'.Str::upper(Str::random(5));
    }
}
