<?php

namespace App\Services\Finance;

use App\Models\Expense;
use App\Models\User;
use App\Services\Concerns\RunsInTransaction;
use App\Services\Concerns\SyncsPhotos;
use App\Services\Media\MediaService;
use App\Services\System\PushNotificationService;
use App\Support\Media\MediaPresenter;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;

class ExpenseService
{
    use RunsInTransaction, SyncsPhotos;

    private const MAX_PHOTOS = 1;

    public function __construct(
        private readonly CashService $cashService,
        private readonly MediaService $mediaService,
        private readonly PushNotificationService $pushNotificationService,
    ) {}

    public function paginateForIndex(array $tableQuery): LengthAwarePaginator
    {
        $query = Expense::query()
            ->with(['createdBy.profile', 'media'])
            ->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
                $search = $tableQuery['search'];
                $query->where(function (Builder $query) use ($search): void {
                    $query->where('description', 'like', "%{$search}%");
                });
            });

        $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);

        return $query
            ->paginate(25)
            ->withQueryString()
            ->through(function (Expense $expense) {
                $expense->setAttribute(
                    'photos',
                    MediaPresenter::collection($expense, 'photos'),
                );

                return $expense;
            });
    }

    public function create(array $validated, User $user): void
    {
        $expense = $this->runInTransaction(
            function () use ($validated, $user): Expense {
                $amount = (int) $validated['amount'];
                $description = $validated['description'];

                $expense = Expense::create([
                    'amount' => $amount,
                    'description' => $description,
                    'created_by_id' => $user->id,
                ]);

                $cashTransaction = $this->cashService->recordOutgoing(
                    $expense,
                    $amount,
                    $description,
                    $user,
                );

                $expense->update([
                    'cash_transaction_id' => $cashTransaction->id,
                ]);

                $this->syncPhotos($expense, $validated, self::MAX_PHOTOS);

                return $expense;
            },
            'Gagal membuat pengeluaran',
        );

        $this->pushNotificationService->sendToRoles(
            '💸 Pengeluaran Baru',
            "Pengeluaran baru sebesar {$expense->amount_formatted} dengan keterangan {$expense->description} telah dicatat oleh {$user->profile?->full_name}.",
            ['owner', 'developer', 'direktur'],
            route('admin.finance.expenses.index'),
        );
    }

    public function update(Expense $expense, array $validated, User $user): void
    {
        $this->runInTransaction(
            function () use ($expense, $validated): void {
                $amount = (int) $validated['amount'];
                $description = $validated['description'];

                $expense->update([
                    'amount' => $amount,
                    'description' => $description,
                ]);

                if ($expense->cashTransaction) {
                    $this->cashService->updateReferencedTransaction(
                        $expense->cashTransaction,
                        $amount,
                        $description,
                    );
                }

                $this->syncPhotos($expense, $validated, self::MAX_PHOTOS);
            },
            'Gagal memperbarui pengeluaran',
        );

        $this->pushNotificationService->sendToRoles(
            '✏️ Pengeluaran Diperbarui',
            "Pengeluaran dengan keterangan {$expense->description} diperbarui menjadi sebesar {$expense->amount_formatted} oleh {$user->profile?->full_name}.",
            ['owner', 'developer', 'direktur'],
            route('admin.finance.expenses.index'),
        );
    }

    public function delete(Expense $expense, User $user): void
    {
        $amountFormatted = $expense->amount_formatted;
        $description = $expense->description;

        $this->runInTransaction(
            function () use ($expense): void {
                if ($expense->cashTransaction) {
                    $this->cashService->deleteReferencedTransaction($expense->cashTransaction);
                }

                $expense->delete();
            },
            'Gagal menghapus pengeluaran',
        );

        $this->pushNotificationService->sendToRoles(
            '🗑️ Pengeluaran Dihapus',
            "Pengeluaran sebesar {$amountFormatted} dengan keterangan {$description} telah dihapus oleh {$user->profile?->full_name}.",
            ['owner', 'developer', 'direktur'],
            route('admin.finance.expenses.index'),
        );
    }

    private function applySorting(Builder $query, string $sort, string $direction): void
    {
        if (in_array($sort, ['created_at', 'amount', 'description'], true)) {
            $query->orderBy($sort, $direction);

            return;
        }

        $query->latest();
    }
}
