<?php

namespace App\Core\Services\Accounting;

use App\Core\Repositories\ExpenseRepository;
use App\Models\Expense;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * Expense Service
 * Handles expense business logic
 */
class ExpenseService
{
    public function __construct(
        private ExpenseRepository $repository,
        private JournalEntryService $journalService
    ) {}

    /**
     * Create a new expense
     */
    public function create(array $data): Expense
    {
        DB::beginTransaction();

        try {
            // Generate expense number
            $data['expense_number'] = $this->repository->generateExpenseNumber();

            // Calculate total
            $data['total'] = ($data['amount'] ?? 0) + ($data['tax_amount'] ?? 0);

            // Create expense
            $expense = $this->repository->create([
                'expense_number' => $data['expense_number'],
                'expense_date' => $data['expense_date'],
                'account_id' => $data['account_id'],
                'vendor_id' => $data['vendor_id'] ?? null,
                'category' => $data['category'] ?? null,
                'payment_method' => $data['payment_method'] ?? 'cash',
                'amount' => $data['amount'],
                'tax_amount' => $data['tax_amount'] ?? 0,
                'total' => $data['total'],
                'currency' => $data['currency'] ?? \App\Models\Currency::getDefaultCode(),
                'description' => $data['description'] ?? null,
                'notes' => $data['notes'] ?? null,
                'receipt_path' => $data['receipt_path'] ?? null,
                'status' => $data['status'] ?? 'draft',
                'created_by' => auth()->id(),
            ]);

            // If status is approved/paid, create journal entry
            if (in_array($expense->status, ['approved', 'paid'])) {
                $this->recordExpense($expense);
            }

            DB::commit();

            return $expense->load(['account', 'vendor']);
        } catch (Exception $e) {
            DB::rollBack();
            Log::error('Expense creation failed: '.$e->getMessage());
            throw $e;
        }
    }

    /**
     * Record expense in accounting (create journal entry)
     */
    public function recordExpense(Expense $expense): void
    {
        $account = $expense->account;
        $cashAccount = $this->getCashAccount($expense->payment_method);

        $this->journalService->create([
            'entry_date' => $expense->expense_date,
            'description' => 'Expense: '.$expense->expense_number,
            'status' => 'posted',
            'reference_type' => 'expense',
            'reference_id' => $expense->id,
            'lines' => [
                [
                    'account_id' => $account->id,
                    'type' => 'debit',
                    'amount' => $expense->total,
                    'description' => $expense->description,
                ],
                [
                    'account_id' => $cashAccount->id,
                    'type' => 'credit',
                    'amount' => $expense->total,
                    'description' => 'Payment for expense '.$expense->expense_number,
                ],
            ],
        ]);
    }

    /**
     * Get cash account based on payment method
     */
    private function getCashAccount(string $paymentMethod): \App\Models\ChartOfAccount
    {
        // This should be configured or use a default cash account
        $accountCode = match ($paymentMethod) {
            'cash' => '1010', // Cash account
            'bank_transfer' => '1020', // Bank account
            default => '1010',
        };

        $repo = app(\App\Core\Repositories\ChartOfAccountRepository::class);
        $cashAccount = $repo->getByCode($accountCode);

        if (! $cashAccount) {
            throw new Exception('Cash account not found. Please configure chart of accounts.');
        }

        return $cashAccount;
    }

    /**
     * Approve an expense
     */
    public function approve(Expense $expense): bool
    {
        DB::beginTransaction();

        try {
            $this->repository->update($expense, [
                'status' => 'approved',
                'approved_by' => auth()->id(),
                'approved_at' => now(),
            ]);

            // Record in accounting
            $this->recordExpense($expense);

            DB::commit();

            return true;
        } catch (Exception $e) {
            DB::rollBack();
            Log::error('Expense approval failed: '.$e->getMessage());
            throw $e;
        }
    }
}
