<?php

namespace App\Services;

use App\Models\CashDrawerTransaction;
use App\Models\Expense;
use App\Models\Shift;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class ExpenseService
{
    public function __construct(private readonly AuditLogService $auditLogService)
    {
    }

    public function create(User $user, int $companyId, int $branchId, array $data): Expense
    {
        return DB::transaction(function () use ($user, $companyId, $branchId, $data) {
            $shiftId = $data['shift_id'] ?? $this->resolveShiftId($user, $branchId);

            $expense = Expense::create([
                'company_id' => $companyId,
                'branch_id' => $branchId,
                'shift_id' => $shiftId,
                'expense_category_id' => $data['expense_category_id'] ?? null,
                'user_id' => $user->id,
                'amount' => $data['amount'],
                'description' => $data['description'],
                'payment_method' => $data['payment_method'] ?? 'cash',
                'reference' => $data['reference'] ?? null,
                'receipt_url' => $data['receipt_url'] ?? null,
                'expense_date' => $data['expense_date'],
                'is_approved' => false,
                'device_id' => $data['device_id'] ?? null,
                'synced_at' => ! empty($data['device_id']) ? now() : null,
            ]);

            $this->auditLogService->log($user, 'expense.created', $expense, [], $expense->toArray(), $expense->device_id);

            return $expense->load(['category', 'user', 'approver']);
        });
    }

    public function approve(User $approver, Expense $expense, ?string $notes = null): Expense
    {
        return DB::transaction(function () use ($approver, $expense, $notes) {
            if ($expense->is_approved) {
                throw ValidationException::withMessages([
                    'expense' => ['This expense has already been approved.'],
                ]);
            }

            $expense->forceFill([
                'is_approved' => true,
                'approved_by' => $approver->id,
                'approved_at' => now(),
            ])->save();

            if ($expense->payment_method === 'cash' && $expense->shift_id !== null) {
                $runningBalance = (float) CashDrawerTransaction::query()
                    ->where('shift_id', $expense->shift_id)
                    ->sum('amount');

                CashDrawerTransaction::create([
                    'shift_id' => $expense->shift_id,
                    'cash_register_id' => $expense->shift?->cash_register_id,
                    'user_id' => $expense->user_id,
                    'type' => 'expense',
                    'amount' => -1 * (float) $expense->amount,
                    'running_balance' => round($runningBalance - (float) $expense->amount, 2),
                    'notes' => $notes ?? $expense->description,
                    'reference_type' => Expense::class,
                    'reference_id' => $expense->id,
                ]);
            }

            if ($expense->shift !== null) {
                $expense->shift->activities()->create([
                    'user_id' => $approver->id,
                    'activity_type' => 'expense',
                    'description' => "Expense {$expense->id} approved",
                    'amount' => -1 * (float) $expense->amount,
                    'reference_type' => Expense::class,
                    'reference_id' => $expense->id,
                ]);
            }

            $this->auditLogService->log($approver, 'expense.approved', $expense, [], $expense->toArray(), $expense->device_id);

            return $expense->load(['category', 'user', 'approver']);
        });
    }

    private function resolveShiftId(User $user, int $branchId): ?int
    {
        return Shift::query()
            ->where('branch_id', $branchId)
            ->where('user_id', $user->id)
            ->where('status', 'open')
            ->latest('opened_at')
            ->value('id');
    }
}
