<?php

namespace App\Services;

use App\Models\User;
use App\Models\Order;
use App\Models\Metal;
use App\Models\Product;
use App\Models\SystemSetting;
use Illuminate\Support\Collection;

/**
 * سرویس مانده فلزی مشتری.
 *
 * قرارداد بدهکار/طلبکار (هماهنگ با MoneyBalanceService):
 * - بدهکار فلز = مشتری به ما بدهکار فلز است (سیستم طلبکار فلز است؛ خرید از مشتری).
 * - طلبکار فلز = مشتری از ما طلبکار فلز است (سیستم بدهکار فلز است؛ فروش به مشتری).
 * - debt_grams / total_debit = گرمی که مشتری به ما بدهکار است.
 * - credit_grams / total_credit = گرمی که مشتری از ما طلبکار است.
 */
class MetalBalanceService
{
    /**
     * Calculate customer metal balance from orders minus metal transactions (تحویل/دریافت).
     * گروه‌بندی بر اساس product_id (محصول‌محور).
     * مانده = جمع خطوط سفارش (به تفکیک نوع) منهای جمع تراکنش‌های فلز ثبت‌شده برای همان سفارشات.
     *
     * @return Collection<int, array{product_id: int, product: \App\Models\Product, total_debit: float, total_credit: float, balance: float, status: string}>
     */
    public function calculateCustomerMetalBalance(User $customer): Collection
    {
        $precision = $this->getPrecision();
        $byProductId = [];

        $orders = Order::with('lines.product')
            ->where('user_id', $customer->id)
            ->get();

        // جمع خطوط سفارش به تفکیک محصول (debit=خرید از مشتری، credit=فروش به مشتری)
        foreach ($orders as $order) {
            foreach ($order->lines as $line) {
                if ($line->grams <= 0 || !$line->product_id) {
                    continue;
                }
                $product = $line->product;
                if (!$product) {
                    continue;
                }
                $key = (int) $line->product_id;
                if (!isset($byProductId[$key])) {
                    $byProductId[$key] = ['product' => $product, 'debit' => 0.0, 'credit' => 0.0, 'metal_debit' => 0.0, 'metal_credit' => 0.0];
                }
                $grams = (float) $line->grams;
                if ($order->order_type === 'BUY') {
                    $byProductId[$key]['debit'] += $grams;
                } else {
                    $byProductId[$key]['credit'] += $grams;
                }
            }
        }

        // جمع تراکنش‌های فلز (تحویل/دریافت) برای سفارشات این مشتری، به تفکیک محصول و نوع سفارش
        $orderIds = $orders->pluck('id')->toArray();
        if (!empty($orderIds)) {
            $metalSums = Metal::query()
                ->whereIn('order_id', $orderIds)
                ->selectRaw('order_id, product_id, SUM(grams) as total_grams')
                ->groupBy('order_id', 'product_id')
                ->get();

            $orderTypeById = $orders->keyBy('id')->map(fn ($o) => $o->order_type);

            foreach ($metalSums as $row) {
                $productId = (int) $row->product_id;
                $grams = (float) $row->total_grams;
                $orderType = $orderTypeById->get($row->order_id);

                if (!isset($byProductId[$productId])) {
                    $product = Product::find($productId);
                    if (!$product) {
                        continue;
                    }
                    $byProductId[$productId] = ['product' => $product, 'debit' => 0.0, 'credit' => 0.0, 'metal_debit' => 0.0, 'metal_credit' => 0.0];
                }

                if ($orderType === 'BUY') {
                    $byProductId[$productId]['metal_debit'] += $grams;
                } else {
                    $byProductId[$productId]['metal_credit'] += $grams;
                }
            }
        }

        $result = collect();
        foreach ($byProductId as $productId => $row) {
            $orderDebit = $row['debit'];
            $orderCredit = $row['credit'];
            $metalDebit = $row['metal_debit'] ?? 0.0;
            $metalCredit = $row['metal_credit'] ?? 0.0;

            // مانده = تعهد از سفارشات منهای تحویل/دریافت انجام‌شده
            $remainingDebit = max(0, $orderDebit - $metalDebit);
            $remainingCredit = max(0, $orderCredit - $metalCredit);

            // balance از دید سیستم: مثبت = مشتری بدهکار فلز، منفی = مشتری طلبکار فلز
            $balance = $remainingDebit - $remainingCredit;

            if (abs($balance) > 0.001) {
                $result->push([
                    'product_id' => $productId,
                    'product' => $row['product'],
                    'total_debit' => round($remainingDebit, $precision),
                    'total_credit' => round($remainingCredit, $precision),
                    'balance' => round($balance, $precision),
                    'status' => $balance > 0.001 ? 'CREDITOR' : ($balance < -0.001 ? 'DEBTOR' : 'BALANCED'),
                ]);
            }
        }

        return $result;
    }

    /**
     * Get customer metal balance summary (گرم، بدون خالص‌سازی).
     *
     * @return array{balances: Collection, total_debt_grams: float, total_credit_grams: float}
     */
    public function getCustomerMetalBalanceSummary(User $customer): array
    {
        $balances = $this->calculateCustomerMetalBalance($customer);
        $precision = $this->getPrecision();

        $totalDebtGrams = $balances->sum(fn ($b) => $b['balance'] > 0 ? $b['balance'] : 0);
        $totalCreditGrams = $balances->sum(fn ($b) => $b['balance'] < 0 ? abs($b['balance']) : 0);

        $formattedBalances = $balances->map(function ($b) use ($precision) {
            $product = $b['product'];
            return [
                'product_id' => $b['product_id'],
                'product' => [
                    'id' => $product->id,
                    'name' => $product->name,
                    'metal_type' => $product->metal_type,
                    'purity' => (float) $product->purity,
                ],
                'debt_grams' => $b['balance'] > 0 ? round($b['balance'], $precision) : 0,
                'credit_grams' => $b['balance'] < 0 ? round(abs($b['balance']), $precision) : 0,
                'total_debit' => $b['total_debit'],
                'total_credit' => $b['total_credit'],
            ];
        });

        return [
            'balances' => $formattedBalances,
            'total_debt_grams' => round($totalDebtGrams, $precision),
            'total_credit_grams' => round($totalCreditGrams, $precision),
        ];
    }

    /**
     * Get all customers with metal debt/credit.
     *
     * @return Collection
     */
    public function getAllCustomersMetalBalance(): Collection
    {
        $customers = User::where('is_customer', true)->get();
        $result = collect();

        foreach ($customers as $customer) {
            $summary = $this->getCustomerMetalBalanceSummary($customer);

            if ($summary['total_debt_grams'] > 0.001 || $summary['total_credit_grams'] > 0.001) {
                $result->push([
                    'customer' => $customer,
                    'balances' => $summary['balances'],
                    'total_debt_grams' => $summary['total_debt_grams'],
                    'total_credit_grams' => $summary['total_credit_grams'],
                ]);
            }
        }

        return $result;
    }

    /**
     * Get precision from settings (default 3 decimals).
     */
    private function getPrecision(): int
    {
        $setting = SystemSetting::where('key', 'metal_precision')->first();
        return (int) ($setting?->value ?? 3);
    }
}
