<?php

namespace App\Services\Refunds;

use App\Models\Configuration;
use App\Models\Refund;
use App\Models\User;
use App\Services\ApplicationUrlService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class RefundRequestService
{
    public const REQUEST_TYPES = ['tuition', 'overpayment', 'duplicate', 'extra'];

    public function __construct(
        private RefundPolicyService $policyService,
        private RefundSummaryService $summaryService,
        private RefundNotificationService $notifications,
        private ApplicationUrlService $urls,
    ) {
    }

    public function requestRefund(array $payload, int $actorId): array
    {
        $parentId = (int) ($payload['parent_id'] ?? 0);
        $requestType = strtolower((string) ($payload['request_type'] ?? ''));
        $amount = (float) ($payload['amount'] ?? 0);
        $invoiceId = isset($payload['invoice_id']) ? (int) $payload['invoice_id'] : null;
        $paymentId = isset($payload['payment_id']) ? (int) $payload['payment_id'] : null;
        $reason = $payload['reason'] ?? null;

        if (!in_array($requestType, self::REQUEST_TYPES, true)) {
            return ['ok' => false, 'message' => 'Invalid refund request type.'];
        }

        $parent = User::query()->find($parentId);
        if (!$parent) {
            return ['ok' => false, 'message' => 'Parent not found.'];
        }

        $termSchoolYear = (string) (Configuration::getConfig('school_year') ?? '');
        $termSemester = (string) (Configuration::getConfig('semester') ?? '');
        $schoolYear = (string) ($payload['school_year'] ?? $termSchoolYear);
        $semester = (string) ($payload['semester'] ?? $termSemester);

        if ($requestType === 'tuition') {
            $result = $this->policyService->processWithdrawalRefund($parentId, $invoiceId, $schoolYear, $semester, $actorId);
            if (empty($result['ok'])) {
                return ['ok' => false, 'message' => $result['error'] ?? 'Refund calculation failed.'];
            }

            if (($result['refund_amount'] ?? 0) > 0) {
                $this->notifications->notifyPending($parentId, (float) $result['refund_amount'], $this->urls->webLoginUrl());
            }

            return [
                'ok' => true,
                'refund_id' => $result['refund_id'] ?? null,
                'refund_amount' => $result['refund_amount'] ?? 0.0,
                'status' => $result['status'] ?? null,
            ];
        }

        if (in_array($requestType, ['overpayment', 'extra'], true)) {
            $summary = $this->summaryService->getParentFinancialSummary($parentId, $schoolYear, $semester);
            if (($summary['unapplied_balance'] ?? 0) <= 0) {
                return ['ok' => false, 'message' => 'No unapplied balance available.'];
            }
            if ($amount > (float) ($summary['unapplied_balance'] ?? 0) + 0.0001) {
                return [
                    'ok' => false,
                    'message' => 'Amount exceeds available unapplied balance.',
                    'available' => number_format((float) ($summary['unapplied_balance'] ?? 0), 2),
                ];
            }
        }

        if ($amount <= 0) {
            return ['ok' => false, 'message' => 'Refund amount must be greater than zero.'];
        }

        return DB::transaction(function () use (
            $parentId,
            $schoolYear,
            $semester,
            $invoiceId,
            $amount,
            $requestType,
            $reason,
            $paymentId,
            $actorId
        ) {
            $refund = Refund::query()->create([
                'parent_id' => $parentId,
                'school_year' => $schoolYear,
                'semester' => $semester,
                'invoice_id' => $invoiceId,
                'refund_amount' => $amount,
                'refund_paid_amount' => 0.00,
                'status' => Refund::STATUS_PENDING,
                'reason' => $reason,
                'request' => $requestType,
                'note' => $paymentId ? ('duplicate-of-payment#' . $paymentId) : null,
                'requested_at' => utc_now(),
                'created_at' => utc_now(),
                'updated_at' => utc_now(),
                'updated_by' => $actorId,
            ]);

            if (!$refund) {
                Log::error('Failed to create refund request.', [
                    'parent_id' => $parentId,
                    'type' => $requestType,
                ]);
                return ['ok' => false, 'message' => 'Failed to create refund request.'];
            }

            $this->notifications->notifyPending($parentId, $amount, $this->urls->webLoginUrl());

            return [
                'ok' => true,
                'refund_id' => (int) $refund->id,
                'refund_amount' => (float) $refund->refund_amount,
            ];
        });
    }
}
