<?php
# ────────────── lee_gwena ──────────────
# Code by: ashley_gwena
# Created: 2026-03-12
# Notes: Trust and business transfer workflow
# ───────────────────────────────────────

namespace App\Http\Controllers\Accounting;

use App\Http\Controllers\Controller;
use App\Http\Requests\ChartOfAccounts\TransferRequest;
use App\Models\Client;
use App\Models\MainAccount;
use App\Models\Transfer;
use App\Services\Accounting\BusinessLedgerService;
use Illuminate\Support\Facades\DB;

class TransferController extends Controller
{
    public function index()
    {
        $transfers = Transfer::query()->orderByDesc('id')->get();

        return view('chart_of_accounts.transfers.index', compact('transfers'));
    }

    public function create(BusinessLedgerService $ledgerService)
    {
        $clients = Client::query()
            ->orderBy('first_name')
            ->orderBy('last_name')
            ->get();
        $accounts = MainAccount::query()
            ->where('is_trust_account', true)
            ->orderBy('account_code')
            ->get();
        $businessBalance = $ledgerService->balance();

        return view('chart_of_accounts.transfers.create', compact('clients', 'accounts', 'businessBalance'));
    }

    public function store(TransferRequest $request)
    {
        $payload = $request->validated();

        $payload['destination_account_id'] = $payload['transfer_type'] === 'trust_to_trust'
            ? ($payload['destination_account_id'] ?? null)
            : null;

        if (!$this->accountsMatchTransferType($payload['transfer_type'], $payload['source_account_id'], $payload['destination_account_id'])) {
            return back()->withErrors(['transfer_type' => 'Transfer accounts do not match the selected transfer type.']);
        }

        if ($payload['transfer_type'] === 'trust_to_trust' && $payload['source_account_id'] === $payload['destination_account_id']) {
            return back()->withErrors(['destination_account_id' => 'Source and destination must differ.']);
        }

        if (!$this->hasSufficientTrustBalance($payload['client_id'], $payload['amount'], $payload['transfer_type'])) {
            return back()->withErrors(['amount' => 'Insufficient client trust balance.']);
        }

        $transfer = Transfer::query()->create([
            ...$payload,
            'status' => 'pending',
            'transfer_date' => now()->toDateString(),
            'created_by' => auth()->id(),
            'updated_by' => auth()->id(),
        ]);

        return redirect()->route('transfers.show', $transfer)->with('success', 'Transfer submitted for approval.');
    }

    public function show(Transfer $transfer)
    {
        $preview = $this->journalPreview($transfer);

        return view('chart_of_accounts.transfers.show', compact('transfer', 'preview'));
    }

    public function edit(Transfer $transfer, BusinessLedgerService $ledgerService)
    {
        if ($transfer->status !== 'pending') {
            return back()->withErrors(['transfer' => 'Only pending transfers can be edited.']);
        }

        $clients = Client::query()
            ->orderBy('first_name')
            ->orderBy('last_name')
            ->get();
        $accounts = MainAccount::query()
            ->where('is_trust_account', true)
            ->orderBy('account_code')
            ->get();
        $businessBalance = $ledgerService->balance();

        return view('chart_of_accounts.transfers.edit', compact('transfer', 'clients', 'accounts', 'businessBalance'));
    }

    public function update(TransferRequest $request, Transfer $transfer)
    {
        if ($transfer->status !== 'pending') {
            return back()->withErrors(['transfer' => 'Only pending transfers can be updated.']);
        }

        $payload = $request->validated();
        $payload['destination_account_id'] = $payload['transfer_type'] === 'trust_to_trust'
            ? ($payload['destination_account_id'] ?? null)
            : null;

        if (!$this->accountsMatchTransferType($payload['transfer_type'], $payload['source_account_id'], $payload['destination_account_id'])) {
            return back()->withErrors(['transfer_type' => 'Transfer accounts do not match the selected transfer type.']);
        }

        if ($payload['transfer_type'] === 'trust_to_trust' && $payload['source_account_id'] === $payload['destination_account_id']) {
            return back()->withErrors(['destination_account_id' => 'Source and destination must differ.']);
        }

        if (!$this->hasSufficientTrustBalance($payload['client_id'], $payload['amount'], $payload['transfer_type'])) {
            return back()->withErrors(['amount' => 'Insufficient client trust balance.']);
        }

        $transfer->update([
            ...$payload,
            'updated_by' => auth()->id(),
        ]);

        return redirect()->route('transfers.show', $transfer)->with('success', 'Transfer updated.');
    }

    public function destroy(Transfer $transfer)
    {
        if ($transfer->status !== 'pending') {
            return back()->withErrors(['transfer' => 'Only pending transfers can be deleted.']);
        }

        $transfer->delete();

        return redirect()->route('transfers.index')->with('success', 'Transfer deleted.');
    }

    public function approve(Transfer $transfer)
    {
        $transfer->update([
            'status' => 'approved',
            'updated_by' => auth()->id(),
        ]);

        return back()->with('success', 'Transfer approved.');
    }

    public function post(Transfer $transfer)
    {
        if ($transfer->status !== 'approved') {
            return back()->withErrors(['transfer' => 'Transfer must be approved before posting.']);
        }

        $transfer->update([
            'status' => 'posted',
            'updated_by' => auth()->id(),
        ]);

        return back()->with('success', 'Transfer posted.');
    }

    public function reverse(Transfer $transfer)
    {
        $transfer->update([
            'status' => 'reversed',
            'updated_by' => auth()->id(),
        ]);

        return back()->with('success', 'Transfer reversed.');
    }

    private function hasSufficientTrustBalance(int $clientId, float $amount, string $transferType): bool
    {
        if ($transferType === 'business_to_trust') {
            return true;
        }

        $balance = DB::table('trust_ledger_entries as tle')
            ->join('client_trust_accounts as cta', 'cta.id', '=', 'tle.trust_account_id')
            ->where('cta.client_id', $clientId)
            ->selectRaw('COALESCE(SUM(debit - credit), 0) as balance')
            ->value('balance');

        return ($balance ?? 0) >= $amount;
    }

    private function journalPreview(Transfer $transfer): array
    {
        $entries = match ($transfer->transfer_type) {
            'trust_to_trust' => [
                ['account_id' => $transfer->source_account_id, 'label' => 'Trust Account', 'type' => 'credit', 'amount' => $transfer->amount],
                ['account_id' => $transfer->destination_account_id, 'label' => 'Trust Account', 'type' => 'debit', 'amount' => $transfer->amount],
            ],
            'business_to_trust' => [
                ['account_id' => null, 'label' => 'Business Ledger', 'type' => 'credit', 'amount' => $transfer->amount],
                ['account_id' => $transfer->source_account_id, 'label' => 'Trust Account', 'type' => 'debit', 'amount' => $transfer->amount],
            ],
            default => [
                ['account_id' => $transfer->source_account_id, 'label' => 'Trust Account', 'type' => 'credit', 'amount' => $transfer->amount],
                ['account_id' => null, 'label' => 'Business Ledger', 'type' => 'debit', 'amount' => $transfer->amount],
            ],
        };

        return ['entries' => $entries];
    }

    private function accountsMatchTransferType(string $type, int $sourceId, ?int $destinationId): bool
    {
        $source = MainAccount::query()->find($sourceId);
        if (!$source) {
            return false;
        }

        return match ($type) {
            'trust_to_business' => (bool) $source->is_trust_account,
            'business_to_trust' => (bool) $source->is_trust_account,
            'trust_to_trust' => $destinationId
                ? (bool) $source->is_trust_account && (bool) MainAccount::query()->find($destinationId)?->is_trust_account
                : false,
            default => false,
        };
    }
}
