<?php

namespace App\Domain\Financial\Services;

use App\Domain\Financial\Enums\TransactionType;
use App\Domain\Financial\Models\Expense;
use App\Domain\Financial\Models\FacilityRentPayment;
use App\Domain\Financial\Models\FinancialAccount;
use App\Domain\Financial\Models\Transaction;
use App\Domain\Shared\Exceptions\DomainException;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class ExpenseService
{
    public function recordRentPayment(array $data, User $actor): FacilityRentPayment
    {
        return DB::transaction(function () use ($data, $actor) {
            $academyId = app('current_academy')?->id ?? $actor->academy_id;

            $rentPayment = FacilityRentPayment::create([
                'academy_id' => $academyId,
                'branch_id' => $data['branch_id'] ?? null,
                'facility_id' => $data['facility_id'],
                'period' => $data['period'],
                'amount' => $data['amount'],
                'payment_method' => $data['payment_method'],
                'cheque_number' => $data['cheque_number'] ?? null,
                'cheque_date' => $data['cheque_date'] ?? null,
                'bank_name' => $data['bank_name'] ?? null,
                'recipient_name' => $data['recipient_name'] ?? null,
                'payment_date' => $data['payment_date'],
                'notes' => $data['notes'] ?? null,
                'created_by' => $actor->id,
            ]);

            $this->createExpenseTransaction(
                academyId: $academyId,
                expenseAccountCode: '5010',
                paymentMethod: $data['payment_method'],
                amount: $data['amount'],
                description: "إيجار منشأة: {$data['period']}",
                referenceModel: $rentPayment,
                actor: $actor,
                transactionDate: $data['payment_date'],
            );

            return $rentPayment;
        });
    }

    public function recordExpense(array $data, User $actor): Expense
    {
        return DB::transaction(function () use ($data, $actor) {
            $academyId = app('current_academy')?->id ?? $actor->academy_id;

            $expense = Expense::create([
                'academy_id' => $academyId,
                'branch_id' => $data['branch_id'] ?? null,
                'category' => $data['category'],
                'amount' => $data['amount'],
                'description' => $data['description'],
                'recipient_name' => $data['recipient_name'] ?? null,
                'payment_method' => $data['payment_method'],
                'receipt_reference' => $data['receipt_reference'] ?? null,
                'expense_date' => $data['expense_date'],
                'notes' => $data['notes'] ?? null,
                'created_by' => $actor->id,
            ]);

            $expenseAccountCode = $this->resolveExpenseAccountCode($data['category']);

            $this->createExpenseTransaction(
                academyId: $academyId,
                expenseAccountCode: $expenseAccountCode,
                paymentMethod: $data['payment_method'],
                amount: $data['amount'],
                description: $data['description'],
                referenceModel: $expense,
                actor: $actor,
                transactionDate: $data['expense_date'],
            );

            return $expense;
        });
    }

    public function recordExternalRevenue(array $data, User $actor): Expense
    {
        return DB::transaction(function () use ($data, $actor) {
            $academyId = app('current_academy')?->id ?? $actor->academy_id;

            $expense = Expense::create([
                'academy_id' => $academyId,
                'branch_id' => $data['branch_id'] ?? null,
                'category' => 'external_revenue',
                'amount' => $data['amount'],
                'description' => $data['description'],
                'recipient_name' => $data['source'] ?? null,
                'payment_method' => $data['payment_method'],
                'receipt_reference' => $data['reference_number'] ?? null,
                'expense_date' => $data['revenue_date'],
                'notes' => $data['notes'] ?? null,
                'created_by' => $actor->id,
            ]);

            $revenueAccount = FinancialAccount::where('academy_id', $academyId)
                ->where('code', '4060')
                ->first();

            if (!$revenueAccount) {
                throw new DomainException('حساب الإيرادات الخارجية غير موجود — يرجى إعداد شجرة الحسابات');
            }

            $cashAccountCode = match ($data['payment_method']) {
                'bank_transfer', 'cheque' => '1010',
                default => '1000',
            };

            $cashAccount = FinancialAccount::where('academy_id', $academyId)
                ->where('code', $cashAccountCode)
                ->first();

            if (!$cashAccount) {
                throw new DomainException('حساب النقدية/البنك غير موجود — يرجى إعداد شجرة الحسابات');
            }

            Transaction::create([
                'academy_id' => $academyId,
                'debit_account_id' => $cashAccount->id,
                'credit_account_id' => $revenueAccount->id,
                'reference_type' => get_class($expense),
                'reference_id' => $expense->id,
                'amount' => $data['amount'],
                'currency' => 'EGP',
                'type' => TransactionType::PaymentReceived,
                'description' => $data['description'],
                'transaction_date' => $data['revenue_date'],
                'created_by' => $actor->id,
            ]);

            return $expense;
        });
    }

    public function cancelExpense(Expense $expense, string $reason, User $actor): Expense
    {
        if ($expense->status === 'cancelled') {
            throw new DomainException('هذا المصروف ملغى بالفعل');
        }

        return DB::transaction(function () use ($expense, $reason, $actor) {
            $expense->update([
                'status' => 'cancelled',
                'cancelled_by' => $actor->id,
                'cancelled_at' => now(),
                'cancellation_reason' => $reason,
            ]);

            $originalTransaction = Transaction::where('reference_type', get_class($expense))
                ->where('reference_id', $expense->id)
                ->first();

            if ($originalTransaction) {
                Transaction::create([
                    'academy_id' => $expense->academy_id,
                    'debit_account_id' => $originalTransaction->credit_account_id,
                    'credit_account_id' => $originalTransaction->debit_account_id,
                    'reference_type' => get_class($expense),
                    'reference_id' => $expense->id,
                    'amount' => $expense->amount,
                    'currency' => 'EGP',
                    'type' => TransactionType::Adjustment,
                    'description' => "إلغاء مصروف: {$expense->description}",
                    'transaction_date' => now()->toDateString(),
                    'created_by' => $actor->id,
                ]);
            }

            return $expense;
        });
    }

    private function createExpenseTransaction(
        int $academyId,
        string $expenseAccountCode,
        string $paymentMethod,
        int $amount,
        string $description,
        $referenceModel,
        User $actor,
        string $transactionDate,
    ): void {
        $debitAccount = FinancialAccount::where('academy_id', $academyId)
            ->where('code', $expenseAccountCode)
            ->first();

        if (!$debitAccount) {
            throw new DomainException('حساب المصروفات غير موجود — يرجى إعداد شجرة الحسابات');
        }

        $creditAccountCode = match ($paymentMethod) {
            'cash' => '1000',
            'bank_transfer', 'cheque' => '1010',
            default => '1000',
        };

        $creditAccount = FinancialAccount::where('academy_id', $academyId)
            ->where('code', $creditAccountCode)
            ->first();

        if (!$creditAccount) {
            throw new DomainException('حساب النقدية/البنك غير موجود — يرجى إعداد شجرة الحسابات');
        }

        Transaction::create([
            'academy_id' => $academyId,
            'debit_account_id' => $debitAccount->id,
            'credit_account_id' => $creditAccount->id,
            'reference_type' => get_class($referenceModel),
            'reference_id' => $referenceModel->id,
            'amount' => $amount,
            'currency' => 'EGP',
            'type' => TransactionType::PaymentMade,
            'description' => $description,
            'transaction_date' => $transactionDate,
            'created_by' => $actor->id,
        ]);
    }

    private function resolveExpenseAccountCode(string $category): string
    {
        return match ($category) {
            'maintenance' => '5030',
            'utilities' => '5030',
            'sports_equipment' => '5020',
            'supplies' => '5020',
            default => '5050',
        };
    }
}
