<?php

namespace App\Services\Reimbursements;

use App\Models\Expense;
use App\Models\Reimbursement;
use App\Models\ReimbursementBatch;
use App\Models\ReimbursementBatchAdminFile;
use App\Models\ReimbursementBatchItem;
use Illuminate\Support\Facades\DB;
use RuntimeException;

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

    public function createBatch(?string $title, int $userId, string $schoolYear, string $semester): array
    {
        $sequence = $this->nextYearlyBatchNumber($schoolYear);
        $now = now();

        $batch = ReimbursementBatch::query()->create([
            'title' => $title !== null && trim($title) !== '' ? trim($title) : null,
            'status' => ReimbursementBatch::STATUS_OPEN,
            'created_by' => $userId ?: null,
            'opened_at' => $now,
            'school_year' => $schoolYear,
            'semester' => $semester,
            'yearly_batch_number' => $sequence,
        ]);

        $label = trim((string) $batch->title) !== '' ? (string) $batch->title : ('Batch #' . $sequence);
        if (trim((string) $batch->title) === '') {
            $batch->title = $label;
            $batch->save();
        }

        return [
            'batch_id' => (int) $batch->id,
            'label' => $label,
            'sequence' => $sequence,
        ];
    }

    public function lockBatch(int $batchId, int $userId, string $schoolYear, string $semester): void
    {
        $batch = ReimbursementBatch::query()->find($batchId);
        if (!$batch || strtolower((string) $batch->status) !== ReimbursementBatch::STATUS_OPEN) {
            throw new RuntimeException('Batch not found or already closed.');
        }

        $adminRows = ReimbursementBatchItem::query()
            ->selectRaw('DISTINCT COALESCE(admin_id, 0) AS admin_id')
            ->where('batch_id', $batchId)
            ->whereNull('unassigned_at')
            ->get();

        $adminIds = array_values(array_unique($adminRows->pluck('admin_id')->map(fn ($id) => (int) $id)->all()));
        $requiredAdmins = array_values(array_filter($adminIds, static fn ($adminId) => $adminId > 0));

        if (!empty($requiredAdmins)) {
            $uploadedAdminIds = ReimbursementBatchAdminFile::query()
                ->where('batch_id', $batchId)
                ->whereIn('admin_id', $requiredAdmins)
                ->pluck('admin_id')
                ->map(fn ($id) => (int) $id)
                ->unique()
                ->values()
                ->all();

            foreach ($requiredAdmins as $adminId) {
                if (!in_array($adminId, $uploadedAdminIds, true)) {
                    throw new RuntimeException('All admin sections must have a check file before submitting the batch.');
                }
            }
        }

        DB::transaction(function () use ($batchId, $userId, $schoolYear, $semester) {
            $items = DB::table('reimbursement_batch_items as bi')
                ->selectRaw('bi.id as batch_item_id, bi.reimbursement_id as batch_reimb_id, bi.expense_id, e.amount, e.purchased_by, e.description, e.reimbursement_id as expense_reimb_id, e.school_year as expense_school_year, e.semester as expense_semester')
                ->join('expenses as e', 'e.id', '=', 'bi.expense_id')
                ->where('bi.batch_id', $batchId)
                ->whereNull('bi.unassigned_at')
                ->get();

            foreach ($items as $item) {
                $expenseId = (int) ($item->expense_id ?? 0);
                $recipientId = (int) ($item->purchased_by ?? 0);
                if ($expenseId <= 0 || $recipientId <= 0) {
                    continue;
                }

                $reimbId = $item->batch_reimb_id ?: ($item->expense_reimb_id ?: $this->lookupService->findReimbursementIdForExpense($expenseId));

                if (!$reimbId) {
                    $payload = [
                        'expense_id' => $expenseId,
                        'amount' => (float) ($item->amount ?? 0),
                        'reimbursed_to' => $recipientId,
                        'approved_by' => $userId ?: null,
                        'description' => trim((string) ($item->description ?? '')),
                        'status' => 'Paid',
                        'added_by' => $userId ?: null,
                        'school_year' => $item->expense_school_year ?: $schoolYear,
                        'semester' => $item->expense_semester ?: $semester,
                        'reimbursement_method' => 'Check',
                        'batch_number' => $batchId,
                        'created_at' => now(),
                        'updated_at' => now(),
                    ];

                    $reimbId = (int) Reimbursement::query()->create($payload)->id;
                }

                if ($reimbId) {
                    Reimbursement::query()->where('id', $reimbId)->update([
                        'batch_number' => $batchId,
                        'approved_by' => $userId ?: null,
                        'status' => 'Paid',
                    ]);

                    Expense::query()->where('id', $expenseId)->update(['reimbursement_id' => $reimbId]);
                    if (!empty($item->batch_item_id)) {
                        ReimbursementBatchItem::query()->where('id', (int) $item->batch_item_id)->update([
                            'reimbursement_id' => $reimbId,
                        ]);
                    }
                }
            }

            ReimbursementBatch::query()->where('id', $batchId)->update([
                'status' => ReimbursementBatch::STATUS_CLOSED,
                'closed_at' => now(),
                'closed_by' => $userId ?: null,
            ]);
        });
    }

    private function nextYearlyBatchNumber(string $schoolYear): int
    {
        $year = trim($schoolYear);
        if ($year === '') {
            return 1;
        }

        $max = ReimbursementBatch::query()
            ->where('school_year', $year)
            ->max('yearly_batch_number');

        return ((int) $max) + 1;
    }
}
