<?php

namespace Modules\NX600Lab\Entities\Services;

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Schema;
use Modules\NX600Lab\Entities\NxModuleSetting;
use Modules\NX600Lab\Entities\NxTestSession;
use Modules\NX600Lab\Entities\NxWorklist;

/**
 * Bridge between the NX600Lab module and the VetClinic module.
 *
 * When VetClinic is installed the lab worklist can be raised directly against
 * a pet, and incoming device results are linked back to the pet's profile.
 * When VetClinic is absent every method degrades gracefully and the lab
 * module runs as a fully standalone system.
 */
class VetClinicSyncService
{
    /**
     * Is the VetClinic module physically present and migrated?
     */
    public static function vetClinicInstalled(): bool
    {
        try {
            return class_exists(\Modules\VetClinic\Entities\VcPet::class)
                && Schema::hasTable('vc_pets');
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * Is sync active for this business? (VetClinic present AND the toggle on.)
     */
    public static function syncEnabled(?int $businessId): bool
    {
        if (!$businessId || !static::vetClinicInstalled()) {
            return false;
        }
        return (string) NxModuleSetting::get($businessId, 'vetclinic_sync', '1') === '1';
    }

    /**
     * Translate a VetClinic pet's textual species into the NX600 numeric code.
     */
    public static function speciesCodeForPet($pet, ?int $businessId): int
    {
        $species = strtolower(trim((string) ($pet->species ?? '')));
        if ($species === '') {
            return 99;
        }

        foreach (NxModuleSetting::speciesMap($businessId) as $code => $label) {
            if (strtolower(trim((string) $label)) === $species) {
                return (int) $code;
            }
        }

        // Unmapped species — fall back to "Other" (99) if defined, else 0.
        return array_key_exists(99, NxModuleSetting::speciesMap($businessId)) ? 99 : 0;
    }

    /**
     * Translate a VetClinic gender enum into the NX600 sex code.
     */
    public static function sexCode(?string $gender): int
    {
        return match (strtolower((string) $gender)) {
            'male'   => 0,
            'female' => 1,
            default  => 9,
        };
    }

    /**
     * Whole-year age from a date of birth (999 = unknown).
     */
    public static function ageFromDob($dob): int
    {
        if (empty($dob)) {
            return 999;
        }
        try {
            $age = Carbon::parse($dob)->age;
            return ($age >= 0 && $age < 999) ? $age : 999;
        } catch (\Throwable $e) {
            return 999;
        }
    }

    /**
     * Build the worklist field set for a given pet.
     *
     * @return array{patient_id:string,patient_name:string,species:int,sex:int,age:int,contact_id:?int,vc_pet_id:int}
     */
    public static function worklistFieldsForPet($pet, ?int $businessId): array
    {
        $code = $pet->serial_no ?: ($pet->microchip_no ?: ('P' . $pet->id));

        return [
            'patient_id'   => substr((string) $code, 0, 13),
            'patient_name' => substr((string) $pet->petname, 0, 60),
            'species'      => static::speciesCodeForPet($pet, $businessId),
            'sex'          => static::sexCode($pet->gender ?? null),
            'age'          => static::ageFromDob($pet->dob ?? null),
            'contact_id'   => $pet->contact_id ?? null,
            'vc_pet_id'    => $pet->id,
        ];
    }

    /**
     * After a device result lands, inherit the pet linkage from the matching
     * worklist entry so the session shows up on the pet's profile.
     */
    public static function applyWorklistLinkage(NxTestSession $session): void
    {
        if (!static::vetClinicInstalled()) {
            return;
        }

        // Primary match: the worklist entry the device pulled this sample from.
        $worklist = NxWorklist::withTrashed()
            ->where('sample_no', $session->sample_no)
            ->whereNotNull('vc_pet_id')
            ->latest('id')
            ->first();

        // Fallback: match by the patient code that was sent to the device,
        // in case the analyzer reported results under a different sample number.
        if (!$worklist && !empty($session->patient_id)) {
            $worklist = NxWorklist::withTrashed()
                ->where('patient_id', $session->patient_id)
                ->whereNotNull('vc_pet_id')
                ->latest('id')
                ->first();
        }

        if (!$worklist) {
            return;
        }

        $session->forceFill([
            'business_id' => $session->business_id ?: $worklist->business_id,
            'contact_id'  => $worklist->contact_id,
            'vc_pet_id'   => $worklist->vc_pet_id,
        ])->save();
    }

    /**
     * Pets belonging to a business, for owner/pet picker dropdowns.
     */
    public static function petsForBusiness(int $businessId)
    {
        if (!static::vetClinicInstalled()) {
            return collect();
        }
        return \Modules\VetClinic\Entities\VcPet::where('business_id', $businessId)
            ->where('status', 'active')
            ->orderBy('petname')
            ->get();
    }

    /**
     * Pets belonging to a single owner contact.
     */
    public static function petsForOwner(int $businessId, int $contactId)
    {
        if (!static::vetClinicInstalled()) {
            return collect();
        }
        return \Modules\VetClinic\Entities\VcPet::where('business_id', $businessId)
            ->where('contact_id', $contactId)
            ->orderBy('petname')
            ->get();
    }
}
