<?php

namespace App\Domain\Financial\Services;

use App\Domain\Financial\Enums\PaymentMethod;
use App\Domain\Financial\Enums\PaymentStatus;
use App\Domain\Financial\Enums\TransactionType;
use App\Domain\Financial\Models\CashSession;
use App\Domain\Financial\Models\Payment;
use App\Domain\Financial\Models\Transaction;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Training\Models\Enrollment;
use App\Models\User;

class RefundService
{
    public function __construct(
        private readonly InvoiceService $invoiceService,
        private readonly CashSessionService $cashSessionService,
    ) {}

    /**
     * Preview the impact of refunding the given payment UUIDs.
     * Returns plain arrays (safe for Livewire state storage).
     */
    public function preview(array $paymentUuids): array
    {
        $results = [];

        foreach ($paymentUuids as $uuid) {
            $payment = Payment::where('uuid', $uuid)->with('invoice')->first();
            if (! $payment) {
                continue;
            }

            $enrollmentName = null;
            if ($payment->invoice_id) {
                $enrollment = Enrollment::where('invoice_id', $payment->invoice_id)
                    ->whereIn('status', ['active', 'pending'])
                    ->with('program')
                    ->first();
                if ($enrollment) {
                    $enrollmentName = $enrollment->program?->name_ar
                        ?? $enrollment->program?->name
                        ?? 'برنامج';
                }
            }

            $results[] = [
                'uuid'             => $payment->uuid,
                'reference'        => $payment->reference,
                'amount'           => $payment->amount,
                'method_label'     => $payment->method->label(),
                'enrollment_name'  => $enrollmentName,
            ];
        }

        return $results;
    }

    /**
     * Execute the financial side of a refund for each payment UUID.
     * MUST be called inside a DB::transaction() by the caller.
     * Returns [['refund' => Payment, 'enrollment' => Enrollment|null, 'original' => Payment], ...]
     * so the caller can cancel enrollments in the same transaction.
     */
    public function processRefunds(array $paymentUuids, string $reason, User $actor): array
    {
        if (count($paymentUuids) > 2) {
            throw new DomainException('يمكن استرداد دفعتين كحد أقصى في عملية واحدة');
        }

        if (empty($paymentUuids)) {
            throw new DomainException('لم يتم تحديد أي دفعات للاسترداد');
        }

        $results = [];

        foreach ($paymentUuids as $uuid) {
            $payment = Payment::where('uuid', $uuid)
                ->lockForUpdate()
                ->first();

            if (! $payment) {
                throw new DomainException("الدفعة غير موجودة");
            }

            if ($payment->status !== PaymentStatus::Confirmed) {
                throw new DomainException("الدفعة {$payment->reference} غير مؤكدة ولا يمكن استردادها");
            }

            if ($payment->direction !== 'inbound') {
                throw new DomainException("لا يمكن استرداد دفعة صادرة");
            }

            // Find linked enrollment BEFORE mutations (for caller to cancel)
            $enrollment = null;
            if ($payment->invoice_id) {
                $enrollment = Enrollment::where('invoice_id', $payment->invoice_id)
                    ->whereIn('status', ['active', 'pending'])
                    ->with(['program', 'group'])
                    ->first();
            }

            // Create outbound refund payment record
            $refundPayment = Payment::create([
                'academy_id'   => $payment->academy_id,
                'branch_id'    => $payment->branch_id,
                'invoice_id'   => $payment->invoice_id,
                'reference'    => 'REF-' . now()->format('YmdHis') . '-' . $payment->id,
                'direction'    => 'outbound',
                'method'       => $payment->method,
                'status'       => PaymentStatus::Confirmed,
                'payer_type'   => $payment->payer_type,
                'payer_id'     => $payment->payer_id,
                'amount'       => $payment->amount,
                'currency'     => $payment->currency ?? 'EGP',
                'payment_date' => now()->toDateString(),
                'confirmed_at' => now(),
                'notes'        => "استرداد للدفعة {$payment->reference}" . ($reason ? " — {$reason}" : ''),
                'created_by'   => $actor->id,
            ]);

            // Mark original payment as refunded
            $payment->update(['status' => PaymentStatus::Refunded]);

            // Create double-entry transaction for the refund
            Transaction::create([
                'academy_id'        => $refundPayment->academy_id,
                'debit_account_id'  => 2, // Accounts Receivable (reversing the original credit)
                'credit_account_id' => 1, // Cash/Bank (money going out)
                'payment_id'        => $refundPayment->id,
                'invoice_id'        => $refundPayment->invoice_id,
                'amount'            => $refundPayment->amount,
                'currency'          => $refundPayment->currency ?? 'EGP',
                'type'              => TransactionType::Refund,
                'description'       => "استرداد: {$payment->reference}",
                'transaction_date'  => now()->toDateString(),
                'created_by'        => $actor->id,
            ]);

            // Deduct from cash session if this was a cash payment
            if ($payment->method === PaymentMethod::Cash) {
                $cashSession = CashSession::where('user_id', $actor->id)
                    ->where('status', 'open')
                    ->first();

                if ($cashSession) {
                    $this->cashSessionService->recordCashOut($cashSession, $payment->amount);
                }
            }

            // Reverse the invoice balance
            if ($payment->invoice_id) {
                $this->invoiceService->updatePaidAmount(
                    $payment->invoice->fresh(),
                    -$payment->amount,
                    $actor
                );
            }

            $results[] = [
                'refund'      => $refundPayment,
                'enrollment'  => $enrollment,
                'original'    => $payment,
            ];
        }

        return $results;
    }
}
