<?php

namespace App\Services\Accounting;

use App\Enums\JournalSource;
use App\Models\FixedAsset;
use App\Models\JournalEntry;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;

class FixedAssetService
{
    public function __construct(
        private readonly JournalService $journalService,
        private readonly AccountLookupService $accountLookup,
    ) {}

    public function runDepreciation(FixedAsset $asset): JournalEntry
    {
        if ($asset->status !== 'active') {
            throw new InvalidArgumentException('Depreciation can only be run on active assets.');
        }

        if (! $asset->useful_life_months || $asset->useful_life_months <= 0) {
            throw new InvalidArgumentException('Asset must have a useful life to calculate depreciation.');
        }

        $monthlyAmount = $this->calculateMonthlyDepreciation($asset);

        if (bccomp($monthlyAmount, '0.00', 2) <= 0) {
            throw new InvalidArgumentException('No depreciable amount remaining for this asset.');
        }

        $depreciationExpense = $this->accountLookup->configured('depreciation_expense');
        $accumulatedDepreciation = $this->accountLookup->configured('accumulated_depreciation');

        return DB::transaction(function () use ($asset, $monthlyAmount, $depreciationExpense, $accumulatedDepreciation) {
            $entry = $this->journalService->post([
                'date' => now()->toDateString(),
                'description' => "Depreciation — {$asset->asset_code} {$asset->name}",
                'source' => JournalSource::Depreciation->value,
                'source_reference' => $asset->asset_code,
            ], [
                [
                    'account_id' => $depreciationExpense->id,
                    'debit_amount' => $monthlyAmount,
                    'credit_amount' => 0,
                    'line_memo' => "Depreciation {$asset->asset_code}",
                ],
                [
                    'account_id' => $accumulatedDepreciation->id,
                    'debit_amount' => 0,
                    'credit_amount' => $monthlyAmount,
                    'line_memo' => "Accumulated depreciation {$asset->asset_code}",
                ],
            ]);

            $newAccumulated = bcadd((string) $asset->accumulated_depreciation, $monthlyAmount, 2);
            $newBookValue = bcsub((string) $asset->purchase_cost, $newAccumulated, 2);

            if (bccomp($newBookValue, (string) $asset->salvage_value, 2) < 0) {
                $newBookValue = number_format((float) $asset->salvage_value, 2, '.', '');
                $newAccumulated = bcsub((string) $asset->purchase_cost, $newBookValue, 2);
            }

            $asset->update([
                'accumulated_depreciation' => $newAccumulated,
                'book_value' => $newBookValue,
                'status' => bccomp($newBookValue, (string) $asset->salvage_value, 2) <= 0 ? 'fully_depreciated' : $asset->status,
            ]);

            return $entry;
        });
    }

    public function calculateMonthlyDepreciation(FixedAsset $asset): string
    {
        $depreciableBase = bcsub((string) $asset->purchase_cost, (string) $asset->salvage_value, 2);
        $remaining = bcsub($depreciableBase, (string) $asset->accumulated_depreciation, 2);

        if (bccomp($remaining, '0.00', 2) <= 0) {
            return '0.00';
        }

        $monthly = bcdiv($depreciableBase, (string) $asset->useful_life_months, 2);

        return bccomp($monthly, $remaining, 2) > 0 ? $remaining : $monthly;
    }
}
