<?php

namespace Modules\ClinicLab\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\DB;
use Modules\ClinicLab\Entities\CllTestCatalog;
use Modules\ClinicLab\Entities\CllTestCatalogAnalyte;
use Modules\ClinicLab\Entities\Services\CatalogService;

/**
 * Test catalogue CRUD (+ inline analytes) — /clinic-lab/catalog.
 * Permission-gated on cliniclab.catalog.manage. business_id scoped.
 */
class CllCatalogController extends Controller
{
    protected function guard(): void
    {
        if (! auth()->user() || ! auth()->user()->can('cliniclab.catalog.manage')) {
            abort(403, 'Unauthorized action.');
        }
    }

    protected function bizId()
    {
        return request()->session()->get('user.business_id');
    }

    public function index()
    {
        $this->guard();
        $tests = CllTestCatalog::forBusiness($this->bizId())
            ->withCount('analytes')
            ->orderBy('category')->orderBy('name')->get();
        return view('clinicLab::catalog.index', compact('tests'));
    }

    public function create(CatalogService $catalog)
    {
        $this->guard();
        $test = new CllTestCatalog(['is_active' => true]);
        $products = $catalog->labProducts((int) $this->bizId());
        return view('clinicLab::catalog.form', compact('test', 'products'));
    }

    public function edit($id, CatalogService $catalog)
    {
        $this->guard();
        $test = CllTestCatalog::forBusiness($this->bizId())->with('analytes')->findOrFail($id);
        $products = $catalog->labProducts((int) $this->bizId());
        return view('clinicLab::catalog.form', compact('test', 'products'));
    }

    public function store(Request $request, CatalogService $catalog)
    {
        $this->guard();
        $data = $this->validated($request);

        // Auto-link an ERP product by name when none chosen.
        if (empty($data['product_id'])) {
            $data['product_id'] = $catalog->findProductIdByName((int) $this->bizId(), $data['name']);
        }

        $data['business_id'] = $this->bizId();
        $test = CllTestCatalog::create($data);
        $this->syncAnalytes($test, $request);

        return redirect()->route('clinic-lab.catalog.index')
            ->with('status', ['success' => 1, 'msg' => 'Test added to the catalogue.']);
    }

    public function update($id, Request $request)
    {
        $this->guard();
        $test = CllTestCatalog::forBusiness($this->bizId())->findOrFail($id);
        $data = $this->validated($request, $test->id);
        $test->update($data);
        $this->syncAnalytes($test, $request);

        return redirect()->route('clinic-lab.catalog.index')
            ->with('status', ['success' => 1, 'msg' => 'Test updated.']);
    }

    public function destroy($id)
    {
        $this->guard();
        $test = CllTestCatalog::forBusiness($this->bizId())->findOrFail($id);
        $test->delete(); // analytes cascade
        return redirect()->route('clinic-lab.catalog.index')
            ->with('status', ['success' => 1, 'msg' => 'Test removed.']);
    }

    protected function validated(Request $request, ?int $ignoreId = null): array
    {
        $bizId = $this->bizId();
        $codeRule = 'required|string|max:40|unique:cll_test_catalog,code,' . ($ignoreId ?: 'NULL')
            . ',id,business_id,' . $bizId;

        return $request->validate([
            'code'             => $codeRule,
            'name'             => 'required|string|max:150',
            'category'         => 'required|in:hematology,chemistry,microbiology,serology,immunology,hormones,urine,stool,molecular,histopathology,other',
            'product_id'       => 'nullable|integer',
            'specimen_type'    => 'required|in:blood,serum,plasma,urine,stool,swab,csf,tissue,other',
            'container'        => 'nullable|string|max:60',
            'min_volume'       => 'nullable|string|max:30',
            'fasting_required' => 'nullable|boolean',
            'tat_hours'        => 'nullable|integer|min:0|max:2000',
            'method'           => 'nullable|string|max:120',
            'is_panel'         => 'nullable|boolean',
            'is_active'        => 'nullable|boolean',
        ]) + [
            'fasting_required' => $request->boolean('fasting_required'),
            'is_panel'         => $request->boolean('is_panel'),
            'is_active'        => $request->boolean('is_active'),
        ];
    }

    /** Replace the test's analyte rows from the posted inline table. */
    protected function syncAnalytes(CllTestCatalog $test, Request $request): void
    {
        $rows = $request->input('analytes', []);
        CllTestCatalogAnalyte::where('catalog_id', $test->id)->delete();

        $sort = 0;
        foreach ($rows as $row) {
            $code = trim((string) ($row['analyte_code'] ?? ''));
            $name = trim((string) ($row['analyte_name'] ?? ''));
            if ($code === '' && $name === '') {
                continue;
            }
            CllTestCatalogAnalyte::create([
                'catalog_id'    => $test->id,
                'analyte_code'  => $code ?: $name,
                'analyte_name'  => $name ?: $code,
                'unit'          => $row['unit'] ?? null,
                'ref_low'       => $this->num($row['ref_low'] ?? null),
                'ref_high'      => $this->num($row['ref_high'] ?? null),
                'critical_low'  => $this->num($row['critical_low'] ?? null),
                'critical_high' => $this->num($row['critical_high'] ?? null),
                'sort_order'    => $sort++,
            ]);
        }
    }

    protected function num($v): ?float
    {
        return ($v === null || $v === '') ? null : (is_numeric($v) ? (float) $v : null);
    }
}
