<?php

namespace App\Services\Reimbursements;

use App\Models\ReimbursementBatch;
use App\Models\ReimbursementBatchItem;
use Illuminate\Support\Facades\Log;
use RuntimeException;

class ReimbursementBatchAssignmentService
{
    public function __construct(private ReimbursementLookupService $lookupService)
    {
    }

    public function updateAssignment(
        int $expenseId,
        int $batchId,
        ?int $adminId,
        ?int $reimbursementId,
        string $schoolYear,
        string $semester
    ): array {
        if ($expenseId <= 0) {
            throw new RuntimeException('Invalid expense id.');
        }

        $now = now();

        $activeItem = ReimbursementBatchItem::query()
            ->where('expense_id', $expenseId)
            ->whereNull('unassigned_at')
            ->first();

        if ($batchId <= 0) {
            if ($activeItem) {
                $activeItem->update(['unassigned_at' => $now]);
                if (!$reimbursementId && !empty($activeItem->reimbursement_id)) {
                    $reimbursementId = (int) $activeItem->reimbursement_id;
                }
            }

            return [
                'batch_id' => 0,
                'admin_id' => null,
                'reimbursement_id' => $reimbursementId,
            ];
        }

        $batch = ReimbursementBatch::query()->find($batchId);
        if (!$batch || strtolower((string) $batch->status) !== ReimbursementBatch::STATUS_OPEN) {
            throw new RuntimeException('Batch not found or already closed.');
        }

        $currentAdmin = $activeItem ? (int) ($activeItem->admin_id ?? 0) : null;
        $activeSameBatch = $activeItem && (int) ($activeItem->batch_id ?? 0) === $batchId;
        if ($activeSameBatch && ($currentAdmin === ($adminId ?? 0))) {
            return [
                'batch_id' => $batchId,
                'admin_id' => $adminId,
                'reimbursement_id' => $activeItem->reimbursement_id ?? null,
            ];
        }

        if ($activeSameBatch) {
            if (!$reimbursementId) {
                $reimbursementId = $activeItem->reimbursement_id
                    ? (int) $activeItem->reimbursement_id
                    : $this->lookupService->findReimbursementIdForExpense($expenseId);
            }

            $activeItem->update([
                'admin_id' => $adminId,
                'assigned_at' => $now,
                'reimbursement_id' => $reimbursementId,
                'unassigned_at' => $adminId === null ? $now : null,
            ]);

            return [
                'batch_id' => $batchId,
                'admin_id' => $adminId,
                'reimbursement_id' => $reimbursementId,
            ];
        }

        if ($activeItem) {
            $activeItem->update(['unassigned_at' => $now]);
            if (!$reimbursementId && !empty($activeItem->reimbursement_id)) {
                $reimbursementId = (int) $activeItem->reimbursement_id;
            }
        }

        if (!$reimbursementId) {
            $reimbursementId = $this->lookupService->findReimbursementIdForExpense($expenseId);
        }

        try {
            ReimbursementBatchItem::query()->create([
                'batch_id' => $batchId,
                'expense_id' => $expenseId,
                'reimbursement_id' => $reimbursementId,
                'admin_id' => $adminId,
                'assigned_at' => $now,
                'school_year' => $schoolYear,
                'semester' => $semester,
            ]);
        } catch (\Throwable $e) {
            Log::error('Failed to assign expense to batch: ' . $e->getMessage(), [
                'expense_id' => $expenseId,
                'batch_id' => $batchId,
            ]);
            throw new RuntimeException('Unable to update batch assignment right now.');
        }

        return [
            'batch_id' => $batchId,
            'admin_id' => $adminId,
            'reimbursement_id' => $reimbursementId,
        ];
    }
}
