<?php

namespace App\Services\Admin\Finance\Cash;

use App\Enums\CashTransactionType;
use App\Enums\Role;
use App\Models\CashAccount;
use App\Models\CashTransaction;
use App\Services\Concerns\HandlesCashTransactions;
use App\Services\Concerns\RegistersMedia;
use App\Services\NotificationService;
use App\Services\S3PresignedService;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\LengthAwarePaginator as PaginationLengthAwarePaginator;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class CashTransactionService
{
    use HandlesCashTransactions, RegistersMedia;

    public function __construct(
        private S3PresignedService $s3Service,
        private CashAccountService $cashAccountService
    ) {}

    public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator
    {
        $cashAccount = $this->cashAccountService->get();

        if (! $cashAccount) {
            return new PaginationLengthAwarePaginator(collect(), 0, $perPage);
        }

        $paginator = $cashAccount->cashTransactions()
            ->select(['id', 'created_by_id', 'reference_type', 'reference_id', 'amount', 'balance_after', 'type', 'description', 'created_at'])
            ->with('createdBy.userProfile', 'media')
            ->when($search, fn ($q) => $q->where('description', 'like', "%{$search}%"))
            ->when($filters['type'] ?? null, function ($query, $type) {
                $query->where('type', $type);
            })
            ->orderBy($sort, $direction)
            ->paginate($perPage);

        $paginator->getCollection()->transform(fn (CashTransaction $transaction) => $this->formatTransaction($transaction));

        return $paginator;
    }

    public function deposit(array $data): CashTransaction
    {
        $transaction = DB::transaction(fn () => $this->creditCash(
            amount: $data['amount'],
            description: $data['description'],
        ));

        if (array_key_exists('receipt_key', $data)) {
            $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'photos', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null);
        }

        NotificationService::notify(
            roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
            title: 'Setoran Kas Toko',
            body: 'Setoran sebesar Rp '.number_format($data['amount'], 0, ',', '.').' berhasil dicatat'.' oleh '.auth()->user()->full_name.'.',
            url: route('admin.finance.cash-accounts.index'),
        );

        return $transaction;
    }

    public function withdrawal(array $data): CashTransaction
    {
        $transaction = DB::transaction(fn () => $this->debitCash(
            amount: $data['amount'],
            description: $data['description'],
            type: CashTransactionType::WITHDRAWAL,
        ));

        if (array_key_exists('receipt_key', $data)) {
            $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'photos', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null);
        }

        NotificationService::notify(
            roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
            title: 'Penarikan Kas Toko',
            body: 'Penarikan sebesar Rp '.number_format($data['amount'], 0, ',', '.').' berhasil dicatat'.' oleh '.auth()->user()->full_name.'.',
            url: route('admin.finance.cash-accounts.index'),
        );

        return $transaction;
    }

    public function update(CashTransaction $transaction, array $data): CashTransaction
    {
        $transaction = DB::transaction(function () use ($transaction, $data) {
            if (! in_array($transaction->type, [CashTransactionType::DEPOSIT, CashTransactionType::WITHDRAWAL])) {
                throw ValidationException::withMessages([
                    'amount' => 'Transaksi ini tidak dapat diedit.',
                ]);
            }

            $cashAccount = CashAccount::firstOrFail();
            $oldAmount = $transaction->amount;
            $newAmount = $data['amount'];
            $isDeposit = $transaction->type === CashTransactionType::DEPOSIT;

            $difference = $isDeposit ? $newAmount - $oldAmount : $oldAmount - $newAmount;

            if ($difference < 0 && $cashAccount->balance < abs($difference)) {
                throw ValidationException::withMessages([
                    'amount' => 'Saldo tidak mencukupi.',
                ]);
            }

            $newBalance = $cashAccount->balance + $difference;
            $cashAccount->update(['balance' => $newBalance]);

            $transaction->update([
                'amount' => $newAmount,
                'balance_after' => $newBalance,
                'description' => $data['description'],
            ]);

            if (array_key_exists('receipt_key', $data)) {
                $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'photos', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null);
            }

            return $transaction;
        });

        NotificationService::notify(
            roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
            title: 'Transaksi Kas Diperbarui',
            body: 'Transaksi kas sebesar Rp '.number_format($transaction->amount, 0, ',', '.').' berhasil diperbarui'.' oleh '.auth()->user()->full_name.'.',
            url: route('admin.finance.cash-accounts.index'),
        );

        return $transaction;
    }

    public function destroy(CashTransaction $transaction): bool
    {
        return DB::transaction(function () use ($transaction) {
            if (! in_array($transaction->type, [CashTransactionType::DEPOSIT, CashTransactionType::WITHDRAWAL])) {
                throw ValidationException::withMessages([
                    'amount' => 'Transaksi ini tidak dapat dihapus.',
                ]);
            }

            $cashAccount = CashAccount::firstOrFail();
            $isDeposit = $transaction->type === CashTransactionType::DEPOSIT;

            $newBalance = $isDeposit
                ? $cashAccount->balance - $transaction->amount
                : $cashAccount->balance + $transaction->amount;

            if ($newBalance < 0) {
                throw ValidationException::withMessages([
                    'amount' => 'Saldo tidak mencukupi untuk menghapus transaksi ini.',
                ]);
            }

            $cashAccount->update(['balance' => $newBalance]);

            $media = $transaction->getFirstMedia('photos');
            if ($media) {
                Cache::forget("cash_transaction_receipt_{$media->id}");
            }

            $deleted = $transaction->delete();

            if ($deleted) {
                NotificationService::notify(
                    roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
                    title: 'Transaksi Kas Dihapus',
                    body: 'Transaksi kas sebesar Rp '.number_format($transaction->amount, 0, ',', '.').' berhasil dihapus'.' oleh '.auth()->user()->full_name.'.',
                    url: route('admin.finance.cash-accounts.index'),
                );
            }

            return $deleted;
        });
    }

    private function formatTransaction(CashTransaction $transaction): array
    {
        $media = $transaction->getFirstMedia('photos');

        if (! $media) {
            return $transaction->toArray() + [
                'receipt_key' => null,
                'receipt_url' => null,
                'receipt_conversion_url' => null,
            ];
        }

        $s3Key = $media->getCustomProperty('s3_key') ?? $media->getPath();

        return $transaction->toArray() + [
            'receipt_key' => $s3Key,
            'receipt_url' => $this->s3Service->getTemporaryUrl($s3Key),
            'receipt_conversion_url' => $this->s3Service->getTemporaryUrl($media->getPath('thumb')),
        ];
    }
}
