<?php

namespace App\Domain\Financial\Services;

use App\Domain\Financial\Enums\PaymentStatus;
use App\Domain\Financial\Enums\TransactionType;
use App\Domain\Financial\Events\PaymentReceived;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Models\Payment;
use App\Domain\Financial\Models\Transaction;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class PaymentService
{
    public function __construct(
        private InvoiceService $invoiceService,
    ) {}

    public function recordPayment(array $data, User $creator): Payment
    {
        return DB::transaction(function () use ($data, $creator) {
            if (empty($data['payer_type']) && !empty($data['invoice_id'])) {
                $invoice = Invoice::find($data['invoice_id']);
                if ($invoice) {
                    $data['payer_type'] = $invoice->billable_type;
                    $data['payer_id'] = $invoice->billable_id;
                    $data['academy_id'] ??= $invoice->academy_id;
                }
            }

            if (empty($data['reference'])) {
                $data['reference'] = 'PAY-' . now()->format('YmdHis') . '-' . $data['invoice_id'];
            }

            $payment = Payment::create([
                ...$data,
                'status' => PaymentStatus::Confirmed,
                'confirmed_at' => now(),
                'created_by' => $creator->id,
            ]);

            if ($payment->invoice_id) {
                $this->invoiceService->updatePaidAmount(
                    $payment->invoice,
                    $payment->amount,
                    $creator
                );
            }

            $this->createTransaction($payment);

            PaymentReceived::dispatch($payment, $creator);

            return $payment;
        });
    }

    public function refund(Payment $payment, int $amount, User $creator): Payment
    {
        return DB::transaction(function () use ($payment, $amount, $creator) {
            $refund = Payment::create([
                'academy_id' => $payment->academy_id,
                // Inherit the branch of the payment being refunded, otherwise the
                // refund lands in no branch and disappears from every branch view.
                'branch_id' => $payment->branch_id,
                'invoice_id' => $payment->invoice_id,
                'reference' => 'REF-' . $payment->reference,
                'direction' => 'outbound',
                'method' => $payment->method,
                'status' => PaymentStatus::Confirmed,
                'payer_type' => $payment->payer_type,
                'payer_id' => $payment->payer_id,
                'amount' => $amount,
                'currency' => $payment->currency,
                'payment_date' => now()->toDateString(),
                'confirmed_at' => now(),
                'notes' => "Refund for payment #{$payment->reference}",
                'created_by' => $creator->id,
            ]);

            if ($payment->invoice_id) {
                $this->invoiceService->updatePaidAmount(
                    $payment->invoice,
                    -$amount,
                    $creator
                );
            }

            $this->createTransaction($refund, TransactionType::Refund);

            return $refund;
        });
    }

    private function createTransaction(Payment $payment, ?TransactionType $type = null): Transaction
    {
        $isInbound = $payment->direction === 'inbound';
        $type ??= $isInbound ? TransactionType::PaymentReceived : TransactionType::PaymentMade;

        return Transaction::create([
            'academy_id' => $payment->academy_id,
            'debit_account_id' => $this->resolveDebitAccount($payment),
            'credit_account_id' => $this->resolveCreditAccount($payment),
            'payment_id' => $payment->id,
            'invoice_id' => $payment->invoice_id,
            'amount' => $payment->amount,
            'currency' => $payment->currency,
            'type' => $type,
            'description' => $this->buildDescription($payment, $type),
            'transaction_date' => $payment->payment_date,
            'created_by' => $payment->created_by,
        ]);
    }

    private function resolveDebitAccount(Payment $payment): int
    {
        // TODO: resolve from chart of accounts based on payment method
        return 1;
    }

    private function resolveCreditAccount(Payment $payment): int
    {
        // TODO: resolve from chart of accounts based on invoice source
        return 2;
    }

    private function buildDescription(Payment $payment, TransactionType $type): string
    {
        return match ($type) {
            TransactionType::PaymentReceived => "Payment received: {$payment->amount} {$payment->currency}",
            TransactionType::PaymentMade => "Payment made: {$payment->amount} {$payment->currency}",
            TransactionType::Refund => "Refund: {$payment->amount} {$payment->currency}",
            default => "Transaction: {$payment->amount} {$payment->currency}",
        };
    }
}
