<?php

namespace App\Classes\Modules\Accounting\ControllersLogic;


use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\CreatesBankStatementTransactionOwner;
use App\Classes\Modules\Accounting\Services\FetchesBankStatementTransaction;
use App\Classes\Modules\Accounting\Processors\ListShippingPortalTransactions;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
use App\Classes\ValueObjects\Constants\SystemType;
use App\Classes\Modules\Accounting\Processors\ChecksBillNumber;
use App\Classes\ValueObjects\Constants\ShippingTransactionType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Booking;
use App\Models\Transaction;
use App\Models\Wallet;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\StatementTransactionOwner;

class UpdateBankStatementDetailLogic extends AbstractControllerLogic
{

    /**
     * @return array
     */
    protected function notification():array {
        return [
            'title' => 'Updated Bank Statement Transactions Details',
            'message' => 'You have successfully updated the Bank Statement Transactions Details'
        ];
    }

    /** @var FetchesBankStatementTransaction */
    private $fetchesBankStatementTransaction;


    /** @var ChecksBillNumber */
    private $checksBillNumber;

    /** @var UpdatesBankStatementTransactionOwnerStatus */
    private $updatesBankStatementTransactionOwnerStatus;

    /**
     * @param FetchesBankStatementTransaction $fetchesBankStatementTransaction
     * @param ChecksBillNumber $checksBillNumber
     */
    public function __construct(
        FetchesBankStatementTransaction $fetchesBankStatementTransaction, 
        ChecksBillNumber $checksBillNumber, 
        UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus)
    {
        $this->fetchesBankStatementTransaction = $fetchesBankStatementTransaction;
        $this->checksBillNumber = $checksBillNumber;
        $this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus;
    }

    public function logic(Request $request): JsonResponse
    {
        $bankStatementTransaction = $this->fetchesBankStatementTransaction->execute(['id' => $request->route('id')]);

        $transactionReference = $request->input('transaction_reference');
        $systemReference = $request->input('system_references');

        $owner_type = $owner_id = $owner_reference = $statementTransactionOwnerType = $system = null;

        $salesSystems = ['lite', 'cntr', 'probashi', 'pets'];
        $allowedSystems = array_merge($salesSystems, ['exchange', 'izyim']);

        if (!in_array($systemReference, $allowedSystems) && !in_array($request->input('pay_for'), ['internal_bank_transfer', 'others'])) {
            throw new MalformedRequestException('System Reference not allowed');
        }

        switch ($request->input('pay_for')) {
            case 'sales':
                $owner_reference = $transactionReference;
                $statementTransactionOwnerType = StatementTransactionOwnerType::SALES;
                break;

            case 'top_up':
                $owner_reference = $transactionReference;
                $statementTransactionOwnerType = StatementTransactionOwnerType::WALLET_TOP_UP;
                break;

            case 'internal_bank_transfer':
                $statementTransactionOwnerType = StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_IN;
                break;

            case 'others':
                $owner_reference = $transactionReference;
                $statementTransactionOwnerType = StatementTransactionOwnerType::NON_OPERATIONAL;
                break;

            default:
                throw new MalformedRequestException('Transaction Type Not Allowed');
        }

        if (in_array($systemReference, ['exchange', 'izyim'])) {
            $transaction = $this->checksBillNumber->execute($transactionReference, $systemReference);

            if (is_array($transaction) && empty($transaction)) {
                throw new MalformedRequestException('Transaction Not Found.');
            } 

            if($systemReference === 'exchange') {
                $owner_type = Transaction::class;
                $owner_id = $transaction->id;
                if($transaction->type === TransactionType::TOP_UP) {
                    $owner_reference = $transaction->owner->owner->reference;
                }

                if($transaction->type === TransactionType::PAYMENT && $transaction->owner_type === Booking::class) {
                    $owner_reference =  $transaction->owner->marking;
                }

            }

            if($systemReference === 'izyim') {
                $transaction        = $transaction[0];
                $payFor             = $request->input('pay_for');
                $transactionType    = $transaction['type'];
                
                if (($payFor === 'sales' && !in_array($transactionType, [ShippingTransactionType::PAYMENT, ShippingTransactionType::GROUP_PAYMENT]))
                    || ($payFor === 'top_up' && !in_array($transactionType, [ShippingTransactionType::TOP_UP, ShippingTransactionType::GROUP_PAYMENT]))) {
                    throw new MalformedRequestException('Transaction Type does not match.');
                }

                $owner_type         = Transaction::class;
                $owner_id           = $transaction['owner_id'];
                $owner_reference    = $transaction['owner_reference'];

            }
        }

        $system = $systemReference != null ? SystemType::SYSTEM_NAMES[$systemReference] : '';

        $statementTransactionOwner = $this->createBankStatementTransactionOwner($bankStatementTransaction, $statementTransactionOwnerType, $system, $owner_type, $owner_id, $owner_reference);

        // this for edit a transaction already mapped
        if ($request->has('editMapped')) $this->editAccountMapped($statementTransactionOwner);

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

    private function createBankStatementTransactionOwner($bankStatementTransaction, $statementTransactionOwnerType, $system, $owner_type, $owner_id, $owner_reference)
    {
        $ownerData = [
            'type' => $statementTransactionOwnerType,
            'system' => $system,
            'owner_type' => $owner_type,
            'owner_id' => $owner_id,
            'owner_reference' => $owner_reference,
        ];

        return $bankStatementTransaction->owners()->firstOrCreate($ownerData);
    }

    private function editAccountMapped(StatementTransactionOwner $owner){
        $this->updateOwnerStatus($owner, ApprovalStatus::APPROVED);
        
        // update all other owners with the same system, owner type, and owner ID
        $this->updateOtherOwners($owner, ApprovalStatus::REJECTED);
    }

    private function updateOwnerStatus(StatementTransactionOwner $owner, int $status)
    {
        $this->updatesBankStatementTransactionOwnerStatus->execute($owner, $status);
    }

    private function updateOtherOwners(StatementTransactionOwner $owner, int $status): void
    {
        StatementTransactionOwner::where('statement_transaction_id', $owner->statement_transaction_id)
            ->where('id', '!=', $owner->id)
            ->update(['status' => $status]);
    }

}
