<?php

namespace App\Services\Refunds;

use App\Models\Configuration;
use App\Models\Enrollment;
use App\Models\Invoice;
use App\Models\Refund;
use App\Services\Fees\FeeRefundDetailService;
use Illuminate\Support\Facades\DB;

class RefundPolicyService
{
    public function __construct(
        private FeeRefundDetailService $refundDetails,
        private RefundInvoiceAdjustmentService $invoiceAdjustments
    ) {
    }

    public function processWithdrawalRefund(
        int $parentId,
        ?int $invoiceId = null,
        ?string $schoolYear = null,
        ?string $semester = null,
        ?int $actorId = null
    ): array {
        $schoolYear = $schoolYear ?? (string) (Configuration::getConfig('school_year') ?? '');
        if ($schoolYear === '') {
            return ['ok' => false, 'error' => 'Missing school year for refund processing.'];
        }

        $semester = $semester ?? (string) (Configuration::getConfig('semester') ?? '');

        $invoice = $invoiceId
            ? Invoice::query()->find($invoiceId)
            : Invoice::query()
                ->where('parent_id', $parentId)
                ->where('school_year', $schoolYear)
                ->orderByDesc('created_at')
                ->first();

        if (!$invoice) {
            return ['ok' => false, 'error' => 'No invoice found for refund processing.'];
        }

        $students = Enrollment::query()
            ->where('parent_id', $parentId)
            ->where('school_year', $schoolYear)
            ->get()
            ->map(fn ($row) => $row->toArray())
            ->all();

        if (empty($students)) {
            return ['ok' => false, 'error' => 'No enrollments found for refund processing.'];
        }

        $details = $this->refundDetails->calculateRefundDetails($students, $parentId);

        return DB::transaction(function () use (
            $details,
            $invoice,
            $parentId,
            $schoolYear,
            $semester,
            $actorId,
            $students
        ) {
            if (empty($details['ok'])) {
                $reason = $details['error'] ?? 'Refund calculation failed.';
                $note = json_encode([
                    'parent_id' => $parentId,
                    'invoice_id' => (int) ($invoice->id ?? 0),
                    'error' => $reason,
                ]);

                $existingRefund = Refund::query()->where('invoice_id', (int) $invoice->id)->first();
                if ($existingRefund) {
                    $existingRefund->update([
                        'refund_amount' => 0.0,
                        'status' => Refund::STATUS_REJECTED,
                        'reason' => $reason,
                        'request' => 'tuition',
                        'note' => $note,
                        'updated_at' => utc_now(),
                        'updated_by' => $actorId,
                    ]);
                } else {
                    Refund::query()->create([
                        'parent_id' => $parentId,
                        'school_year' => $schoolYear,
                        'semester' => $semester,
                        'invoice_id' => (int) $invoice->id,
                        'refund_amount' => 0.0,
                        'refund_paid_amount' => 0.00,
                        'status' => Refund::STATUS_REJECTED,
                        'reason' => $reason,
                        'request' => 'tuition',
                        'note' => $note,
                        'requested_at' => utc_now(),
                        'created_at' => utc_now(),
                        'updated_at' => utc_now(),
                        'updated_by' => $actorId,
                    ]);
                }

                return ['ok' => false, 'error' => $reason];
            }

            $invoiceUpdate = $this->invoiceAdjustments->applyBookChargeAndUpdateInvoice(
                $invoice,
                $parentId,
                $details,
                $semester,
                $actorId
            );

            $refundAmount = (float) ($details['refund_amount'] ?? 0.0);
            $debitAmount = (float) ($details['debit_amount'] ?? 0.0);
            $eligible = !empty($details['eligible']);
            $status = $refundAmount > 0 ? Refund::STATUS_PENDING : Refund::STATUS_REJECTED;

            $reason = null;
            if ($refundAmount <= 0) {
                if (!$eligible) {
                    $reason = 'No refund due to late withdrawal.';
                } elseif (!empty($details['full_discount'])) {
                    $reason = 'No refund due to full discount.';
                } elseif ($debitAmount > 0) {
                    $reason = 'No refund due; book fee balance remains.';
                } else {
                    $reason = 'No refund due after book fee deduction.';
                }
            }

            $auditPayload = [
                'student_ids' => array_map('intval', array_column($students, 'student_id')),
                'parent_id' => $parentId,
                'withdraw_date' => $details['withdraw_date'] ?? null,
                'refund_deadline' => $details['refund_deadline'] ?? null,
                'eligible' => $details['eligible'] ?? null,
                'paid_amount' => $details['paid_amount'] ?? null,
                'book_price' => $details['book_price'] ?? null,
                'invoice_total' => $details['invoice_total'] ?? null,
                'discount_total' => $details['discount_total'] ?? null,
                'full_discount' => $details['full_discount'] ?? null,
                'refundable_tuition' => $details['refundable_tuition'] ?? null,
                'withdrawn_eligible_count' => $details['withdrawn_eligible_count'] ?? null,
                'refund_amount' => $refundAmount,
                'debit_amount' => $debitAmount,
                'invoice_id' => (int) ($invoice->id ?? 0),
            ];

            $auditPayload = array_merge($auditPayload, $invoiceUpdate);

            $existingRefund = Refund::query()->where('invoice_id', (int) $invoice->id)->first();
            if ($existingRefund) {
                $existingRefund->update([
                    'refund_amount' => $refundAmount,
                    'status' => $status,
                    'reason' => $reason,
                    'request' => 'tuition',
                    'note' => json_encode($auditPayload),
                    'updated_at' => utc_now(),
                    'updated_by' => $actorId,
                ]);

                return [
                    'ok' => true,
                    'refund_id' => (int) $existingRefund->id,
                    'refund_amount' => $refundAmount,
                    'status' => $status,
                ];
            }

            $created = Refund::query()->create([
                'parent_id' => $parentId,
                'school_year' => $schoolYear,
                'semester' => $semester,
                'invoice_id' => (int) $invoice->id,
                'refund_amount' => $refundAmount,
                'refund_paid_amount' => 0.00,
                'status' => $status,
                'reason' => $reason,
                'request' => 'tuition',
                'note' => json_encode($auditPayload),
                'requested_at' => utc_now(),
                'created_at' => utc_now(),
                'updated_at' => utc_now(),
                'updated_by' => $actorId,
            ]);

            if (!$created) {
                return ['ok' => false, 'error' => 'Failed to create refund record.'];
            }

            return [
                'ok' => true,
                'refund_id' => (int) $created->id,
                'refund_amount' => $refundAmount,
                'status' => $status,
            ];
        });
    }
}
