<?php

namespace App\Actions\Transactions;

use App\Models\Expense;
use App\Actions\Balances\CreateBalanceAction;
use App\Actions\BaseAction;
use App\Actions\Ledgers\CreateLedgerAction;
use App\DataObjects\CreateLedgerDTO;
use App\DataObjects\CreateTransactionDTO;
use App\Models\Account;
use Closure;
use Illuminate\Support\Facades\Pipeline;
use Spatie\LaravelData\Data;

class CreateTransactionAction extends BaseAction
{
    public function __invoke(CreateTransactionDTO|Data $payload, Closure $next)
    {
        $payload->transaction = $payload->transactionable->transactions()->create($payload->data);

        $this->transactionAccountLedger($payload);

        $this->withHoldingAccountLedger($payload);

        $this->cashAccountLedger($payload);

        if ($payload->transaction->discount !== 0) {
            $this->discountAccountLedger($payload);
        }

        return $next($payload);
    }

    public function transactionAccountLedger($payload): void
    {
        $branch = $payload->transaction->branch;
        $isExpense = $payload->transactionable instanceof Expense;
        $type = $isExpense ? 'debit' : 'credit';

        $discount = $payload->transaction->discount ?? 0.00;

        if ($branch->isClientVatable) {
            //create transaction account ledger
            $ledgerPayload = new CreateLedgerDTO(
                branch_id: $payload->transactionable->branch_id,
                amount: $payload->transaction->net_amount + $discount ?? 0.00,
                transaction: $payload->transaction,
                account: $payload->transaction->account,
                type: $type,
            );
            $this->ledgerPipe($ledgerPayload);

            $this->taxAccountLedger($payload, $isExpense);
        } else {
            //create transaction account ledger
            $ledgerPayload = new CreateLedgerDTO(
                branch_id: $payload->transactionable->branch_id,
                amount: $payload->transaction->gross_amount ?? 0.00,
                transaction: $payload->transaction,
                account: $payload->transaction->account,
                type: $type,
            );
            $this->ledgerPipe($ledgerPayload);
        }

    }

    public function ledgerPipe(CreateLedgerDTO $ledgerPayload): mixed
    {
        return Pipeline::send(passable: $ledgerPayload)->through([
            CreateLedgerAction::class,
            CreateBalanceAction::class,
        ])->thenReturn();
    }

    public function taxAccountLedger($payload, bool $isExpense): void
    {
        $accountName = $isExpense ? 'Input Tax' : 'Output Tax';
        $type = $isExpense ? 'debit' : 'credit';
        $clientId = $payload->transactionable->branch->client_id;

        $taxAccount = Account::query()
            ->where('account', $accountName)
            ->where('client_id', $clientId)
            ->first();

        $amount = $isExpense 
            ? ($payload->transactionable->input_tax ?? 0.00) 
            : ($payload->transactionable->output_tax ?? 0.00);

        if ($taxAccount && $amount > 0) {
            $ledgerPayload = new CreateLedgerDTO(
                branch_id: $payload->transactionable->branch_id,
                amount: $amount,
                transaction: $payload->transaction,
                account: $taxAccount,
                type: $type,
            );
            $this->ledgerPipe($ledgerPayload);
        }
    }

    public function withHoldingAccountLedger($payload): void
    {
        $isExpense = $payload->transactionable instanceof Expense;
        $accountName = $isExpense ? 'Payable Withholding Tax' : 'Creditable Withholding Tax';
        $type = $isExpense ? 'credit' : 'debit';
        $clientId = $payload->transactionable->branch->client_id;

        $withholdingAccount = Account::query()
            ->where('account', $accountName)
            ->where('client_id', $clientId)
            ->first();

        $amount = $payload->transaction->payable_withholding_tax ?? 0.00;

        if ($withholdingAccount && $amount > 0) {
            $ledgerPayload = new CreateLedgerDTO(
                branch_id: $payload->transactionable->branch_id,
                amount: $amount,
                transaction: $payload->transaction,
                account: $withholdingAccount,
                type: $type,
            );
            $this->ledgerPipe($ledgerPayload);
        }
    }

    public function cashAccountLedger($payload): void
    {
        $isExpense = $payload->transactionable instanceof Expense;
        $type = $isExpense ? 'credit' : 'debit';
        $wht = $isExpense ? ($payload->transaction->payable_withholding_tax ?? 0) : ($payload->transaction->creditable_withholding_tax ?? 0);
        $amount = ($payload->transaction->gross_amount ?? 0) - $wht;
        $clientId = $payload->transactionable->branch->client_id;

        $cashAccount = Account::query()
            ->where('account', 'Cash')
            ->where('client_id', $clientId)
            ->first();

        if ($cashAccount && $amount > 0) {
            $ledgerPayload = new CreateLedgerDTO(
                branch_id: $payload->transactionable->branch_id,
                amount: $amount, 
                transaction: $payload->transaction,
                account: $cashAccount,
                type: $type,
            );
            $this->ledgerPipe($ledgerPayload);
        }
    }

    public function discountAccountLedger($payload): void
    {
        $isExpense = $payload->transactionable instanceof Expense;
        $type = $isExpense ? 'credit' : 'debit';
        $amount = $payload->transaction->discount ?? 0.00;
        $clientId = $payload->transactionable->branch->client_id;

        $discountAccount = Account::query()
            ->where('account', 'Sales Discount')
            ->where('client_id', $clientId)
            ->first();

        if ($discountAccount && $amount > 0) {
            $ledgerPayload = new CreateLedgerDTO(
                branch_id: $payload->transactionable->branch_id,
                amount: $amount,
                transaction: $payload->transaction,
                account: $discountAccount,
                type: $type,
            );
            $this->ledgerPipe($ledgerPayload);
        }
    }
}
