<?php

namespace Modules\MedBloodBank\Http\Controllers;

use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\MedBloodBank\Entities\MbbCrossmatch;
use Modules\MedBloodBank\Entities\MbbDonation;
use Modules\MedBloodBank\Services\BloodBankService;
use Yajra\DataTables\Facades\DataTables;

/**
 * Crossmatch screen (SIFA P6-T6). Recipients are ClinicCore ClcPatients (reused,
 * never a parallel table). The bag suggestion uses the service ABO/Rh
 * compatibility matrix + FIFO (earliest expiry first). Guarded by
 * mbb.crossmatch.perform.
 */
class CrossmatchController extends MedBloodBankBaseController
{
    public function __construct(private BloodBankService $service)
    {
    }

    public function index(Request $request)
    {
        $this->guard('mbb.crossmatch.perform');

        if ($request->ajax()) {
            $query = MbbCrossmatch::forBusiness($this->businessId())
                ->leftJoin('mbb_donations', 'mbb_donations.id', '=', 'mbb_crossmatches.donation_id')
                ->select([
                    'mbb_crossmatches.id',
                    'mbb_crossmatches.patient_id',
                    'mbb_crossmatches.patient_blood_group',
                    'mbb_crossmatches.method',
                    'mbb_crossmatches.result',
                    'mbb_crossmatches.performed_at',
                    'mbb_donations.bag_no',
                    'mbb_donations.blood_group as donor_group',
                ]);

            return DataTables::of($query)
                ->addColumn('patient_name', fn ($r) => e($this->patientName((int) $r->patient_id)))
                ->editColumn('method', fn ($r) => __('medbloodbank::lang.method_' . $r->method))
                ->editColumn('result', fn ($r) => $r->result
                    ? '<span class="label label-' . ($r->result === 'compatible' ? 'success' : 'danger') . '">'
                        . __('medbloodbank::lang.result_' . $r->result) . '</span>'
                    : '<span class="label label-default">—</span>')
                ->editColumn('performed_at', fn ($r) => $r->performed_at ? Carbon::parse($r->performed_at)->toDateString() : '—')
                ->rawColumns(['result'])
                ->make(true);
        }

        return view('medbloodbank::crossmatch.index', [
            'methods' => MbbCrossmatch::METHODS,
        ]);
    }

    /** Suggest compatible, FIFO-ordered available bags for a recipient group. */
    public function suggest(Request $request)
    {
        $this->guard('mbb.crossmatch.perform');

        $data = $this->validate($request, [
            'blood_group'  => 'required|in:' . implode(',', MbbDonation::BLOOD_GROUPS),
            'product_type' => 'nullable|in:' . implode(',', MbbDonation::PRODUCT_TYPES),
        ]);

        $bags = $this->service->suggestBags(
            $this->businessId(),
            $data['blood_group'],
            $data['product_type'] ?? null
        );

        return response()->json([
            'compatible_groups' => $this->service->compatibleDonorGroups($data['blood_group']),
            'bags' => $bags->map(fn ($b) => [
                'id'           => $b->id,
                'bag_no'       => $b->bag_no,
                'blood_group'  => $b->blood_group,
                'product_type' => __('medbloodbank::lang.product_' . $b->product_type),
                'expiry_date'  => optional($b->expiry_date)->toDateString(),
            ]),
        ]);
    }

    public function store(Request $request)
    {
        $this->guard('mbb.crossmatch.perform');

        $data = $this->validate($request, [
            'patient_id'          => 'required|integer',
            'donation_id'         => 'required|integer',
            'patient_blood_group' => 'nullable|in:' . implode(',', MbbDonation::BLOOD_GROUPS),
            'antibody_screen'     => 'nullable|in:negative,positive',
            'method'              => 'required|in:' . implode(',', MbbCrossmatch::METHODS),
            'result'              => 'required|in:' . implode(',', MbbCrossmatch::RESULTS),
        ]);

        $donation = MbbDonation::forBusiness($this->businessId())->find($data['donation_id']);
        if (! $donation) {
            return $this->fail(__('medbloodbank::lang.bag_not_found'));
        }

        try {
            MbbCrossmatch::create([
                'business_id'         => $this->businessId(),
                'patient_id'          => (int) $data['patient_id'],
                'donation_id'         => (int) $data['donation_id'],
                'patient_blood_group' => $data['patient_blood_group'] ?? null,
                'antibody_screen'     => $data['antibody_screen'] ?? null,
                'method'              => $data['method'],
                'result'              => $data['result'],
                'performed_by'        => $this->userId(),
                'performed_at'        => Carbon::now(),
            ]);

            // Reserve a compatible bag so it isn't double-committed before issue.
            if ($data['result'] === 'compatible' && $donation->status === 'available') {
                $donation->update(['status' => 'reserved']);
            }

            return $this->ok(__('medbloodbank::lang.crossmatch_saved'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    /** Patient picker: ClinicCore patients in this business (name via contact). */
    public function patients(Request $request)
    {
        $this->guard('mbb.crossmatch.perform');

        if (! Schema::hasTable('clc_patients')) {
            return response()->json([]);
        }

        $term = $request->input('q', '');

        $rows = DB::table('clc_patients')
            ->leftJoin('contacts', 'contacts.id', '=', 'clc_patients.contact_id')
            ->where('clc_patients.business_id', $this->businessId())
            ->where('clc_patients.status', 'active')
            ->where(function ($q) use ($term) {
                $q->where('clc_patients.patient_name', 'LIKE', '%' . $term . '%')
                  ->orWhere('contacts.name', 'LIKE', '%' . $term . '%')
                  ->orWhere('contacts.mobile', 'LIKE', '%' . $term . '%');
            })
            ->limit(20)
            ->get([
                'clc_patients.id',
                'clc_patients.patient_name',
                'clc_patients.bloodgroup',
                'contacts.name as contact_name',
                'contacts.mobile',
            ]);

        return response()->json($rows->map(function ($p) {
            $name = trim($p->patient_name ?? '') ?: trim($p->contact_name ?? '') ?: ('#' . $p->id);

            return [
                'id'          => $p->id,
                'text'        => $name . ($p->mobile ? ' (' . $p->mobile . ')' : ''),
                'blood_group' => $p->bloodgroup,
            ];
        }));
    }

    private function patientName(int $patientId): string
    {
        if (! Schema::hasTable('clc_patients')) {
            return '#' . $patientId;
        }

        $p = DB::table('clc_patients')
            ->leftJoin('contacts', 'contacts.id', '=', 'clc_patients.contact_id')
            ->where('clc_patients.id', $patientId)
            ->first(['clc_patients.patient_name', 'contacts.name as contact_name']);

        if (! $p) {
            return '#' . $patientId;
        }

        return trim($p->patient_name ?? '') ?: trim($p->contact_name ?? '') ?: ('#' . $patientId);
    }
}
