<?php
namespace App\Services;
use App\Models\Account;
use App\Models\BankAccount;
use App\Models\BankTransaction;
use App\Models\FiscalYear;
use App\Models\Journal;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class FinanceService {
    public static function account(string $code): Account { return Account::where('code', $code)->firstOrFail(); }
    /** Post a balanced journal. $lines = [['account' => code|id, 'debit' => x, 'credit' => y, 'narration' => .., 'quarry_id' => .., 'project_id' => ..]] */
    public static function postJournal(string $date, string $narration, array $lines, ?string $sourceModule = null, $sourceId = null, ?string $reference = null): Journal {
        FiscalService::assertOpen($date, 'Journal posting');
        return DB::transaction(function () use ($date, $narration, $lines, $sourceModule, $sourceId, $reference) {
            $td = round(collect($lines)->sum('debit'), 2); $tc = round(collect($lines)->sum('credit'), 2);
            if (abs($td - $tc) > 0.009) throw new \RuntimeException("Unbalanced journal: Dr {$td} vs Cr {$tc}");
            $j = Journal::create(['journal_no' => SequenceService::next('journal'), 'journal_date' => $date,
                'fiscal_year_id' => FiscalYear::current()?->id, 'reference' => $reference, 'narration' => $narration,
                'total_debit' => $td, 'total_credit' => $tc, 'status' => 'posted', 'source_module' => $sourceModule,
                'source_id' => $sourceId, 'created_by' => Auth::id(), 'posted_by' => Auth::id(), 'posted_at' => now()]);
            foreach ($lines as $l) {
                // Codes ('1100') take precedence; fall back to numeric id (e.g. bank gl_account_id)
                $acc = Account::where('code', (string) $l['account'])->first() ?? Account::findOrFail($l['account']);
                $j->lines()->create(['account_id' => $acc->id, 'debit' => $l['debit'] ?? 0, 'credit' => $l['credit'] ?? 0,
                    'narration' => $l['narration'] ?? $narration, 'quarry_id' => $l['quarry_id'] ?? null, 'project_id' => $l['project_id'] ?? null]);
                self::applyBalance($acc, (float) ($l['debit'] ?? 0), (float) ($l['credit'] ?? 0));
            }
            return $j;
        });
    }
    public static function applyBalance(Account $acc, float $dr, float $cr): void {
        $delta = in_array($acc->account_type, ['asset', 'expense']) ? $dr - $cr : $cr - $dr;
        $acc->increment('current_balance', $delta);
    }
    public static function reverseJournal(Journal $j, string $reason): Journal {
        $lines = $j->lines->map(fn($l) => ['account' => $l->account_id, 'debit' => $l->credit, 'credit' => $l->debit, 'narration' => 'Reversal: '.$reason, 'quarry_id' => $l->quarry_id, 'project_id' => $l->project_id])->all();
        return self::postJournal(date('Y-m-d'), 'Reversal of '.$j->journal_no.' — '.$reason, $lines, 'reversal', $j->id, $j->journal_no);
    }
    public static function bankTxn(int $bankId, string $date, string $type, float $amount, string $narration, ?string $module = null, $refId = null, ?string $reference = null, ?string $ccy = null, ?float $rate = null): BankTransaction {
        FiscalService::assertOpen($date, 'Bank entry');
        return DB::transaction(function () use ($bankId, $date, $type, $amount, $narration, $module, $refId, $reference, $ccy, $rate) {
            $b = BankAccount::lockForUpdate()->findOrFail($bankId);
            $ccy = strtoupper($ccy ?? $b->currency ?? 'GHS');
            if ($ccy !== strtoupper($b->currency ?? 'GHS')) throw new \RuntimeException("Currency mismatch: {$ccy} entry into {$b->currency} account {$b->account_no}.");
            $t = BankTransaction::create(['txn_no' => SequenceService::next('bank_txn'), 'txn_date' => $date, 'bank_account_id' => $b->id,
                'txn_type' => $type, 'amount' => $amount, 'reference' => $reference, 'related_module' => $module,
                'related_id' => $refId, 'narration' => $narration, 'created_by' => Auth::id(),
                'currency_code' => $ccy, 'exchange_rate' => $rate ?? CurrencyService::rate($ccy)]);
            $delta = in_array($type, ['deposit', 'transfer-in', 'interest']) ? $amount : -$amount;
            $b->increment('current_balance', $delta);
            return $t;
        });
    }
}
