<?php

namespace Modules\ClinicCore\Entities;

use Illuminate\Database\Eloquent\Model;

/**
 * One movement row in the controlled-drug register. Rows are insert-only —
 * corrections happen via a reversing entry that references the original
 * serial in `notes`, never by editing or deleting historical rows.
 */
class ClcControlledMovement extends Model
{
    protected $table = 'clc_controlled_stock_movements';
    protected $guarded = ['id'];

    protected $casts = [
        'expiry_date'   => 'date',
        'qty'           => 'decimal:3',
        'balance_after' => 'decimal:3',
    ];

    public function medicine()
    {
        return $this->belongsTo(ClcMedicine::class, 'medicine_id');
    }

    public function pet()
    {
        return $this->belongsTo(ClcPatient::class, 'patient_id');
    }

    public function owner()
    {
        return $this->belongsTo(\App\Contact::class, 'contact_id');
    }

    public function appointment()
    {
        return $this->belongsTo(ClcAppointment::class, 'appointment_id');
    }

    public function doctor()
    {
        return $this->belongsTo(ClcDoctor::class, 'doctor_id');
    }

    public function witness()
    {
        return $this->belongsTo(\App\User::class, 'witness_user_id');
    }

    public function createdBy()
    {
        return $this->belongsTo(\App\User::class, 'created_by');
    }

    public static function reasonCodes(): array
    {
        return [
            'receive'      => ['label' => 'Stock received',  'direction' => 'in',  'color' => 'success'],
            'dispense'             => ['label' => 'Dispensed',           'direction' => 'out', 'color' => 'primary'],
            'dispense_unverified'  => ['label' => 'Dispensed (unverified)','direction' => 'out', 'color' => 'danger'],
            'return'       => ['label' => 'Patient return',  'direction' => 'in',  'color' => 'info'],
            'wastage'      => ['label' => 'Wastage',         'direction' => 'out', 'color' => 'warning'],
            'expiry'       => ['label' => 'Expired',         'direction' => 'out', 'color' => 'danger'],
            'transfer_in'  => ['label' => 'Transfer in',     'direction' => 'in',  'color' => 'success'],
            'transfer_out' => ['label' => 'Transfer out',    'direction' => 'out', 'color' => 'warning'],
            'adjust'       => ['label' => 'Manual adjust',   'direction' => 'adjust','color' => 'default'],
            'correction'   => ['label' => 'Correction',      'direction' => 'adjust','color' => 'default'],
        ];
    }

    /**
     * Atomically allocate the next serial number for a business. Format:
     * CR-{businessId}-{0000001}
     */
    public static function nextSerial(int $businessId): string
    {
        return \DB::transaction(function () use ($businessId) {
            $last = static::where('business_id', $businessId)
                ->lockForUpdate()
                ->orderByDesc('id')->value('serial_no');
            $n = 1;
            if ($last && preg_match('/-(\d+)$/', $last, $m)) {
                $n = ((int) $m[1]) + 1;
            }
            return 'CR-' . $businessId . '-' . str_pad((string) $n, 7, '0', STR_PAD_LEFT);
        });
    }

    /**
     * Current running balance for a medicine (optionally narrowed to a batch).
     */
    public static function currentBalance(int $businessId, int $medicineId, ?string $batch = null): float
    {
        $q = static::where('business_id', $businessId)->where('medicine_id', $medicineId);
        if ($batch !== null) $q->where('batch_no', $batch);
        $latest = $q->orderByDesc('id')->first();
        return $latest ? (float) $latest->balance_after : 0.0;
    }
}
