<?php

namespace Yajny\Member\Controllers;

use Yajny\EventsYj\Events\PayoutNew;
use Yajny\EventsYj\Events\PayoutUpdated;
use Yajny\Member\Entities\PayoutAddRequest;
use Yajny\Member\Entities\PayoutMethodFeesTypeUtil;
use Yajny\Member\Entities\Payouts\PayoutStatusUtil;
use Yajny\Member\Entities\PayoutUpdateRequest;
use Yajny\Member\Exceptions\UserPayouts\AmountLessThanMinAmountException;
use Yajny\Member\Exceptions\UserPayouts\AmountMoreThanConfirmedBalanceException;
use Yajny\Member\Exceptions\UserPayouts\PaymentAccountNotFoundException;
use Yajny\Member\Exceptions\UserPayouts\PaymentMethodNotFoundException;
use Yajny\Member\Models\Currencies;
use Yajny\Member\Models\CurrencyRate;
use Yajny\Member\Models\Member;
use Yajny\Member\Models\PayoutMethods;
use Yajny\Member\Models\UserPayouts;
use Yajny\Member\Models\UsersBalance;
use Yajny\Models\Country;

class PayoutCtrl
{
    /**
     * @param PayoutAddRequest $request
     * @return mixed
     * @throws AmountLessThanMinAmountException
     * @throws AmountMoreThanConfirmedBalanceException
     * @throws PaymentAccountNotFoundException
     * @throws PaymentMethodNotFoundException
     */
    public static function addPayout(PayoutAddRequest $request)
    {
//        $user = Member::findOrFail($request->user_id);
//        $method = PayoutMethods::where('code', $request->method_code)->where('enabled', true)->first();
//
//        if (!$method)
//            throw new PaymentMethodNotFoundException();
//
//        $userPayMode = $user->payout_methods()->where('method_code', $request->method_code)->where('account', $request->account)->first();
//        if (!$userPayMode)
//            throw new PaymentAccountNotFoundException();
//
//        $conversionRate = 1;
//        if (isset($method->default_currency) && strtoupper($method->default_currency) != 'USD') {
//            $payoutCurrency = Currencies::where('iso_code', strtoupper($method->default_currency))->first();
//            if ($payoutCurrency) {
//                $conversionRate = $payoutCurrency->conversion_rate;
//            }
//        }
//        $amountInUSD = $request->amount / $conversionRate;
//
//        if ($request->amount < $method->minimum_amount)
//            throw new AmountLessThanMinAmountException();
//
//        if ($amountInUSD > $user->confirmed_balance)
//            throw new AmountMoreThanConfirmedBalanceException();
//
//        $fees = self::calculateFees($method, $request);
//        $payout = new UserPayouts;
//        self::savePayout($payout, $method, $request, $amountInUSD, $conversionRate, $fees);
//        $user->payouts()->save($payout);
//
//        event(new  PayoutNew($user, $payout));
//        return $payout->id;


        //get user country because the mobile is currently sending USD only

        $user = Member::findOrFail($request->user_id);
        $method = PayoutMethods::where('code', $request->method_code)->where('enabled', true)->first();
        $amount = $request->amount;
        if (!$method) {
            // return error -> Method not found
            throw new PaymentMethodNotFoundException();
        }

        $userPayMode = $user->payout_methods()->where('method_code', $request->method_code)->where('account', $request->account)->first();
        $conversionRate = Currencies::where('iso_code', $method->default_currency)->first()->conversion_rate;
        $amountInUSD = $amount / $conversionRate;


        if (!$userPayMode) {
            // return error -> Method not found
            throw new PaymentAccountNotFoundException();
        }
        if ($amount < $method->minimum_amount) {
            // return error -> Requesting less than Minuimum amount
            throw new AmountLessThanMinAmountException();
        }


        $userBalance = UsersBalance::where('user_id', $user->user_id)->first();
        // method currency is USD
        if ($method->default_currency == "USD") {
            if ($userBalance->available_amount < $amountInUSD) {
                throw new AmountMoreThanConfirmedBalanceException();
            }
        } // payout method used is different from USD
        else {

            if ($userBalance['available_amount_' . strtolower($method->default_currency)] < $amount)
                throw new AmountMoreThanConfirmedBalanceException();
        }
        $fees = self::calculateFees($method, $request);
        $payout = new UserPayouts;
        self::savePayout($payout, $method, $request, $amountInUSD, $conversionRate, $fees);
        $user->payouts()->save($payout);

        event(new  PayoutNew($user, $payout));
        return $payout->id;
    }

    /**
     * @param $id
     * @param PayoutUpdateRequest $request
     * @return mixed
     * @throws AmountLessThanMinAmountException
     * @throws AmountMoreThanConfirmedBalanceException
     * @throws PaymentAccountNotFoundException
     * @throws PaymentMethodNotFoundException
     */
    public static function updatePayout($id, PayoutUpdateRequest $request)
    {
        $user = Member::findOrFail($request->user_id);
        $userPayout = UserPayouts::findOrFail($id);

        $updateRate = false;
        $methodQuery = PayoutMethods::where('code', $request->method_code);
        if ($userPayout->method_code == $request->method_code) {
            // No Change in Method --> Keep Conversion Rate
            $method = $methodQuery->withoutGlobalScopes()->first();
        } else {
            // Change in Method --> Update Conversion Rate
            $method = $methodQuery->first();
            $updateRate = true;
        }

        if (!$method)
            throw new PaymentMethodNotFoundException();

        $userPayoutMethodQuery = $user->payout_methods_without_global_scopes()->where('method_code', $request->method_code)->where('account', $request->account);
        if ($userPayout->account != $request->account)
            $userPayoutMethodQuery = $userPayoutMethodQuery->where('enabled', 1);

        $userPayoutMethod = $userPayoutMethodQuery->first();
        if (!$userPayoutMethod)
            throw new PaymentAccountNotFoundException();

        $conversionRate = Currencies::where('iso_code', $method->default_currency)->first()->conversion_rate;
        $amountInUSD = $request->amount;
        if($method->default_currency!="USD") $amountInUSD = $request->amount/$conversionRate;

       if (in_array($request->status, PayoutStatusUtil::getAcceptingStatuses()) ){
            if ($method->default_currency!="USD"){
                if ($request->amount*$conversionRate < $method->minimum_amount)
                    throw new AmountLessThanMinAmountException();
            }else{
                if ($request->amount < $method->minimum_amount)
                    throw new AmountLessThanMinAmountException();
            }

        }
        $confirmed_balance = $user->confirmed_balance;
        if (!in_array($userPayout->status, PayoutStatusUtil::getRejectingStatuses()))
            $confirmed_balance += $userPayout->amount;
        if ($method->default_currency=="USD"){
            if ($amountInUSD > $confirmed_balance)
                throw new AmountMoreThanConfirmedBalanceException();
        }
        $fees = self::calculateFees($method, $request);

        self::savePayout($userPayout, $method, $request, $amountInUSD, $conversionRate, $fees);
        $userPayout->save();

        event(new PayoutUpdated($user, $userPayout));
        return $userPayout->id;
    }

    /**
     * @param PayoutMethods $method
     * @param PayoutAddRequest|PayoutUpdateRequest $request
     * @return float|int|mixed|null
     */
    public static function calculateFees(PayoutMethods $method, $request)
    {
        $fees = null;
        $methodFee = $method->fee;
        if ($methodFee) {
            if ($request->user_id) {
                $user = Member::find($request->user_id);
                if ($user->is_merchant) {
                    $methodFee->amount = $methodFee->merchant_amount;
                }
            }
            if ($methodFee->type == PayoutMethodFeesTypeUtil::PERCENTAGE)
                $fees = $request->amount * $methodFee->amount / 100;
            else
                $fees = $methodFee->amount;

            if ($methodFee->min_amount)
                $fees = max($fees, $methodFee->min_amount);
        }
        if($fees > 200){
            $fees = 200;
        }
        if($fees < 20){
            $fees = 20;
        }
        return $fees;
    }

    /**
     * @param UserPayouts $payout
     * @param PayoutMethods $method
     * @param PayoutAddRequest|PayoutUpdateRequest $request
     * @param $amountInUSD
     * @param $conversionRate
     * @param null $fees
     */
    public static function savePayout(UserPayouts $payout, PayoutMethods $method, $request, $amountInUSD, $conversionRate, $fees = null)
    {

        $payout->method_code = $request->method_code;
        $payout->amount = $amountInUSD;
        $payout->final_amount = $amountInUSD;
        $payout->cashback_amount = 0;
        $payout->reward_amount = 0;
        $payout->account = $request->account;
        $payout->currency_rate = $conversionRate;
        $payout->local_currency = $method->default_currency;
        $payout->local_amount = $request->amount;
        $payout->local_final_amount = $request->amount;
        $payout->status = $request->status ?? PayoutStatusUtil::CREATED;
        if ($request->note)
            $payout->note = $request->note;

        if ($fees) {
            $feesInUSD = $fees / $conversionRate;
            $payout->fees_amount = $feesInUSD;
            $payout->local_fees_amount = $fees;
            $payout->final_amount = $amountInUSD - $feesInUSD;
            $payout->local_final_amount = $request->amount - $fees;
            $payout->fees_data = json_encode([
                'amount' => $method->fee->amount,
                'type' => $method->fee->type,
                'min_amount' => $method->fee->min_amount
            ]);
        }
    }
}