<?php


namespace App\Classes\Modules\Transactions\ControllersLogic;

use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\Transactions\Processors\CreateSupplierTransactionProcessor;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Models\Document;
use App\Models\Group;

use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Companies\Services\FetchesCompany;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\BillGroup;
use App\Models\Transaction;

class CreateSupplierBillGroupLogic extends AbstractControllerLogic
{


    /**
     * @return array
     */
    protected function notification(): array
    {
        return [
            'title' => 'Create Supplier White Form Order',
            'message' => 'You have successfully created currency supplier white form order'
        ];
    }

    /** @var FetchesCompany */
    private $fetchesCompany;

    /** @var CreatesTransaction */
    private $createsTransaction;

    /** @var CreatesDocument */
    private $createsDocument;

    /** @var CreatesFiles */
    private $createsFile;

    /** @var GeneratesTransactionBillNumber */
    private $generatesTransactionBillNumber;

    /** @var UpdateGroupLogic */
    private $updateGroupLogic;

    /** @var UpdatesTransactionStatus */
    private $updatesTransactionStatus;


    /**
     * CreateSupplierBillGroupLogic constructor.
     * @param FetchesCompany $fetchesCompany
     * @param CreatesTransaction $createsTransaction
     * @param CreatesDocument $createsDocument
     * @param CreatesFiles $createsFile
     * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
     * @param UpdateGroupLogic $updateGroupLogic
     * @param UpdatesTransactionStatus $updatesTransactionStatus
     */
    public function __construct(FetchesCompany $fetchesCompany, CreatesTransaction $createsTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber, UpdateGroupLogic $updateGroupLogic, UpdatesTransactionStatus $updatesTransactionStatus)
    {
        $this->fetchesCompany = $fetchesCompany;
        $this->createsTransaction = $createsTransaction;
        $this->createsDocument = $createsDocument;
        $this->createsFile = $createsFile;
        $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
        $this->updateGroupLogic = $updateGroupLogic;
        $this->updatesTransactionStatus = $updatesTransactionStatus;
    }

    public function logic(Request $request): JsonResponse
    {

        $supplier = $this->fetchesCompany->execute(['id' => $request->route('id')]);

        $payments = $request->input('payments');
        $supplierRefunds = $request->input('supplierRefunds');

        foreach ($supplierRefunds as $supplierRefund) {
            $refund = Transaction::find($supplierRefund['id']);

            if ($refund->owner->transactions()->where('type', TransactionType::BILL)->first()->issuer !== $supplier->id) {
                throw new MalformedRequestException('The supplier refund and bill group does not belongs to same supplier.');
            }

            if ($refund->type !== TransactionType::SUPPLIER_REFUND) {
                throw new MalformedRequestException('Only transaction type supplier refund can be used for bill refund.');
            }

            if ($refund->currency_rate == 1) {
                throw new MalformedRequestException('Supplier refund with currecy rate 1 cannot be used for bill refund.');
            }
        }

        $amount = 0;
        $original_amount = 0;

        foreach ($payments as $payment) {
            $amount += $payment['amount'];
            $original_amount += $payment['original_amount'];
        }

        $service_charges = 0;

        // if ($supplier->id === 4548 || $supplier->id === 2729) {
        //     $amount =  round(floatval(str_replace(',', '', $request->input('payment_total'))), 2);
        // } else {
        $service_charges = floatval(str_replace(',', '', $request->input('service_charges')));
        // }

        $rate = $original_amount / $amount;

        // if ($supplier->id === 4548 || $supplier->id === 2729) {
        //     $request['rate'] = $rate;
        //     $request['supplier_id'] = $supplier->id;
        //     foreach ($payments as $payment) {
        //         $request->route()->setParameter('id', $payment['id']);
        //         $this->updateGroupLogic->execute($request);
        //     }
        // }

        $billGroup = new BillGroup();
        $billGroup->issuer = $supplier->id;
        $billGroup->receiver = 1;
        $billGroup->reference = $this->generatesTransactionBillNumber->execute('BSPO-');
        $billGroup->amount = round(($amount + $service_charges), 2);
        $billGroup->original_amount = round($original_amount, 2);
        $billGroup->currency_id = 1;
        $billGroup->original_currency_id = $payments[0]['original_currency']['id'];
        $billGroup->currency_rate = $rate;
        $billGroup->tax = 0;
        $billGroup->service_charge = round($service_charges, 2);
        $billGroup->status = ApprovalStatus::PENDING_SUBMISSION;
        $billGroup->save();

        foreach ($payments as $payment) {
            $billGroup->groups()->sync($payment['id'], false);
        }

        //create bill refund
        $amount += $service_charges;

        foreach ($supplierRefunds as $supplierRefund) {
            $refund = Transaction::find($supplierRefund['id']);
            $deductedRefunds = $refund->transactions()->where('type', TransactionType::BILL_REFUND)->where('status', ApprovalStatus::APPROVED)->get();
            $refundDeductableAmount = $refund->amount - $deductedRefunds->sum('amount');
            $refundDeductableOriginalAmount = $refund->original_amount - $deductedRefunds->sum('original_amount');

            $amount -= $refundDeductableAmount;
            $original_amount -= $refundDeductableOriginalAmount;

            if ($amount > 0) {
                $deductedRefundAmount = $refundDeductableAmount;
                $deductedRefundOriginalAmount = $refundDeductableOriginalAmount;

                $this->updatesTransactionStatus->execute($refund, ApprovalStatus::COMPLETED);
            }

            if ($amount < 0) {
                $deductedRefundAmount = $refundDeductableAmount + $amount;
                $deductedRefundOriginalAmount = $refundDeductableOriginalAmount + $original_amount;
            }

            $billNumber = $this->generatesTransactionBillNumber->execute('BRFD-');

            $object = new TransactionObject(
                $billNumber,
                TransactionType::BILL_REFUND,
                $supplier->id,
                1,
                1,
                PaymentMethodType::CASH,
                $deductedRefundAmount,
                $deductedRefundOriginalAmount,
                $refund->currency_id,
                $refund->original_currency_id,
                $deductedRefundOriginalAmount / $deductedRefundAmount,
                0,
                0,
                null,
                ApprovalStatus::APPROVED,
                []
            );

            $transaction = $this->createsTransaction->execute($refund, $object);

            $billGroup->billRefunds()->sync($transaction->id, false);
        }

        $billGroup->amount = round($amount, 2);
        $billGroup->save();

        return $this->response([]);
    }
}
