<?php
namespace App\Controllers;

use App\Traits\ResponseTrait;
use CodeIgniter\Controller;
use App\Libraries\ValueSet;
use App\Models\OrderTest\OrderTestModel;
use App\Models\Patient\PatientModel;
use App\Models\PatVisit\PatVisitModel;

class OrderTestController extends Controller {
    use ResponseTrait;

    protected $db;
    protected $model;
    protected $patientModel;
    protected $visitModel;
    protected $rules;

    public function __construct() {
        $this->db = \Config\Database::connect();
        $this->model = new OrderTestModel();
        $this->patientModel = new PatientModel();
        $this->visitModel = new PatVisitModel();
        $this->rules = [
            'InternalPID' => 'required|is_natural'
        ];
    }

    public function index() {
        $internalPID = $this->request->getVar('InternalPID');
        $includeDetails = $this->request->getVar('include') === 'details';

        try {
            if ($internalPID) {
                $rows = $this->model->getOrdersByPatient($internalPID);
            } else {
                $rows = $this->db->table('ordertest')
                    ->where('DelDate', null)
                    ->orderBy('TrnDate', 'DESC')
                    ->get()
                    ->getResultArray();
            }

            $rows = ValueSet::transformLabels($rows, [
                'Priority' => 'order_priority',
                'OrderStatus' => 'order_status',
            ]);

            if ($includeDetails && !empty($rows)) {
                foreach ($rows as &$row) {
                    $row['Specimens'] = $this->getOrderSpecimens($row['InternalOID']);
                    $row['Tests'] = $this->getOrderTests($row['InternalOID']);
                }
            }

            return $this->respond([
                'status' => 'success',
                'message' => 'Data fetched successfully',
                'data' => $rows
            ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

    public function show($orderID = null) {
        try {
            $row = $this->model->getOrder($orderID);
            if (empty($row)) {
                return $this->respond([
                    'status' => 'success',
                    'message' => 'Data not found.',
                    'data' => null
                ], 200);
            }

            $row = ValueSet::transformLabels([$row], [
                'Priority' => 'order_priority',
                'OrderStatus' => 'order_status',
            ])[0];

            // Include specimens and tests
            $row['Specimens'] = $this->getOrderSpecimens($row['InternalOID']);
            $row['Tests'] = $this->getOrderTests($row['InternalOID']);

            return $this->respond([
                'status' => 'success',
                'message' => 'Data fetched successfully',
                'data' => $row
            ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

    private function getOrderSpecimens($internalOID) {
        $specimens = $this->db->table('specimen s')
            ->select('s.*, cd.ConCode, cd.ConName')
            ->join('containerdef cd', 'cd.ConDefID = s.ConDefID', 'left')
            ->where('s.OrderID', $internalOID)
            ->where('s.EndDate IS NULL')
            ->get()
            ->getResultArray();
        
        // Get status for each specimen
        foreach ($specimens as &$specimen) {
            $status = $this->db->table('specimenstatus')
                ->where('SID', $specimen['SID'])
                ->where('EndDate IS NULL')
                ->orderBy('CreateDate', 'DESC')
                ->get()
                ->getRowArray();
            $specimen['Status'] = $status['SpcStatus'] ?? 'PENDING';
        }
        
        return $specimens;
    }

    private function getOrderTests($internalOID) {
        $tests = $this->db->table('patres pr')
            ->select('pr.*, tds.TestSiteCode, tds.TestSiteName, tds.TestType, tds.SeqScr AS TestSeqScr, tds.SeqRpt AS TestSeqRpt, tds.DisciplineID, d.DisciplineCode, d.DisciplineName, d.SeqScr AS DisciplineSeqScr, d.SeqRpt AS DisciplineSeqRpt')
            ->join('testdefsite tds', 'tds.TestSiteID = pr.TestSiteID', 'left')
            ->join('discipline d', 'd.DisciplineID = tds.DisciplineID', 'left')
            ->where('pr.OrderID', $internalOID)
            ->where('pr.DelDate IS NULL')
            ->orderBy('COALESCE(d.SeqScr, 999999) ASC')
            ->orderBy('COALESCE(d.SeqRpt, 999999) ASC')
            ->orderBy('COALESCE(tds.SeqScr, 999999) ASC')
            ->orderBy('COALESCE(tds.SeqRpt, 999999) ASC')
            ->orderBy('pr.ResultID ASC')
            ->get()
            ->getResultArray();

        foreach ($tests as &$test) {
            $discipline = [
                'DisciplineID' => $test['DisciplineID'] ?? null,
                'DisciplineCode' => $test['DisciplineCode'] ?? null,
                'DisciplineName' => $test['DisciplineName'] ?? null,
                'SeqScr' => $test['DisciplineSeqScr'] ?? null,
                'SeqRpt' => $test['DisciplineSeqRpt'] ?? null,
            ];

            $test['Discipline'] = $discipline;
            $test['SeqScr'] = $test['TestSeqScr'] ?? null;
            $test['SeqRpt'] = $test['TestSeqRpt'] ?? null;
            $test['DisciplineID'] = $discipline['DisciplineID'];

            unset($test['DisciplineCode'], $test['DisciplineName'], $test['DisciplineSeqScr'], $test['DisciplineSeqRpt'], $test['TestSeqScr'], $test['TestSeqRpt']);
        }
        unset($test);

        return $tests;
    }

    public function create() {
        $input = $this->request->getJSON(true);

        if (!$this->validateData($input, $this->rules)) {
            return $this->failValidationErrors($this->validator->getErrors());
        }

        try {
            if (!$this->patientModel->find($input['InternalPID'])) {
                return $this->failValidationErrors(['InternalPID' => 'Patient not found']);
            }

            if (!empty($input['PatVisitID'])) {
                $visit = $this->visitModel->find($input['PatVisitID']);
                if (!$visit) {
                    return $this->failValidationErrors(['PatVisitID' => 'Visit not found']);
                }
            }

            $orderID = $this->model->createOrder($input);
             
            // Fetch complete order details
            $order = $this->model->getOrder($orderID);
            $order['Specimens'] = $this->getOrderSpecimens($order['InternalOID']);
            $order['Tests'] = $this->getOrderTests($order['InternalOID']);

            // Rule engine triggers are fired at the test/result level (test_created, result_updated)

            return $this->respondCreated([
                'status' => 'success',
                'message' => 'Order created successfully',
                'data' => $order
            ], 201);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

    public function update($OrderID = null) {
        $input = $this->request->getJSON(true);

        if ($OrderID === null || $OrderID === '') {
            return $this->failValidationErrors(['OrderID' => 'OrderID is required']);
        }

        if (isset($input['OrderID']) && (string) $input['OrderID'] !== (string) $OrderID) {
            return $this->failValidationErrors(['OrderID' => 'OrderID in URL does not match body']);
        }

        try {
            $input['OrderID'] = $OrderID;
            $order = $this->model->getOrder($OrderID);
            if (!$order) {
                return $this->failNotFound('Order not found');
            }

            $updateData = [];
            if (isset($input['Priority'])) $updateData['Priority'] = $input['Priority'];
            if (isset($input['OrderStatus'])) $updateData['OrderStatus'] = $input['OrderStatus'];
            if (isset($input['OrderingProvider'])) $updateData['OrderingProvider'] = $input['OrderingProvider'];
            if (isset($input['DepartmentID'])) $updateData['DepartmentID'] = $input['DepartmentID'];
            if (isset($input['WorkstationID'])) $updateData['WorkstationID'] = $input['WorkstationID'];

            if (!empty($updateData)) {
                $this->model->update($order['InternalOID'], $updateData);
            }

            $updatedOrder = $this->model->getOrder($OrderID);
            $updatedOrder['Specimens'] = $this->getOrderSpecimens($updatedOrder['InternalOID']);
            $updatedOrder['Tests'] = $this->getOrderTests($updatedOrder['InternalOID']);

            return $this->respond([
                'status' => 'success',
                'message' => 'Order updated successfully',
                'data' => $updatedOrder
            ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

    public function delete() {
        $input = $this->request->getJSON(true);
        $orderID = $input['OrderID'] ?? null;

        if (empty($orderID)) {
            return $this->failValidationErrors(['OrderID' => 'OrderID is required']);
        }

        try {
            $order = $this->model->getOrder($orderID);
            if (!$order) {
                return $this->failNotFound('Order not found');
            }

            $this->model->softDelete($orderID);

            return $this->respondDeleted([
                'status' => 'success',
                'message' => 'Order deleted successfully'
            ]);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

    public function updateStatus() {
        $input = $this->request->getJSON(true);

        if (empty($input['OrderID']) || empty($input['OrderStatus'])) {
            return $this->failValidationErrors(['error' => 'OrderID and OrderStatus are required']);
        }

        $validStatuses = ['ORD', 'SCH', 'ANA', 'VER', 'REV', 'REP'];
        if (!in_array($input['OrderStatus'], $validStatuses)) {
            return $this->failValidationErrors(['OrderStatus' => 'Invalid status. Valid: ' . implode(', ', $validStatuses)]);
        }

        try {
            $order = $this->model->getOrder($input['OrderID']);
            if (!$order) {
                return $this->failNotFound('Order not found');
            }

            $this->model->updateStatus($input['OrderID'], $input['OrderStatus']);

            $updatedOrder = $this->model->getOrder($input['OrderID']);
            $updatedOrder['Specimens'] = $this->getOrderSpecimens($updatedOrder['InternalOID']);
            $updatedOrder['Tests'] = $this->getOrderTests($updatedOrder['InternalOID']);

            return $this->respond([
                'status' => 'success',
                'message' => 'Order status updated successfully',
                'data' => $updatedOrder
            ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }
}
