<?php

namespace Modules\MedAccounting\Entities;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;

/**
 * A journal entry header (SIFA P7-T2). A balanced double-entry document
 * (total_debit == total_credit) with draft/posted/reversed status. entry_no is
 * JE-{business_id}-{seq}. (reference_type, reference_id) is the idempotency key
 * AutoPostingService uses so a given ERP event is posted at most once.
 */
class MedaccJournalEntry extends Model
{
    protected $table = 'medacc_journal_entries';

    protected $guarded = ['id'];

    protected $casts = [
        'date'         => 'date',
        'posted_at'    => 'datetime',
        'reversed_at'  => 'datetime',
        'total_debit'  => 'decimal:4',
        'total_credit' => 'decimal:4',
    ];

    public const STATUSES = ['draft', 'posted', 'reversed'];

    public const REFERENCE_TYPES = ['invoice', 'payment', 'payroll', 'purchase', 'expense', 'manual'];

    public function scopeForBusiness($query, $businessId)
    {
        return $query->where('business_id', $businessId);
    }

    public function scopePosted($query)
    {
        return $query->where('status', 'posted');
    }

    public function lines()
    {
        return $this->hasMany(MedaccJournalLine::class, 'journal_entry_id');
    }

    public function reversalOf()
    {
        return $this->belongsTo(self::class, 'reversal_of_id');
    }

    /**
     * Next JE number for a business: JE-{business_id}-{zero-padded seq}. Derived
     * from the current max seq so it survives gaps and is safe under repair.
     */
    public static function nextEntryNo(int $businessId): string
    {
        $prefix = 'JE-' . $businessId . '-';

        $last = static::where('business_id', $businessId)
            ->where('entry_no', 'like', $prefix . '%')
            ->orderByRaw('LENGTH(entry_no) DESC')
            ->orderBy('entry_no', 'desc')
            ->value('entry_no');

        $seq = 0;
        if ($last && preg_match('/(\d+)$/', $last, $m)) {
            $seq = (int) $m[1];
        }

        return $prefix . str_pad((string) ($seq + 1), 6, '0', STR_PAD_LEFT);
    }

    /** True when an entry already exists for this ERP event (idempotency guard). */
    public static function existsForReference(int $businessId, string $refType, $refId): bool
    {
        return static::where('business_id', $businessId)
            ->where('reference_type', $refType)
            ->where('reference_id', $refId)
            ->whereIn('status', ['draft', 'posted'])
            ->exists();
    }
}
