<?php

namespace App\Services;

use App\Models\BalanceLog;
use App\Models\FixedCommissionSetting;
use App\Models\IncomeHistory;
use App\Models\User;

class FixedCommissionDistributor
{
    public const TERMINAL_COLLECTOR_REFERRAL_CODE = '123';

    public function creditActivationCommissions(User $user): void
    {
        $this->walkRecipients($user, function (User $recipient, int $level) use ($user): void {
            $amount = $this->configuredAmount($level);
            if ($amount <= 0) {
                return;
            }

            $recipient->balance = (float) ($recipient->balance ?? 0) + $amount;
            $recipient->save();

            BalanceLog::create([
                'user_id' => $recipient->id,
                'amount' => $amount,
            ]);

            IncomeHistory::create([
                'user_id' => $recipient->id,
                'source' => 'referral',
                'amount' => $amount,
                'description' => 'অভিনন্দন! আপনি ' . $level . ' তম লেভেলের '
                    . $user->name . ' (' . $user->referral_code . ') এর একাউন্ট অ্যাক্টিভেশন থেকে '
                    . $amount . '৳ কমিশন পেয়েছেন।',
            ]);
        });
    }

    public function creditVerificationCommissions(User $user): void
    {
        $this->walkRecipients($user, function (User $recipient, int $level) use ($user): void {
            $amount = $this->configuredAmount($level);
            if ($amount <= 0) {
                return;
            }

            $recipient->balance = (float) ($recipient->balance ?? 0) + $amount;
            $recipient->save();

            BalanceLog::create([
                'user_id' => $recipient->id,
                'amount' => $amount,
            ]);

            IncomeHistory::create([
                'user_id' => $recipient->id,
                'source' => 'referral',
                'amount' => $amount,
                'description' => 'অভিনন্দন! আপনি ' . $level . ' তম লেভেলের '
                    . $user->name . ' (' . $user->referral_code . ') এর একাউন্ট ভেরিফিকেশন থেকে '
                    . $amount . '৳ কমিশন পেয়েছেন।',
            ]);
        });
    }

    public function reverseVerificationCommissions(User $user): void
    {
        $this->walkRecipients($user, function (User $recipient, int $level) use ($user): void {
            $amount = $this->configuredAmount($level);
            if ($amount <= 0) {
                return;
            }

            $beforeBalance = (float) ($recipient->balance ?? 0);
            $appliedAmount = round(min($amount, $beforeBalance), 2);
            if ($appliedAmount <= 0) {
                return;
            }

            $recipient->balance = round($beforeBalance - $appliedAmount, 2);
            $recipient->save();

            BalanceLog::create([
                'user_id' => $recipient->id,
                'amount' => -1 * $appliedAmount,
            ]);

            IncomeHistory::create([
                'user_id' => $recipient->id,
                'source' => 'referral_reversal',
                'amount' => -1 * $appliedAmount,
                'description' => 'রিভার্সাল: ' . $level . ' তম লেভেলের '
                    . $user->name . ' (' . $user->referral_code . ') এর একাউন্ট non-verify হওয়ায় '
                    . $appliedAmount . '৳ কমিশন সমন্বয় করা হয়েছে।',
            ]);
        });
    }

    private function walkRecipients(User $user, callable $callback): void
    {
        $uplineReference = trim((string) ($user->referred_by ?? ''));
        $repeatCollector = false;

        for ($level = 1; $level <= FixedCommissionSetting::MAX_GENERATION; $level++) {
            $recipient = $repeatCollector
                ? $this->findCollector()
                : $this->findUserByReference($uplineReference);

            if (!$recipient) {
                break;
            }

            $callback($recipient, $level);

            if ($this->isTerminalCollector($recipient)) {
                $repeatCollector = true;
                continue;
            }

            $uplineReference = trim((string) ($recipient->referred_by ?? ''));
            if ($uplineReference === '') {
                break;
            }
        }
    }

    private function configuredAmount(int $level): float
    {
        return round((float) (FixedCommissionSetting::where('generation', $level)->value('amount') ?? 0), 2);
    }

    private function findCollector(): ?User
    {
        return User::query()
            ->where('referral_code', self::TERMINAL_COLLECTOR_REFERRAL_CODE)
            ->lockForUpdate()
            ->first();
    }

    private function findUserByReference(?string $reference): ?User
    {
        $reference = trim((string) $reference);
        if ($reference === '') {
            return null;
        }

        $user = User::query()
            ->where('referral_code', $reference)
            ->lockForUpdate()
            ->first();

        if ($user || !ctype_digit($reference)) {
            return $user;
        }

        return User::query()
            ->whereKey((int) $reference)
            ->lockForUpdate()
            ->first();
    }

    private function isTerminalCollector(?User $user): bool
    {
        return trim((string) ($user->referral_code ?? '')) === self::TERMINAL_COLLECTOR_REFERRAL_CODE;
    }
}
