<?php

namespace api\modules\v1\controllers;

use api\components\Controller;
use Yii;
use api\models\search\LisResultSearch;
use common\models\HisRequest;
use common\models\RiaFrmHVLAB;
use common\models\RiaLabD;
use OpenApi\Annotations as OA;
use yii\helpers\Json;

/**
 * @OA\Tag(
 *   name="LisResult",
 *   description="LIS Result endpoints"
 * )
 */
class LisResultControllerOld extends Controller
{

    /**
     * @OA\Get(
     *   path="/lis-result",
     *   summary="Get list LIS Result",
     *   operationId="LisResultIndex",
     *   tags={"LisResult"},
     *   security={{"bearerAuth": {}}},
     *   @OA\Response(
     *     response=200,
     *     description="OK",
     *     @OA\JsonContent(
     *       type="object",
     *       @OA\Property(property="count", type="integer", example=10),
     *       @OA\Property(
     *         property="dataModels",
     *         type="array",
     *         @OA\Items(ref="#/components/schemas/LisResult")
     *       ),
     *       @OA\Property(property="total", type="integer", example=100)
     *     )
     *   ),
     *   @OA\Response(
     *     response=401,
     *     description="Unauthorized",
     *     @OA\JsonContent(ref="#/components/schemas/Unauthorized")
     *   )
     * )
     */


    /*public function actionIndex()
    {
        $search['LisResultSearch'] = Yii::$app->request->queryParams;
        $searchModel = new LisResultSearch();
        $dataProvider = $searchModel->search($search);

        return $this->apiCollection([
            'count' => $dataProvider->count,
            'dataModels' => $dataProvider->models,
        ], $dataProvider->totalCount);
    }*/



    public function actionCreate()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        /*if (empty(Yii::$app->request->rawBody)) {
      return ['message' => 'ไม่มีข้อมูล'];
    }*/

        //$data = Json::decode(Yii::$app->request->rawBody, true);
        //$labNo = $data['HisRequest']['LabNo'] ?? null;

        /*$hisReq = HisRequest::findOne(['LabNo' => $labNo]);
    if (!$hisReq) return ['message' => 'ไม่พบ HisRequest'];*/

        /*$RiaFrmH = RiaFrmHVLAB::find()
      ->where(['PatHN' => $hisReq->HN, 'CustID' => $hisReq->CustomerCode])
      ->one();*/

        //$RiaLabD = RiaLabD::find()->leftJoin([''])->where(['RiaHDocID' => $RiaFrmH->RiaHDocID])->all();
        $RiaLabD = RiaLabD::find()
            ->alias('d')
            //->select(['h.RiaHDocID', 'h.RiaHDocDate'])
            ->leftJoin(['h' => RiaFrmHVLAB::tableName()], 'd.RiaHDocID = h.RiaHDocID')
            ->where([
                'h.CustID' => '1087',
                'd.RiaLDApprvFlag' => 1,
            ])
            //->limit('20')
            ->andWhere(['>', 'h.RiaHDocDate', '2025-10-20'])
            //->groupBy(['h.RiaHDocID', 'h.RiaHDocDate'])
            ->orderBy(['h.RiaHDocDate' => SORT_DESC])
            ->asArray()
            ->all();

        // ===== สร้าง TestList (พร้อม ResultList ภายในแต่ละ Test) =====
        $testList = [];
        $baseDir = 'https://report.prolab.co.th/api/get_gems_data.php';
        $timestamp = date('YmdHis');

        foreach ($RiaLabD as $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows['RiaHDocID']]);
            $hisReq = null;
            if ($riaH) {
                $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();
            }

            $testList[] = [
                'LabNo'    => $hisReq->LabNo,
                'RequestRemark' => '',
                'ResultStatus' => '',
                'TestList' =>
                [
                    'TestCode'               => (string)$rows['LabID'],
                    'TestName'               => (string)$rows['LabName'],
                    'TestRemark'             => '',
                    'InformCriticalByCode'   => '',
                    'InformCriticalBy'       => '',
                    'InformCriticalTo'       => '',
                    'InformCriticalDateTime' => '',
                    'CriticalFlag'           => '',
                    'ReportResultByCode'     => '',
                    'ReportResultBy'         => (string)($rows['RiaLDUserNameEntry'] ?? ''),
                    'ReportResultDateTime'   => $this->dateTimeFormat($rows['RiaLDUserDateEntry']),
                    'ApproveResultByCode'    => '',
                    'ApproveResultBy'        => (string)($rows['RiaLDUserNameAppr'] ?? ''),
                    'ApproveResultDateTime'  => $this->dateTimeFormat($rows['RiaLDUserDateAppr']),
                    'FileName'               => '',
                    'Filebase64'             => '',
                    'ResultList'             => [
                        'ResultCode'     => (string)$rows['LabID'],
                        'ResultName'     => (string)$rows['LabName'],
                        'ResultValue'    => (string)($rows['RiaLDRsltAnal'] ?? ''),
                        'ResultUnit'     => (string)($rows['LabUnit'] ?? ''),
                        'ResultFlag'     => '',
                        'ReferenceRange' => (string)($rows['LabNormal1'] ?? ''),
                        'ResultRemark'   => '',
                    ],
                ]
            ];
        }

        return $testList;

        // ===== ดึง token จาก DB (หรือขอใหม่ถ้ายังไม่มี/ใกล้หมดอายุ) =====
        $cfg = Yii::$app->params['external_api'] ?? null;
        if (!$cfg) {
            return ['error' => 'ค่าการตั้งค่า external_api ไม่ถูกต้อง กรุณาตั้งค่าใน config/params.php'];
        }

        $apiName = 'external_api';
        $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one();
        $token = null;
        $nowTs = time();
        $refreshMargin = $cfg['refreshMargin'] ?? 300; // วินาทีก่อนหมดอายุที่ถือว่าใกล้หมด

        $needRequest = false;
        if (!$tokenModel) {
            $needRequest = true;
        } else {
            $expiresTs = strtotime($tokenModel->expires_at);
            if ($expiresTs <= ($nowTs + $refreshMargin)) {
                $needRequest = true;
            } else {
                $token = $tokenModel->token;
            }
        }

        if ($needRequest) {
            $tokenResp = $this->getApiToken($cfg['tokenUrl'], $cfg['username'], $cfg['password']);
            if (!$tokenResp['ok']) {
                if ($tokenModel && $tokenModel->token) {
                    $token = $tokenModel->token; // ใช้ token เก่าเป็น fallback
                } else {
                    return ['error' => 'ไม่สามารถขอ Token ได้', 'details' => $tokenResp];
                }
            } else {
                $token = $tokenResp['token'];
                $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600);
                if (!$tokenModel) {
                    $tokenModel = new ApiToken();
                    $tokenModel->api_name = $apiName;
                }
                $tokenModel->token = $token;
                $tokenModel->expires_at = $expiresAt;
                $tokenModel->save(false);
            }
        }

        // ===== ส่งด้วย cURL =====
        $url = $cfg['receiveUrl'];
        $response = $this->postJsonCurl($url, $payload, [
            'Authorization' => "Bearer {$token}",
        ]);

        if (isset($response['http_code']) && $response['http_code'] == 401) {
            return [
                'sent_payload' => $payload,
                'response' => $response,
                'note' => 'API ตอบ 401 Unauthorized — token อาจหมดอายุหรือไม่ถูกต้อง. ตรวจสอบ cron job token/refresh หรือ run php yii token/refresh',
            ];
        }

        return ['sent_payload' => $payload, 'response' => $response];
    }

    /** helper: แปลง datetime เป็นรูปแบบ dd/mm/YYYY H:i:s */
    public function dateTimeFormat($value)
    {
        if (empty($value)) return '';
        try {
            $dt = new \DateTime($value);
            return $dt->format('d/m/Y H:i:s');
        } catch (\Throwable $e) {
            return '';
        }
    }


    public function actionCreate()
    {

        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        $baseDir = 'https://report.prolab.co.th/api/get_gems_data.php';
        $RiaLabD = RiaLabD::find()
            ->alias('d')
            ->leftJoin(['h' => RiaFrmHVLAB::tableName()], 'd.RiaHDocID = h.RiaHDocID')
            ->where([
                'h.CustID' => '1087',
                'd.RiaLDApprvFlag' => 1,
            ])
            ->andWhere(['>', 'h.RiaHDocDate', '2025-10-20'])
            ->orderBy(['h.RiaHDocDate' => SORT_DESC])
            ->asArray()
            ->all();

        $testList = [];
        $labFileCounter = [];

        foreach ($RiaLabD as $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows['RiaHDocID']]);
            $hisReq = $riaH ? HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one() : null;

            $fileBase64 = '';
            $fileName = '';
            $realFileNames = []; // เก็บชื่อไฟล์จริงจาก API

            if ($hisReq) {
                $labNo = $hisReq->LabNo;
                $timestamp = date('dmYHis');

                if (!isset($labFileCounter[$labNo])) {
                    $labFileCounter[$labNo] = 1;
                } else {
                    $labFileCounter[$labNo]++;
                }
                $seq = $labFileCounter[$labNo];

                // เรียก API ตรวจสอบไฟล์
                $payloadCheck = [
                    'mode' => 'writeresult',
                    'LIS_Brand' => 'SIKARIN',
                    'CustID' => $riaH->CustID,
                    'LabNo' => $hisReq->LabNo,
                ];

                $ch = curl_init($baseDir);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_POST, true);
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payloadCheck));
                curl_setopt($ch, CURLOPT_HTTPHEADER, [
                    'Content-Type: application/json',
                    'Accept: application/json',
                ]);
                $response = curl_exec($ch);
                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                curl_close($ch);

                // แสดงชื่อไฟล์จริงและ response
                echo "<pre>LabNo: {$labNo}\nHTTP Code: {$httpCode}\nResponse:\n";
                var_dump($response);
                echo "</pre>";

                if ($httpCode == 200 && !empty($response)) {
                    $filesArray = json_decode($response, true);
                    if (!empty($filesArray)) {
                        foreach ($filesArray as $f) {
                            if (!empty($f['FileName'])) {
                                $realFileNames[] = $f['FileName'];
                            }
                        }

                        // ตั้งชื่อไฟล์ใหม่สำหรับ JSON
                        $fileName = "{$labNo}_{$timestamp}_{$seq}.pdf";

                        // แปลง Base64 ตัวอย่างจากไฟล์แรก
                        if (!empty($filesArray[0]['content'])) {
                            $fileBase64 = base64_encode($filesArray[0]['content']);
                        }
                    }
                }
            }

            $testList[] = [
                'LabNo' => $hisReq->LabNo ?? '',
                'RequestRemark' => '',
                'ResultStatus' => '',
                'TestList' => [
                    'TestCode' => (string)$rows['LabID'],
                    'TestName' => (string)$rows['LabName'],
                    'TestRemark' => '',
                    'ReportResultBy' => (string)($rows['RiaLDUserNameEntry'] ?? ''),
                    'ReportResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateEntry']),
                    'ApproveResultBy' => (string)($rows['RiaLDUserNameAppr'] ?? ''),
                    'ApproveResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateAppr']),
                    'FileName' => '',
                    'Filebase64' => '',
                    'RealFileNames' => $realFileNames, // แสดงชื่อไฟล์จริงจาก API
                    'ResultList' => [
                        'ResultCode' => (string)$rows['LabID'],
                        'ResultName' => (string)$rows['LabName'],
                        'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''),
                        'ResultUnit' => (string)($rows['LabUnit'] ?? ''),
                        'ReferenceRange' => (string)($rows['LabNormal1'] ?? ''),
                        'ResultRemark' => '',
                    ],
                ]
            ];
        }

        return $testList;

        // ===== สร้าง payload =====
        /*$payload = [
      'LabResults' => $testList,
    ];*/

        // ===== ดึง token จาก DB หรือขอใหม่ =====
        $cfg = Yii::$app->params['external_api'] ?? null;
        if (!$cfg) {
            return ['error' => 'ค่าการตั้งค่า external_api ไม่ถูกต้อง กรุณาตั้งค่าใน config/params.php'];
        }

        $apiName = 'external_api';
        $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one();
        $token = null;
        $nowTs = time();
        $refreshMargin = $cfg['refreshMargin'] ?? 300;

        $needRequest = false;
        if (!$tokenModel) {
            $needRequest = true;
        } else {
            $expiresTs = strtotime($tokenModel->expires_at);
            if ($expiresTs <= ($nowTs + $refreshMargin)) {
                $needRequest = true;
            } else {
                $token = $tokenModel->token;
            }
        }

        if ($needRequest) {
            $tokenResp = $this->getApiToken($cfg['tokenUrl'], $cfg['username'], $cfg['password']);
            if (!$tokenResp['ok']) {
                if ($tokenModel && $tokenModel->token) {
                    $token = $tokenModel->token;
                } else {
                    return ['error' => 'ไม่สามารถขอ Token ได้', 'details' => $tokenResp];
                }
            } else {
                $token = $tokenResp['token'];
                $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600);
                if (!$tokenModel) {
                    $tokenModel = new ApiToken();
                    $tokenModel->api_name = $apiName;
                }
                $tokenModel->token = $token;
                $tokenModel->expires_at = $expiresAt;
                $tokenModel->save(false);
            }
        }

        // ===== ส่ง payload ด้วย cURL =====
        $url = $cfg['receiveUrl'];
        $response = $this->postJsonCurl($url, $payload, [
            'Authorization' => "Bearer {$token}",
        ]);

        if (isset($response['http_code']) && $response['http_code'] == 401) {
            return [
                'sent_payload' => $payload,
                'response' => $response,
                'note' => 'API ตอบ 401 Unauthorized — token อาจหมดอายุหรือไม่ถูกต้อง. ตรวจสอบ cron job token/refresh หรือ run php yii token/refresh',
            ];
        }

        return ['sent_payload' => $payload, 'response' => $response];
    }

    /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */
    public function dateTimeFormat($value)
    {
        if (empty($value)) return '';
        try {
            $dt = new \DateTime($value);
            return $dt->format('d/m/Y H:i:s');
        } catch (\Throwable $e) {
            return '';
        }
    }
}


<?php

namespace api\modules\v1\controllers;

use api\components\Controller;
use common\models\ApiToken;
use Yii;
use common\models\HisRequest;
use common\models\LisApiD;
use common\models\LisApiSend;
use common\models\LisResult;
use common\models\LisResultSend;
use common\models\RiaFrmDVlab;
use common\models\RiaFrmHVLAB;
use common\models\RiaLabD;
use DateTime;
use OpenApi\Annotations as OA;
use yii\helpers\Json;

/**
 * @OA\Tag(
 *   name="LisResult",
 *   description="LIS Result endpoints"
 * )
 */
class LisResultController extends Controller
{

    /**
     * @OA\Get(
     *   path="/lis-result",
     *   summary="Get list LIS Result",
     *   operationId="LisResultIndex",
     *   tags={"LisResult"},
     *   security={{"bearerAuth": {}}},
     *   @OA\Response(
     *     response=200,
     *     description="OK",
     *     @OA\JsonContent(
     *       type="object",
     *       @OA\Property(property="count", type="integer", example=10),
     *       @OA\Property(
     *         property="dataModels",
     *         type="array",
     *         @OA\Items(ref="#/components/schemas/LisResult")
     *       ),
     *       @OA\Property(property="total", type="integer", example=100)
     *     )
     *   ),
     *   @OA\Response(
     *     response=401,
     *     description="Unauthorized",
     *     @OA\JsonContent(ref="#/components/schemas/Unauthorized")
     *   )
     * )
     */

    public function actionCreate()
    {
        /*$cfg = \Yii::$app->params['external_api'] ?? null;
        if (!$cfg) {
            echo "external_api config ไม่ถูกต้อง\n";
            return;
        }

        // ===== 1. ขอ/refresh token =====
        $token = $this->getToken($cfg);
        if (!$token) {
            echo "ไม่สามารถขอ token ได้\n";
            return;
        }*/

        // ===== 2. สร้าง payload $testList[] แบบเต็ม =====
        /*$RiaLabD = RiaLabD::find()
            ->alias('d')
            ->leftJoin(['h' => RiaFrmHVLAB::tableName()], 'd.RiaHDocID = h.RiaHDocID')
            ->where(['h.CustID' => '1087', 'd.RiaLDApprvFlag' => 1])
            ->andWhere(['>', 'h.RiaHDocDate', '2025-10-20'])
            ->orderBy(['h.RiaHDocDate' => SORT_DESC])
            ->asArray()
            ->all();*/

        $RiaLabD = LisApiD::find()->where(['RiaHDocID' => '252940159', 'CustID' => '1087', 'RiaLDApprvFlag' => '1'])->orderBy(['RiaHDocDate' => SORT_DESC])->all();

        foreach ($RiaLabD as $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows['RiaHDocID']]);
            $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

            $LisResult = LisResult::find()->where(['lab_no' => $hisReq->LabNo, 'test_code' => $rows->LabID, 'cust_id' => $riaH->CustID, 'hn' => $riaH->PatHN])->one();
            if (!$LisResult) {
                $LisResult = new LisResult([
                    'lab_no' => $hisReq->LabNo,
                    'riah_doc_id' => $rows->RiaHDocID,
                    'riah_doc_date' => $rows->RiaHDocDate,
                    'test_code' => $rows->LabID,
                    'test_name' => $rows->LabName,
                    'cust_id' => $riaH->CustID,
                    'hn' => $riaH->PatHN,
                    'result_value' => $rows->RiaLDRsltAnal ?? '',
                    'result_unit' => $rows->LabUnit ?? '',
                    'reference_range' => $rows->LabNormal1 ?? '',
                ]);
                $LisResult->save();
            }

            $testList[] = [
                'LabNo' => $hisReq->LabNo,
                'RequestRemark' => '',
                'ResultStatus' => '',
                'TestList' => [
                    'TestCode'               => (string)$rows->LabID,
                    'TestName'               => (string)$rows->LabName,
                    'TestRemark'             => '',
                    'InformCriticalByCode'   => '',
                    'InformCriticalBy'       => '',
                    'InformCriticalTo'       => '',
                    'InformCriticalDateTime' => '',
                    'CriticalFlag'           => '',
                    'ReportResultByCode'     => '',
                    'ReportResultBy'         => (string)($rows->RiaLDUserNameEntry ?? ''),
                    'ReportResultDateTime'   => $this->dateTimeFormat($rows->RiaLDUserDateEntry),
                    'ApproveResultByCode'    => '',
                    'ApproveResultBy'        => (string)($rows->RiaLDUserNameAppr ?? ''),
                    'ApproveResultDateTime'  => $this->dateTimeFormat($rows->RiaLDUserDateAppr),
                    'FileName'               => '',
                    'Filebase64'             => '',
                    'ResultList'             => [
                        'ResultCode'     => (string)$rows->LabID,
                        'ResultName'     => (string)$rows->LabName,
                        'ResultValue'    => (string)($rows->RiaLDRsltAnal ?? ''),
                        'ResultUnit'     => (string)($rows->LabUnit ?? ''),
                        'ResultFlag'     => '',
                        'ReferenceRange' => (string)($rows->LabNormal1 ?? ''),
                        'ResultRemark'   => '',
                    ],
                ]
            ];
        }

        /*$testList = [];
        foreach ($RiaLabD as $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows['RiaHDocID']]);
            $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

            $LisResult = LisResult::find()->where(['lab_no' => $hisReq->LabNo, 'test_code' => $rows['LabID'], 'cust_id' => $riaH->CustID, 'hn' => $riaH->PatHN])->one();
            if (!$LisResult) {
                $LisResult = new LisResult([
                    'lab_no' => $hisReq->LabNo,
                    'riah_doc_id' => $rows['RiaHDocID'],
                    'riah_doc_date' => $rows['RiaHDocDate'],
                    'test_code' => $rows['LabID'],
                    'test_name' => $rows['LabName'],
                    'cust_id' => $riaH->CustID,
                    'hn' => $riaH->PatHN,
                    'result_value' => $rows['RiaLDRsltAnal'],
                    'result_unit' => $rows['LabUnit'],
                    'reference_range' => $rows['LabNormal1'],
                ]);
                $LisResult->save();
            }

            $testList[] = [
                'LabNo' => $hisReq->LabNo,
                'RequestRemark' => '',
                'ResultStatus' => '',
                'TestList' => [
                    'TestCode'               => (string)$rows['LabID'],
                    'TestName'               => (string)$rows['LabName'],
                    'TestRemark'             => '',
                    'InformCriticalByCode'   => '',
                    'InformCriticalBy'       => '',
                    'InformCriticalTo'       => '',
                    'InformCriticalDateTime' => '',
                    'CriticalFlag'           => '',
                    'ReportResultByCode'     => '',
                    'ReportResultBy'         => (string)($rows['RiaLDUserNameEntry'] ?? ''),
                    'ReportResultDateTime'   => $this->dateTimeFormat($rows['RiaLDUserDateEntry']),
                    'ApproveResultByCode'    => '',
                    'ApproveResultBy'        => (string)($rows['RiaLDUserNameAppr'] ?? ''),
                    'ApproveResultDateTime'  => $this->dateTimeFormat($rows['RiaLDUserDateAppr']),
                    'FileName'               => '',
                    'Filebase64'             => '',
                    'ResultList'             => [
                        'ResultCode'     => (string)$rows['LabID'],
                        'ResultName'     => (string)$rows['LabName'],
                        'ResultValue'    => (string)($rows['RiaLDRsltAnal'] ?? ''),
                        'ResultUnit'     => (string)($rows['LabUnit'] ?? ''),
                        'ResultFlag'     => '',
                        'ReferenceRange' => (string)($rows['LabNormal1'] ?? ''),
                        'ResultRemark'   => '',
                    ],
                ]
            ];
        }*/

        return $testList;
    }
    // ===== 3. ส่ง payload ด้วย cURL =====
    /*foreach ($testList as $payload) {
            $response = $this->postJsonCurl($cfg['receiveUrl'], $payload, [
                'Authorization' => "Bearer {$token}"
            ]);

            if (isset($response['http_code']) && $response['http_code'] == 401) {
                echo "Unauthorized: token หมดอายุ\n";
                continue;
            }

            if (isset($response['success']) && $response['success'] === true) {
                $labNo = $payload['LabNo'] ?? null;
                if ($labNo) {
                    foreach ($payload['TestList'] as $test) {
                        $LisResult = LisResult::find()->where([
                            'lab_no' => $labNo,
                            'test_code' => $test['TestCode'],
                            'hn' => $test['PatHN'],
                            'cust_id' => $test['CustID'],
                        ])->one();
                        if ($LisResult) {
                            $LisResult->status = 1;
                            $LisResult->save(false);
                        }
                    }
                }
                echo "ส่งสำเร็จ LabNo: {$labNo}\n";
            }
        }
    }

    private function getToken(array $cfg): ?string
    {
        $tokenModel = ApiToken::find()->where(['api_name' => 'external_api'])->one();
        $nowTs = time();
        $refreshMargin = isset($cfg['refreshMargin']) ? (int)$cfg['refreshMargin'] : 300;
        $needRequest = !$tokenModel || strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin);

        if ($needRequest) {
            $tokenResp = $this->requestApiToken($cfg['tokenUrl'], $cfg['username'], $cfg['password']);
            if (!isset($tokenResp['ok']) || !$tokenResp['ok']) {
                // ถ้ามี token เก่า ให้ fallback, ถ้าไม่มี คืน null
                return $tokenModel->token ?? null;
            }

            $token = $tokenResp['token'];
            $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600);
            if (!$tokenModel) {
                $tokenModel = new ApiToken();
                $tokenModel->api_name = 'external_api';
            }
            $tokenModel->token = $token;
            $tokenModel->expires_at = $expiresAt;
            $tokenModel->save(false);
            return $token;
        }

        return $tokenModel->token ?? null;
    }

    private function postJsonCurl(string $url, $payload, array $headers = []): array
    {
        $ch = curl_init();
        $json = json_encode($payload);
        $curlHeaders = ['Content-Type: application/json'];
        foreach ($headers as $k => $v) {
            $curlHeaders[] = "{$k}: {$v}";
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
        // ถ้ต้องการ ignore SSL (ไม่แนะนำใน production) ให้เปิดด้านล่าง
        // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

        $respBody = curl_exec($ch);
        $errNo = curl_errno($ch);
        $errMsg = curl_error($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($errNo) {
            Yii::error("cURL error ({$errNo}): {$errMsg}");
        }

        $data = json_decode($respBody, true);
        if ($data === null) {
            // ถ้าไม่ใช่ JSON ให้เก็บ raw body ด้วย
            $data = ['raw' => $respBody];
        }

        return array_merge(['http_code' => $http_code, 'curl_errno' => $errNo, 'curl_error' => $errMsg], (array)$data);*/


    private function requestApiToken(string $url, string $username, string $password): array
    {
        $ch = curl_init();
        $payload = json_encode(['username' => $username, 'password' => $password]);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_TIMEOUT, 15);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);

        $resp = curl_exec($ch);
        $errNo = curl_errno($ch);
        $errMsg = curl_error($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($errNo) {
            Yii::error("requestApiToken curl error ({$errNo}): {$errMsg}");
            return ['ok' => false, 'http_code' => $http_code, 'body' => $resp];
        }

        $data = json_decode($resp, true);
        if ($http_code == 200 && isset($data['token'])) {
            return ['ok' => true, 'token' => $data['token'], 'expires_at' => $data['expires_at'] ?? null];
        }

        Yii::error("requestApiToken failed, http_code={$http_code}, body={$resp}");
        return ['ok' => false, 'http_code' => $http_code, 'body' => $resp];
    }

    /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */
    public function dateTimeFormat($value)
    {
        if (empty($value)) return '';
        try {
            $dt = new \DateTime($value);
            return $dt->format('d/m/Y H:i:s');
        } catch (\Throwable $e) {
            return '';
        }
    }

    public function actionCreate()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        // ===== ดึงข้อมูล Lab จาก LisApiD =====
        $RiaLabD = LisApiD::find()
            ->where(['RiaHDocID' => '252940159', 'CustID' => '1087', 'RiaLDApprvFlag' => '1'])
            ->orderBy(['RiaHDocDate' => SORT_DESC])
            ->all();

        $testList = [];
        $sendMapping = []; // เก็บ mapping RiaHDocID => payload index

        foreach ($RiaLabD as $i => $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
            $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

            $testList[] = [
                'LabNo'    => $hisReq->LabNo,
                'RequestRemark' => '',
                'ResultStatus' => '',
                'TestList' =>
                [
                    'TestCode'               => (string)$rows['LabID'],
                    'TestName'               => (string)$rows['LabName'],
                    'TestRemark'             => '',
                    'InformCriticalByCode'   => '',
                    'InformCriticalBy'       => '',
                    'InformCriticalTo'       => '',
                    'InformCriticalDateTime' => '',
                    'CriticalFlag'           => '',
                    'ReportResultByCode'     => '',
                    'ReportResultBy'         => (string)($rows['RiaLDUserNameEntry'] ?? ''),
                    'ReportResultDateTime'   => $this->dateTimeFormat($rows['RiaLDUserDateEntry']),
                    'ApproveResultByCode'    => '',
                    'ApproveResultBy'        => (string)($rows['RiaLDUserNameAppr'] ?? ''),
                    'ApproveResultDateTime'  => $this->dateTimeFormat($rows['RiaLDUserDateAppr']),
                    'FileName'               => '',
                    'Filebase64'             => '',
                    'ResultList'             => [
                        'ResultCode'     => (string)$rows['LabID'],
                        'ResultName'     => (string)$rows['LabName'],
                        'ResultValue'    => (string)($rows['RiaLDRsltAnal'] ?? ''),
                        'ResultUnit'     => (string)($rows['LabUnit'] ?? ''),
                        'ResultFlag'     => '',
                        'ReferenceRange' => trim(($rows['LabNormal1'] ?? '') . ' ' . ($rows['LabNormal2'] ?? '')),
                        'ResultRemark'   => '',
                    ],
                ]
            ];

            $sendMapping[$rows->RiaHDocID] = $i; // mapping ใช้ตรวจสอบส่งสำเร็จ
        }

        // ===== ตรวจสอบ config =====
        $cfg = Yii::$app->params['external_api'] ?? null;
        if (!$cfg) {
            return [
                'error' => 'Config external_api not found',
                'sent_payload' => $testList
            ];
        }

        if (empty($cfg['receiveUrl'])) {
            return [
                'error' => 'receiveUrl not configured',
                'config' => $cfg,
                'sent_payload' => $testList
            ];
        }

        // ===== ส่ง payload =====
        $tokenError = null;
        $token = $this->getValidApiToken($cfg, $tokenError);

        // ถ้าไม่มี token ให้ return error
        if (!$token) {
            return [
                'error' => 'Cannot get API token',
                'token_error' => $tokenError,
                'sent_payload' => $testList
            ];
        }

        $url = $cfg['receiveUrl'];

        // Debug: แสดง URL และ config
        $debugInfo = [
            'url' => $url,
            'tokenUrl' => $cfg['tokenUrl'] ?? 'not set',
            'token_exists' => !empty($token),
            'token_length' => strlen($token ?? ''),
            'payload_count' => count($testList),
        ];

        $response = $this->postJsonCurl($url, $testList, [
            'Authorization' => "Bearer {$token}",
        ]);

        $response['debug'] = $debugInfo;
        if ($tokenError) {
            $response['token_warning'] = $tokenError;
        }

        // ===== ตรวจสอบ response message =====
        $success = isset($response['message']) && strtolower($response['message']) === 'success';

        // เก็บ cust_id และ hn ที่ประมวลผล
        $processedPatients = [];

        // ===== สร้าง LisResult เฉพาะรายการที่ส่งสำเร็จ =====
        if ($success) {
            foreach ($RiaLabD as $rows) {
                $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
                $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

                // เก็บข้อมูล patient ที่ประมวลผล
                $patientKey = $riaH->CustID . '_' . $riaH->PatHN;
                if (!isset($processedPatients[$patientKey])) {
                    $processedPatients[$patientKey] = [
                        'cust_id' => $riaH->CustID,
                        'hn' => $riaH->PatHN,
                    ];
                }

                // สร้าง LisResult เฉพาะถ้า record ยังไม่มี
                $LisResult = LisResult::find()->where([
                    'lab_no' => $hisReq->LabNo,
                    'test_code' => $rows->LabID,
                    'cust_id' => $riaH->CustID,
                    'hn' => $riaH->PatHN
                ])->one();

                if (!$LisResult) {
                    $LisResult = new LisResult([
                        'lab_no' => $hisReq->LabNo,
                        'riah_doc_id' => $rows->RiaHDocID,
                        'riah_doc_date' => $rows->RiaHDocDate,
                        'test_code' => $rows->LabID,
                        'test_name' => $rows->LabName,
                        'cust_id' => $riaH->CustID,
                        'hn' => $riaH->PatHN,
                        'result_value' => $rows->RiaLDRsltAnal ?? '',
                        'result_unit' => $rows->LabUnit ?? '',
                        'reference_range' => trim(($rows->LabNormal1 ?? '') . ' ' . ($rows->LabNormal2 ?? '')),
                        'status' => 1,
                    ]);
                    $LisResult->save(false);
                } else {
                    $LisResult->status = 1;
                    $LisResult->save(false);
                }
            }
        }

        // ===== Retry สำหรับ record ที่ยัง status = NULL (ส่งจนกว่าจะสำเร็จ) =====
        foreach ($processedPatients as $patient) {
            $toRetry = LisApiSend::find()->where([
                'cust_id' => $patient['cust_id'],
                'hn' => $patient['hn'],
                'status' => null
            ])->all();

            foreach ($toRetry as $LisApiSend) {
                $sent = false;

                // Retry จนกว่าจะส่งสำเร็จ
                while (!$sent) {
                    $sent = $this->sendLisApiSend($LisApiSend);
                    if (!$sent) {
                        sleep(1); // delay 1 วินาที ก่อน retry
                    }
                }
            }
        }
        return ['sent_payload' => $testList, 'response' => $response];
    }

    // ===== helper ดึง token =====
    protected function getValidApiToken($cfg, &$tokenError = null)
    {
        $apiName = 'external_api';
        $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one();
        $token = null;
        $nowTs = time();
        $refreshMargin = $cfg['refreshMargin'] ?? 300;

        $needRequest = !$tokenModel || strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin);
        if ($needRequest) {
            $tokenResp = $this->getApiToken($cfg['tokenUrl'], $cfg['username'], $cfg['password']);

            // ถ้าขอ token ไม่สำเร็จ
            if (!$tokenResp['ok']) {
                $tokenError = [
                    'message' => 'Failed to get token from API',
                    'tokenUrl' => $cfg['tokenUrl'],
                    'error' => $tokenResp['error'] ?? 'Unknown error',
                    'http_code' => $tokenResp['http_code'] ?? 0,
                ];

                // ถ้ามี token เก่าอยู่ ให้ใช้ต่อ
                if ($tokenModel) {
                    return $tokenModel->token;
                }

                return null;
            }

            $token = $tokenResp['token'];
            $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600);

            if (!$tokenModel) {
                $tokenModel = new ApiToken();
                $tokenModel->api_name = $apiName;
            }
            $tokenModel->token = $token;
            $tokenModel->expires_at = $expiresAt;
            $tokenModel->save(false);
        } else {
            $token = $tokenModel->token;
        }

        return $token;
    }

    /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */
    public function dateTimeFormat($value)
    {
        if (empty($value)) return '';
        try {
            $dt = new \DateTime($value);
            return $dt->format('d/m/Y H:i:s');
        } catch (\Throwable $e) {
            return '';
        }
    }

    /** helper: ขอ token จาก API */
    protected function getApiToken($tokenUrl, $username, $password)
    {
        $ch = curl_init($tokenUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
            'username' => $username,
            'password' => $password,
        ]));
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        curl_close($ch);

        if ($httpCode == 200 && $response) {
            $data = json_decode($response, true);
            return [
                'ok' => true,
                'token' => $data['token'] ?? null,
                'expires_at' => $data['expires_at'] ?? null,
            ];
        }

        return [
            'ok' => false,
            'error' => $curlError,
            'http_code' => $httpCode
        ];
    }

    /** helper: ส่ง JSON ผ่าน cURL */
    protected function postJsonCurl($url, $payload, $headers = [])
    {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // ปิดการตรวจสอบ SSL cert
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); // timeout 30 วินาที
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // connection timeout 10 วินาที

        $curlHeaders = ['Content-Type: application/json'];
        foreach ($headers as $key => $value) {
            $curlHeaders[] = "$key: $value";
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        $curlErrno = curl_errno($ch);
        curl_close($ch);

        return [
            'http_code' => $httpCode,
            'response' => $response,
            'message' => json_decode($response, true)['message'] ?? null,
            'curl_error' => $curlError,
            'curl_errno' => $curlErrno,
            'url' => $url,
        ];
    }

    /** helper: ส่ง LisResult เดี่ยว ๆ */
    protected function sendLisResult($LisResult)
    {
        $cfg = Yii::$app->params['external_api'] ?? null;
        if (!$cfg) return false;

        $token = $this->getValidApiToken($cfg);

        $payload = [[
            'LabNo' => $LisResult->lab_no,
            'RequestRemark' => '',
            'ResultStatus' => '',
            'TestList' => [
                'TestCode' => $LisResult->test_code,
                'TestName' => $LisResult->test_name,
                'TestRemark' => '',
                'InformCriticalByCode' => '',
                'InformCriticalBy' => '',
                'InformCriticalTo' => '',
                'InformCriticalDateTime' => '',
                'CriticalFlag' => '',
                'ReportResultByCode' => '',
                'ReportResultBy' => '',
                'ReportResultDateTime' => '',
                'ApproveResultByCode' => '',
                'ApproveResultBy' => '',
                'ApproveResultDateTime' => '',
                'FileName' => '',
                'Filebase64' => '',
                'ResultList' => [
                    'ResultCode' => $LisResult->test_code,
                    'ResultName' => $LisResult->test_name,
                    'ResultValue' => $LisResult->result_value ?? '',
                    'ResultUnit' => $LisResult->result_unit ?? '',
                    'ResultFlag' => '',
                    'ReferenceRange' => $LisResult->reference_range ?? '',
                    'ResultRemark' => '',
                ],
            ]
        ]];

        $response = $this->postJsonCurl($cfg['receiveUrl'], $payload, [
            'Authorization' => "Bearer {$token}",
        ]);

        if (isset($response['http_code']) && $response['http_code'] == 200) {
            $LisResult->status = 1;
            $LisResult->save(false);
            return true;
        }

        return false;
    }


     public function actionCreate()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        // ===== ดึงข้อมูล Lab =====
        $RiaLabD = LisApiD::find()
            ->where(['RiaHDocID' => '253000025', 'CustID' => '1087', 'RiaLDApprvFlag' => '1'])
            ->orderBy(['RiaHDocDate' => SORT_DESC])
            ->all();

        // ===== Group tests by LabNo =====
        $groupedByLabNo = $groupedByLabNo ?? [];
        $sendMapping = $sendMapping ?? [];
        $globalDebug = [];

        foreach ($RiaLabD as $i => $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
            $hisReq = HisRequest::find()->where(['LabNo' => 'OR25100600008', 'HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

            $fileBase64 = '';
            $fileName = '';
            $labNo = $hisReq->LabNo ?? '';
            $timestamp = date('dmYHis');

            // ดึงไฟล์จาก URL
            $docIdParam = $rows['RiaHDocID'] ?? '';
            $debug = [];

            if (!empty($docIdParam)) {
                $dirUrl = 'https://report.prolab.co.th/pdflink/' . $docIdParam . '/';
                
                // ลองหาไฟล์ตาม pattern ที่เป็นไปได้ (ขยายเพิ่มเติม)
                $possibleFiles = [];
                
                // Pattern: {docId}_X.pdf (X = 1-20)
                for ($i = 1; $i <= 20; $i++) {
                    $possibleFiles[] = $docIdParam . '_' . $i . '.pdf';
                }
                
                // Pattern อื่นๆ
                $possibleFiles[] = $docIdParam . '.pdf';
                $possibleFiles[] = 'report.pdf';
                $possibleFiles[] = 'result.pdf';

                foreach ($possibleFiles as $testFileName) {
                    $fileUrl = $dirUrl . $testFileName;
                    
                    // ลอง HEAD request ก่อนเพื่อเช็คว่าไฟล์มีอยู่
                    $ch = curl_init($fileUrl);
                    curl_setopt($ch, CURLOPT_NOBODY, true);
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
                    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
                    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
                    curl_exec($ch);
                    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                    curl_close($ch);

                    if ($httpCode == 200) {
                        $debug[] = "Found: {$testFileName}";
                        
                        // ดึงไฟล์จริง
                        $ch2 = curl_init($fileUrl);
                        curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
                        curl_setopt($ch2, CURLOPT_FOLLOWLOCATION, true);
                        curl_setopt($ch2, CURLOPT_TIMEOUT, 10);
                        curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false);
                        $content = curl_exec($ch2);
                        curl_close($ch2);

                        if (!empty($content)) {
                            $fileBase64 = base64_encode($content);
                            $ext = pathinfo($testFileName, PATHINFO_EXTENSION);
                            $fileName = $labNo . '_' . $timestamp . '.' . $ext;
                            $debug[] = "Success: Loaded (" . strlen($content) . " bytes)";
                            break; // ใช้ไฟล์แรกที่เจอ
                        }
                    }
                }

                if (empty($fileBase64)) {
                    $debug[] = "No file found in {$dirUrl} (checked " . count($possibleFiles) . " patterns)";
                }
            } else {
                $debug[] = "docIdParam empty";
            }

            // เก็บ debug
            $globalDebug[$docIdParam] = $debug;

            // สร้าง test item
            $testItem = [
                'TestCode' => (string)($rows['LabID'] ?? ''),
                'TestName' => (string)($rows['LabName'] ?? ''),
                'TestRemark' => '',
                'InformCriticalByCode' => '',
                'InformCriticalBy' => '',
                'InformCriticalTo' => '',
                'InformCriticalDateTime' => '',
                'CriticalFlag' => '',
                'ReportResultByCode' => '',
                'ReportResultBy' => (string)($rows['RiaLDUserNameEntry'] ?? ''),
                'ReportResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateEntry'] ?? ''),
                'ApproveResultByCode' => '',
                'ApproveResultBy' => (string)($rows['RiaLDUserNameAppr'] ?? ''),
                'ApproveResultDateTime' => $this->dateTimeFormat($rows['RiaLDUserDateAppr'] ?? ''),
                'FileName' => $fileName,
                'Filebase64' => $fileBase64,
                'ResultList' => [
                    [
                        'ResultCode' => (string)($rows['LabID'] ?? ''),
                        'ResultName' => (string)($rows['LabName'] ?? ''),
                        'ResultValue' => (string)($rows['RiaLDRsltAnal'] ?? ''),
                        'ResultUnit' => (string)($rows['LabUnit'] ?? ''),
                        'ResultFlag' => '',
                        'ReferenceRange' => trim(($rows->LabNormal1 ?? '') . ' ' . ($rows->LabNormal2 ?? '')),
                        'ResultRemark' => '',
                    ]
                ],
            ];

            // Group by LabNo
            if (!isset($groupedByLabNo[$labNo])) {
                $groupedByLabNo[$labNo] = [
                    'LabNo' => $labNo,
                    'RequestRemark' => '',
                    'ResultStatus' => '',
                    'TestList' => []
                ];
            }
            $groupedByLabNo[$labNo]['TestList'][] = $testItem;

            $sendMapping[$rows->RiaHDocID] = $i;
        }





           public function actionCreate()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        // ===== ดึงข้อมูล Lab =====
        $RiaLabD = LisApiD::find()
            ->where(['RiaHDocID' => '253010415', 'CustID' => '1087', 'RiaLDApprvFlag' => '1'])
            ->orderBy(['RiaHDocDate' => SORT_DESC])
            ->all();

        // ===== Group tests by LabNo =====
        $groupedByLabNo = $groupedByLabNo ?? [];
        $sendMapping = $sendMapping ?? [];
        $globalDebug = [];

        foreach ($RiaLabD as $i => $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
            $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

            $fileBase64 = '';
            $fileName = '';
            $labNo = $hisReq->LabNo ?? '';
            $timestamp = date('dmYHis');

            // ดึงไฟล์จาก URL
            $docIdParam = $rows['RiaHDocID'] ?? '';
            $debug = [];

            if (!empty($docIdParam)) {
                $dirUrl = 'https://report.prolab.co.th/pdflink/' . $docIdParam . '/';

                // ลองหาไฟล์ตาม pattern ที่เป็นไปได้
                $possibleFiles = [];

                // Pattern: {docId}_X.pdf (X = 1-20)
                for ($i = 1; $i <= 20; $i++) {
                    $possibleFiles[] = $docIdParam . '_' . $i . '.pdf';
                }

                // Pattern อื่นๆ
                $possibleFiles[] = $docIdParam . '.pdf';
                $possibleFiles[] = 'report.pdf';
                $possibleFiles[] = 'result.pdf';

                foreach ($possibleFiles as $fileName) {
                    $fileUrl = $dirUrl . $fileName;

                    // ลอง HEAD request ก่อนเพื่อเช็คว่าไฟล์มีอยู่
                    $ch = curl_init($fileUrl);
                    curl_setopt($ch, CURLOPT_NOBODY, true);
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
                    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
                    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
                    curl_exec($ch);
                    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                    curl_close($ch);

                    $debug[] = "Checking: {$fileName} -> HTTP {$httpCode}";

                    if ($httpCode == 200) {
                        // ดึงไฟล์จริง
                        $ch2 = curl_init($fileUrl);
                        curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
                        curl_setopt($ch2, CURLOPT_FOLLOWLOCATION, true);
                        curl_setopt($ch2, CURLOPT_TIMEOUT, 10);
                        curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false);
                        $content = curl_exec($ch2);
                        curl_close($ch2);

                        if (!empty($content)) {
                            $fileBase64 = base64_encode($content);
                            $ext = pathinfo($fileName, PATHINFO_EXTENSION);
                            $fileName = $labNo . '_' . $timestamp . '.' . $ext;
                            $debug[] = "Success: Loaded (" . strlen($content) . " bytes)";
                            break; // ใช้ไฟล์แรกที่เจอ
                        }
                    }
                }

                if (empty($fileBase64)) {
                    $debug[] = "No file found in {$dirUrl}";
                }
            } else {
                $debug[] = "docIdParam empty";
            }

            // เก็บ debug
            $globalDebug[$docIdParam] = $debug;

            // สร้าง test item
            $testItem = [
                'TestCode' => (string)($rows->LabID ?? ''),
                'TestName' => (string)($rows->LabName ?? ''),
                'TestRemark' => '',
                'InformCriticalByCode' => '',
                'InformCriticalBy' => '',
                'InformCriticalTo' => '',
                'InformCriticalDateTime' => '',
                'CriticalFlag' => '',
                'ReportResultByCode' => '',
                'ReportResultBy' => (string)($rows->RiaLDUserNameEntry ?? ''),
                'ReportResultDateTime' => $this->dateTimeFormat($rows->RiaLDUserDateEntry ?? ''),
                'ApproveResultByCode' => '',
                'ApproveResultBy' => (string)($rows->RiaLDUserNameAppr ?? ''),
                'ApproveResultDateTime' => $this->dateTimeFormat($rows->RiaLDUserDateAppr ?? ''),
                'Filedata' => [
                    [
                        'FileName' => $fileName, // เช่น LN_20241028102033.pdf
                        'Filebase64' => $fileBase64,
                    ]
                ],
                'ResultList' => [
                    [
                        'ResultCode' => (string)($rows->LabID ?? ''),
                        'ResultName' => (string)($rows->LabName ?? ''),
                        'ResultValue' => (string)($rows->RiaLDRsltAnal ?? ''),
                        'ResultUnit' => (string)($rows->LabUnit ?? ''),
                        'ResultFlag' => '',
                        'ReferenceRange' => trim(($rows->LabNormal1 ?? '') . ' ' . ($rows->LabNormal2 ?? '')),
                        'ResultRemark' => '',
                    ]
                ],
            ];



            // ===== จัดกลุ่มตาม LabNo =====
            if (!isset($groupedByLabNo[$labNo])) {
                $groupedByLabNo[$labNo] = [
                    'LabNo' => $labNo,
                    'RequestRemark' => '', // หมายเหตุการขอ
                    'ResultStatus' => '', // P=Prelim, F=Final
                    'TestList' => []
                ];

                $groupedByLabNo[$labNo]['TestList'][] = $testItem;

                $sendMapping[$rows->RiaHDocID] = $i;
            }

            // แปลง grouped data เป็น array
            $testList = array_values($groupedByLabNo);

            // ===== ตรวจสอบ config =====
            $cfg = Yii::$app->params['external_api'] ?? null;
            if (!$cfg) {
                return [
                    'error' => 'Config external_api not found',
                    'sent_payload' => $testList
                ];
            }

            if (empty($cfg['receiveUrl'])) {
                return [
                    'error' => 'receiveUrl not configured',
                    'config' => $cfg,
                    'sent_payload' => $testList
                ];
            }

            // ===== ขอ token (getValidApiToken จะเรียก getApiToken($cfg) ภายใน) =====
            $tokenError = null;
            $token = $this->getValidApiToken($cfg, $tokenError);

            if (!$token) {
                return [
                    'error' => 'Cannot get API token',
                    'token_error' => $tokenError,
                    'sent_payload' => $testList
                ];
            }

            $url = $cfg['receiveUrl'];

            // API ต้องการ object เดียว ไม่ใช่ array - ดึง record แรก
            $payload = count($testList) > 0 ? $testList[0] : null;

            if (!$payload) {
                return [
                    'error' => 'No test data to send',
                    'sent_payload' => $testList,
                    'file_debug' => $globalDebug
                ];
            }

            // ===== ส่ง payload โดยตรง (ไม่ห่อด้วย dataJson) =====
            $wrappedPayload = $payload;

            $debugInfo = [
                'url' => $url,
                'tokenUrl' => $cfg['tokenUrl'] ?? 'not set',
                'token_exists' => !empty($token),
                'token_length' => strlen($token ?? ''),
                'payload_count' => count($testList),
                'payload_structure' => $wrappedPayload,
            ];

            $response = $this->postJsonCurl($url, $wrappedPayload, [
                'Authorization' => "Bearer {$token}",
            ]);

            $response['debug'] = $debugInfo;
            $response['file_debug'] = $globalDebug;
            $response['token_received'] = $token;
            if ($tokenError) {
                $response['token_warning'] = $tokenError;
            }

            // ===== ตรวจสอบ response message =====
            $success = isset($response['message']) && strtolower($response['message']) === 'success';

            // เก็บ cust_id และ hn ที่ประมวลผล
            $processedPatients = [];

            if ($success) {
                foreach ($RiaLabD as $rows) {
                    $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
                    $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

                    $patientKey = $riaH->CustID . '_' . $riaH->PatHN;
                    if (!isset($processedPatients[$patientKey])) {
                        $processedPatients[$patientKey] = [
                            'cust_id' => $riaH->CustID,
                            'hn' => $riaH->PatHN,
                        ];
                    }

                    $LisResult = LisResult::find()->where([
                        'lab_no' => $hisReq->LabNo,
                        'test_code' => $rows->LabID,
                        'cust_id' => $riaH->CustID,
                        'hn' => $riaH->PatHN
                    ])->one();

                    if (!$LisResult) {
                        $LisResult = new LisResult([
                            'lab_no' => $hisReq->LabNo,
                            'riah_doc_id' => $rows->RiaHDocID,
                            'riah_doc_date' => $rows->RiaHDocDate,
                            'test_code' => $rows->LabID,
                            'test_name' => $rows->LabName,
                            'cust_id' => $riaH->CustID,
                            'hn' => $riaH->PatHN,
                            'result_value' => $rows->RiaLDRsltAnal ?? '',
                            'result_unit' => $rows->LabUnit ?? '',
                            'reference_range' => trim(($rows->LabNormal1 ?? '') . ' ' . ($rows->LabNormal2 ?? '')),
                            'status' => 1,
                        ]);
                        $LisResult->save(false);
                    } else {
                        $LisResult->status = 1;
                        $LisResult->save(false);
                    }
                }
            }

            // ===== Retry สำหรับ record ที่ยัง status = NULL (ตามโค้ดคุณเดิม) =====
            foreach ($processedPatients as $patient) {
                $toRetry = LisApiSend::find()->where([
                    'cust_id' => $patient['cust_id'],
                    'hn' => $patient['hn'],
                    'status' => null
                ])->all();

                foreach ($toRetry as $LisApiSend) {
                    $sent = false;
                    while (!$sent) {
                        $sent = $this->sendLisApiSend($LisApiSend);
                        if (!$sent) {
                            sleep(1);
                        }
                    }
                }
            }

            return ['sent_payload' => $testList, 'response' => $response];
        }
    }

    /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */
    public function dateTimeFormat($value)
    {
        if (empty($value)) return '';
        try {
            $dt = new \DateTime($value);
            return $dt->format('d/m/Y H:i:s');
        } catch (\Throwable $e) {
            return '';
        }
    }






    public function actionCreate()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        // ===== ดึงข้อมูล Lab =====
        $RiaLabD = LisApiD::find()
            ->where(['RiaHDocID' => '253010415', 'CustID' => '1086', 'RiaLDApprvFlag' => '1'])
            ->orderBy(['RiaHDocDate' => SORT_DESC])
            ->all();

        $groupedByLabNo = [];
        $sendMapping = [];
        $globalDebug = [];
        //$processedPatients = [];

        foreach ($RiaLabD as $i => $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
            $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

            $filesData = [];
            $labNo = $hisReq->LabNo ?? '';
            $timestamp = date('dmYHis');
            $docIdParam = $rows['RiaHDocID'] ?? '';
            $debug = [];

            if (!empty($docIdParam)) {
                $dirUrl = 'https://report.prolab.co.th/pdflink/' . $docIdParam . '/';
                $possibleFiles = [];
                for ($j = 1; $j <= 20; $j++) {
                    $possibleFiles[] = $docIdParam . '_' . $j . '.pdf';
                }
                $possibleFiles[] = $docIdParam . '.pdf';
                $possibleFiles[] = 'report.pdf';
                $possibleFiles[] = 'result.pdf';

                foreach ($possibleFiles as $fileName) {
                    $fileUrl = $dirUrl . $fileName;

                    $ch = curl_init($fileUrl);
                    curl_setopt($ch, CURLOPT_NOBODY, true);
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
                    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
                    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
                    curl_exec($ch);
                    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                    curl_close($ch);

                    $debug[] = "Checking: {$fileName} -> HTTP {$httpCode}";

                    if ($httpCode == 200) {
                        $ch2 = curl_init($fileUrl);
                        curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
                        curl_setopt($ch2, CURLOPT_FOLLOWLOCATION, true);
                        curl_setopt($ch2, CURLOPT_TIMEOUT, 10);
                        curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false);
                        $content = curl_exec($ch2);
                        curl_close($ch2);

                        if (!empty($content)) {
                            $ext = pathinfo($fileName, PATHINFO_EXTENSION);
                            $newFileName = $labNo . '_' . $timestamp . '.' . $ext;

                            $finfo = new finfo(FILEINFO_MIME_TYPE);
                            $mime = $finfo->buffer($content);

                            $filesData[] = [
                                'filename' => $newFileName,
                                'base64'   => base64_encode($content),
                                'size'     => strlen($content),
                                'mimetype' => $mime,
                            ];

                            $debug[] = "Success: Loaded {$fileName} (" . strlen($content) . " bytes)";
                        }
                    }
                }

                if (empty($filesData)) {
                    $debug[] = "No file found in {$dirUrl}";
                }
            } else {
                $debug[] = "docIdParam empty";
            }

            $globalDebug[$docIdParam] = $debug;

            $testItem = [
                'TestCode' => (string)($rows->TestCode ?? ''),
                'TestName' => (string)($rows->TestName ?? ''),
                'TestRemark' => '',
                'InformCriticalByCode' => '',
                'InformCriticalBy' => '',
                'InformCriticalTo' => '',
                'InformCriticalDateTime' => '',
                'CriticalFlag' => '',
                'ReportResultByCode' => '',
                'ReportResultBy' => (string)($rows->RiaLDUserNameEntry ?? ''),
                'ReportResultDateTime' => $this->dateTimeFormat($rows->RiaLDUserDateEntry ?? ''),
                'ApproveResultByCode' => '',
                'ApproveResultBy' => (string)($rows->RiaLDUserNameAppr ?? ''),
                'ApproveResultDateTime' => $this->dateTimeFormat($rows->RiaLDUserDateAppr ?? ''),
                'Filedata' => array_map(function ($f) {
                    return [
                        'FileName'   => $f['filename'],
                        'Filebase64' => $f['base64'],
                    ];
                }, $filesData),
                'ResultList' => [
                    [
                        'ResultCode' => (string)($rows->TestCode ?? ''),
                        'ResultName' => (string)($rows->TestName ?? ''),
                        'ResultValue' => (string)($rows->RiaLDRsltAnal ?? ''),
                        'ResultUnit' => (string)($rows->LabUnit ?? ''),
                        'ResultFlag' => '',
                        'ReferenceRange' => trim(($rows->LabNormal1 ?? '') . ' ' . ($rows->LabNormal2 ?? '')),
                        'ResultRemark' => '',
                    ]
                ],
            ];

            if (!isset($groupedByLabNo[$labNo])) {
                $groupedByLabNo[$labNo] = [
                    'LabNo' => $labNo,
                    'RequestRemark' => '',
                    'ResultStatus' => '',
                    'TestList' => []
                ];
            }

            $groupedByLabNo[$labNo]['TestList'][] = $testItem;
            $sendMapping[$rows->RiaHDocID] = $i;

            // เก็บ patient สำหรับ retry
            /*$patientKey = $riaH->CustID . '_' . $riaH->PatHN;
            if (!isset($processedPatients[$patientKey])) {
                $processedPatients[$patientKey] = [
                    'CustID' => $riaH->CustID,
                    'PatHN' => $riaH->PatHN,
                ];
            }*/
        }

        $testList = array_values($groupedByLabNo);
        return $testList;
        // ===== ตรวจสอบ config และ token =====
        $cfg = Yii::$app->params['external_api'] ?? null;
        if (!$cfg || empty($cfg['receiveUrl'])) {
            return [
                'error' => !$cfg ? 'Config external_api not found' : 'receiveUrl not configured',
                'sent_payload' => $testList
            ];
        }

        $tokenError = null;
        $token = $this->getValidApiToken($cfg, $tokenError);
        if (!$token) {
            return [
                'error' => 'Cannot get API token',
                'token_error' => $tokenError,
                'sent_payload' => $testList
            ];
        }

        $url = $cfg['receiveUrl'];
        $wrappedPayload = $testList; // ส่ง TestList ทั้งหมด

        $debugInfo = [
            'url' => $url,
            'tokenUrl' => $cfg['tokenUrl'] ?? 'not set',
            'token_exists' => !empty($token),
            'token_length' => strlen($token ?? ''),
            'payload_count' => count($testList),
            'payload_structure' => $wrappedPayload,
        ];

        $response = $this->postJsonCurl($url, $wrappedPayload, [
            'Authorization' => "Bearer {$token}",
        ]);

        $response['debug'] = $debugInfo;
        $response['file_debug'] = $globalDebug;
        $response['token_received'] = $token;
        if ($tokenError) {
            $response['token_warning'] = $tokenError;
        }

        // ===== อัปเดต LisResult =====
        $success = isset($response['message']) && strtolower($response['message']) === 'success';
        if ($success) {
            foreach ($RiaLabD as $rows) {
                $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
                $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();

                $LisResult = LisResult::find()->where([
                    'lab_no' => $hisReq->LabNo,
                    'test_code' => $rows->LabID,
                    'cust_id' => $riaH->CustID,
                    'hn' => $riaH->PatHN
                ])->one();

                if (!$LisResult) {
                    $LisResult = new LisResult([
                        'lab_no' => $hisReq->LabNo,
                        'riah_doc_id' => $rows->RiaHDocID,
                        'riah_doc_date' => $rows->RiaHDocDate,
                        'test_code' => $rows->LabID,
                        'test_name' => $rows->LabName,
                        'cust_id' => $riaH->CustID,
                        'hn' => $riaH->PatHN,
                        'result_value' => $rows->RiaLDRsltAnal ?? '',
                        'result_unit' => $rows->LabUnit ?? '',
                        'reference_range' => trim(($rows->LabNormal1 ?? '') . ' ' . ($rows->LabNormal2 ?? '')),
                        'status' => 1,
                    ]);
                    $LisResult->save(false);
                } else {
                    $LisResult->status = 1;
                    $LisResult->save(false);
                }
            }
        }

        // ===== Retry สำหรับ record ที่ยัง status = NULL =====
        foreach ($processedPatients as $patient) {
            // เฉพาะ CustID=1086 และ PatHN=00000034
            if ($patient['CustID'] != '1086' || $patient['PatHN'] != '00000034') continue;

            $toRetry = LisApiSend::find()->where([
                'CustID' => $patient['CustID'],
                'PatHN' => $patient['PatHN'],
                'status' => null
            ])->all();

            foreach ($toRetry as $LisApiSend) {
                $sent = false;
                while (!$sent) {
                    $sent = $this->sendLisApiSend($LisApiSend);
                    if (!$sent) sleep(1);
                }
            }
        }

        return ['sent_payload' => $testList, 'response' => $response];
    }

    /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */
    public function dateTimeFormat($value)
    {
        if (empty($value)) return '';
        try {
            $dt = new \DateTime($value);
            return $dt->format('d/m/Y H:i:s');
        } catch (\Throwable $e) {
            return '';
        }
    }

    /**
     * ขอ token จาก API (คืนค่า associative array)
     * รับ $cfg ที่มี tokenUrl, username, password
     */
    protected function getApiToken($cfg)
    {
        $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null;
        $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null;
        $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null;

        $payload = [
            'username' => $username,
            'password' => $password
        ];

        $ch = curl_init($tokenUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Accept: application/json',
        ]);
        curl_setopt($ch, CURLOPT_TIMEOUT, 15);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        $raw = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlErr = curl_error($ch);
        curl_close($ch);

        $result = [
            'ok' => false,
            'token' => null,
            'expires_at' => null,
            'http_code' => $httpCode,
            'raw' => $raw,
            'error' => null,
        ];

        if ($raw === false || $curlErr) {
            $result['error'] = 'cURL error: ' . $curlErr;
            return $result;
        }

        $decoded = json_decode($raw, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            $result['error'] = 'Invalid JSON response from token endpoint';
            return $result;
        }

        // รองรับทั้ง access_token หรือ token key
        if (!empty($decoded['access_token'])) {
            $result['ok'] = true;
            $result['token'] = $decoded['access_token'];
            $expiresIn = intval($decoded['expires_in'] ?? 3600);
            $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn);
        } elseif (!empty($decoded['token'])) {
            $result['ok'] = true;
            $result['token'] = $decoded['token'];
            $expiresIn = intval($decoded['expires_in'] ?? 3600);
            $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn);
        } else {
            $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response');
        }

        return $result;
    }

    /**
     * ดึง token ที่ valid (เก็บใน DB หากต้องการ)
     * คืนค่า token string หรือ null และตั้ง $tokenError หากมีปัญหา
     */
    protected function getValidApiToken($cfg, &$tokenError = null)
    {
        $apiName = $cfg['apiName'] ?? 'gems_api';
        $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one();
        $token = null;
        $nowTs = time();
        $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที

        $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin));

        if ($needRequest) {
            $tokenResp = $this->getApiToken($cfg);

            if (empty($tokenResp) || empty($tokenResp['ok'])) {
                $tokenError = [
                    'message' => 'Failed to get token from API',
                    'tokenUrl' => $cfg['tokenUrl'] ?? null,
                    'error' => $tokenResp['error'] ?? 'Unknown error',
                    'http_code' => $tokenResp['http_code'] ?? 0,
                    'raw' => $tokenResp['raw'] ?? null,
                ];

                // fallback: ถ้ามี token เก่าให้ใช้ต่อ
                if ($tokenModel && !empty($tokenModel->token)) {
                    return $tokenModel->token;
                }

                return null;
            }

            $token = $tokenResp['token'];
            $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600);

            if (!$tokenModel) {
                $tokenModel = new ApiToken();
                $tokenModel->api_name = $apiName;
            }
            $tokenModel->token = $token;
            $tokenModel->expires_at = $expiresAt;
            $tokenModel->save(false);
        } else {
            $token = $tokenModel->token;
        }

        return $token;
    }

    /** helper: ส่ง JSON ผ่าน cURL (ปลอดภัยต่อ response ที่ไม่ใช่ JSON) */
    protected function postJsonCurl($url, $payload, $headers = [])
    {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);

        $curlHeaders = ['Content-Type: application/json'];
        foreach ($headers as $key => $value) {
            $curlHeaders[] = "$key: $value";
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        $curlErrno = curl_errno($ch);
        curl_close($ch);

        $decoded = json_decode($response, true);
        $message = null;
        if (is_array($decoded) && isset($decoded['message'])) {
            $message = $decoded['message'];
        }

        return [
            'http_code' => $httpCode,
            'response' => $response,
            'message' => $message,
            'curl_error' => $curlError,
            'curl_errno' => $curlErrno,
            'url' => $url,
        ];
    }

    protected function sendLisApiSend($LisApiSend)
    {
        if (!$LisApiSend) return false;

        $cfg = Yii::$app->params['external_api'] ?? null;
        if (!$cfg || empty($cfg['receiveUrl'])) return false;

        $tokenError = null;
        $token = $this->getValidApiToken($cfg, $tokenError);
        if (!$token) return false;

        $url = $cfg['receiveUrl'];
        $payload = json_decode($LisApiSend->payload_json ?? '{}', true);
        if (!$payload) return false;

        try {
            $response = $this->postJsonCurl($url, $payload, [
                'Authorization' => "Bearer {$token}",
            ]);

            $success = isset($response['message']) && strtolower($response['message']) === 'success';

            // อัปเดต status ของ LisResult แทน
            if (
                !empty($LisApiSend->lab_no) && !empty($LisApiSend->test_code)
                && !empty($LisApiSend->cust_id) && !empty($LisApiSend->hn)
            ) {

                $LisResult = LisResult::find()->where([
                    'lab_no' => $LisApiSend->lab_no,
                    'test_code' => $LisApiSend->test_code,
                    'cust_id' => $LisApiSend->cust_id,
                    'hn' => $LisApiSend->hn
                ])->one();

                if ($LisResult) {
                    $LisResult->status = $success ? 1 : 0;
                    $LisResult->save(false);
                }
            }

            return $success;
        } catch (\Exception $e) {
            Yii::error("sendLisApiSend exception: " . $e->getMessage());
            return false;
        }
    }

    public function actionCreate()
    {
        ini_set('memory_limit', '2048M');
        set_time_limit(0);

        $this->logConsole("=== LisSend Console START (Batch 500 + Show JSON & Response + Insert LisResult) ===");

        $batchSize = 500;
        $query = LisApiD::find()
            // ต้องการเฉพาะรายการที่อนุมัติ (RiaLDApprvFlag = '1') และมีค่า RiaHDocID กับ PatHN จริง (ไม่เป็น NULL/ว่าง)
            ->where(['RiaLDApprvFlag' => '1'])
            ->andWhere(['not', ['RiaHDocID' => null]])
            ->andWhere(['not', ['PatHN' => null]])
            ->andWhere(['<>', 'RiaHDocID', ''])
            ->andWhere(['<>', 'PatHN', ''])
            ->orderBy(['RiaHDocDate' => SORT_DESC]);

        $totalCount = $query->count();
        $this->logConsole("Total records: {$totalCount}");

        // prepare globals
        $sendMapping = [];
        $globalDebug = [];

        // ===== ตรวจ config (ก่อน loop) =====
        $cfg = Yii::$app->params['external_api'] ?? null;
        if (!$cfg || empty($cfg['receiveUrl'])) {
            $this->logConsole("Missing external_api config or receiveUrl", 'error');
            return 1;
        }

        // ===== ขอ token ONCE =====
        $tokenResp = $this->getApiToken($cfg, null);
        if (empty($tokenResp['ok']) || empty($tokenResp['token'])) {
            $this->logConsole("Cannot obtain API token: " . ($tokenResp['error'] ?? 'unknown'), 'error');
            return 1;
        }
        $token = $tokenResp['token'];
        $url = $cfg['receiveUrl'];
        $this->logConsole("Got API token (expires_at={$tokenResp['expires_at']})");

        $offset = 0;
        while (true) {
            $batch = $query->limit($batchSize)->offset($offset)->all();
            if (empty($batch)) break;

            $this->logConsole("Processing batch offset={$offset} size=" . count($batch));
            $offset += $batchSize;

            // ===== group by LabNo for this batch =====
            $groupedByLabNo = [];
            foreach ($batch as $rows) {
                $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
                if (!$riaH) {
                    $globalDebug[$rows->RiaHDocID] = "Missing RiaFrmHVLAB";
                    $this->logConsole("Missing RiaFrmHVLAB for {$rows->RiaHDocID}", 'warning');
                    continue;
                }

                $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();
                $labNo = $hisReq->LabNo ?? null;
                if (empty($labNo)) {
                    $globalDebug[$rows->RiaHDocID] = "Missing HisRequest/LabNo";
                    continue;
                }

                // collect files (use fetchUrl for remote)
                $filesData = [];
                $timestamp = date('dmYHis');
                $docIdParam = $rows->RiaHDocID ?? '';
                $docDate = date('Y-m-d', strtotime($rows->RiaHDocDate ?? ''));

                if (!empty($docIdParam)) {
                    $LisResultUrl = "https://report.prolab.co.th/prolab/printpdf.php?docID={$docIdParam}&docDate={$docDate}";
                    $pdfContent = $this->fetchUrl($LisResultUrl, 20);
                    if ($pdfContent !== false && strlen($pdfContent) > 0) {
                        $filesData[] = [
                            'FileName'   => "{$labNo}_{$timestamp}.pdf",
                            'Filebase64' => base64_encode($pdfContent),
                        ];
                        $this->logConsole("Attached main PDF for docId={$docIdParam} size=" . strlen($pdfContent));
                    }

                    $localDir = "/var/www/html/pdflink/{$docIdParam}";
                    if (is_dir($localDir)) {
                        foreach (scandir($localDir) as $fn) {
                            if ($fn === '.' || $fn === '..') continue;
                            if (!preg_match('/\.(pdf|jpg|jpeg|png|doc|docx)$/i', $fn)) continue;
                            $path = "{$localDir}/{$fn}";
                            if (is_file($path) && is_readable($path)) {
                                $content = @file_get_contents($path);
                                if ($content !== false) {
                                    $filesData[] = [
                                        'FileName'   => "{$labNo}_{$timestamp}_{$fn}",
                                        'Filebase64' => base64_encode($content),
                                    ];
                                    $this->logConsole("Attached local file {$path} size=" . strlen($content));
                                }
                            }
                        }
                    }
                }

                // build test item (do not filter empty fields)
                $testItem = [
                    'TestCode' => (string)($rows->TestCode ?? ''),
                    'TestName' => (string)($rows->TestName ?? ''),
                    'TestRemark' => $rows->TestRemark ?? '',
                    'InformCriticalByCode' => '',
                    'InformCriticalBy' => '',
                    'InformCriticalTo' => '',
                    'InformCriticalDateTime' => '',
                    'CriticalFlag' => '',
                    'ReportResultByCode' => '',
                    'ReportResultBy' => (string)($rows->RiaLDUserNameEntry ?? ''),
                    'ReportResultDateTime' => $this->dateTimeFormat($rows->RiaLDUserDateEntry ?? ''),
                    'ApproveResultByCode' => '',
                    'ApproveResultBy' => (string)($rows->RiaLDUserNameAppr ?? ''),
                    'ApproveResultDateTime' => $this->dateTimeFormat($rows->RiaLDUserDateAppr ?? ''),
                    'Filedata' => $filesData,
                    'ResultList' => [[
                        'ResultCode' => (string)($rows->TestCode ?? ''),
                        'ResultName' => (string)($rows->TestName ?? ''),
                        'ResultValue' => (string)($rows->RiaLDRsltAnal ?? ''),
                        'ResultUnit' => (string)($rows->LabUnit ?? ''),
                        'ResultFlag' => '',
                        'ReferenceRange' => trim(($rows->LabNormal1 ?? '') . ' ' . ($rows->LabNormal2 ?? '')),
                        'ResultRemark' => '',
                    ]],
                ];

                if (!isset($groupedByLabNo[$labNo])) {
                    $groupedByLabNo[$labNo] = [
                        'LabNo' => $labNo,
                        'RequestRemark' => '',
                        'ResultStatus' => '',
                        'TestList' => [],
                    ];
                }
                $groupedByLabNo[$labNo]['TestList'][] = $testItem;

                // track mapping for debug/insert
                $sendMapping[$rows->RiaHDocID] = $rows->RiaHDocID;
            } // end foreach batch rows

            // prepare single payload (first record) if you prefer single
            $testList = $groupedByLabNo;
            $payload = count($testList) > 0 ? array_values($testList)[0] : null;

            // preload already-sent labNos (single query)
            $labNos = array_keys($groupedByLabNo);
            if (!empty($labNos)) {
                $sentLabNos = LisResult::find()
                    ->select('lab_no')
                    ->where(['lab_no' => $labNos, 'status' => 1])
                    ->column();
            } else {
                $sentLabNos = [];
            }
            $sentMap = array_flip($sentLabNos);

            $this->logConsole("Batch: total lab=" . count($labNos) . ", already sent=" . count($sentLabNos));

            // send only unsent labNos
            foreach ($groupedByLabNo as $labNo => $payloadData) {
                if (isset($sentMap[$labNo])) {
                    $this->logConsole("Skip LabNo={$labNo} (already sent)");
                    continue;
                }

                $payloadJson = json_encode($payloadData, JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION | JSON_PARTIAL_OUTPUT_ON_ERROR);

                $this->logConsole("Sending LabNo={$labNo}");
                $this->logConsole("JSON Payload:\n" . $this->truncateForLog($payloadJson, 8192));

                // send
                $response = $this->postJsonCurl($url, $payloadData, [
                    'Authorization' => "Bearer {$token}",
                    'Content-Type' => 'application/json',
                ]);

                $rawResp = $response['response'] ?? '';
                $decoded = json_decode($rawResp, true);
                $statusVal = $decoded['Status'] ?? $decoded['status'] ?? null;
                $messageVal = $decoded['Message'] ?? $decoded['message'] ?? '';

                $success = false;
                if (is_numeric($statusVal)) {
                    $success = (intval($statusVal) === 0);
                } elseif (is_string($statusVal) && strtolower($statusVal) === 'success') {
                    $success = true;
                } elseif (strpos(strtolower($messageVal), 'success') !== false) {
                    $success = true;
                } elseif (strpos(strtolower($rawResp), '"status":0') !== false) {
                    $success = true;
                }

                $this->logConsole("Response (http={$response['http_code']}): " . $this->truncateForLog($rawResp, 8192));

                if ($success) {
                    $this->logConsole("SUCCESS LabNo={$labNo}");

                    // Insert LisResult only for rows in this batch that match this labNo
                    foreach ($batch as $rows) {
                        $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
                        if (!$riaH) continue;
                        $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();
                        if (!$hisReq || ($hisReq->LabNo ?? '') != $labNo) continue;

                        $exists = LisResult::find()->where([
                            'lab_no' => $hisReq->LabNo,
                            'test_code' => $rows->LabID,
                            'cust_id' => $riaH->CustID,
                            'hn' => $riaH->PatHN,
                        ])->exists();

                        if (!$exists) {
                            $LisResult = new LisResult([
                                'lab_no' => $hisReq->LabNo,
                                'riah_doc_id' => $rows->RiaHDocID,
                                'riah_doc_date' => $rows->RiaHDocDate,
                                'test_code' => $rows->LabID,
                                'test_name' => $rows->LabName,
                                'cust_id' => $riaH->CustID,
                                'hn' => $riaH->PatHN,
                                'result_value' => $rows->RiaLDRsltAnal ?? '',
                                'result_unit' => $rows->LabUnit ?? '',
                                'reference_range' => trim(($rows->LabNormal1 ?? '') . ' ' . ($rows->LabNormal2 ?? '')),
                                'status' => 1,
                            ]);
                            $LisResult->save(false);
                        }
                    }
                } else {
                    $this->logConsole("FAIL LabNo={$labNo}; message=" . ($messageVal ?: 'N/A'), 'error');
                }
            } // end foreach groupedByLabNo

            // free memory
            unset($groupedByLabNo, $batch, $sentLabNos, $sentMap, $labNos);
            gc_collect_cycles();
        } // end while

        $this->logConsole("=== LisSend Console END (Processed offset={$offset}) ===");
        return 0;
    }

    /** helper: ส่ง JSON ผ่าน cURL (คืนข้อมูล debug เต็ม) */
    protected function postJsonCurl($url, $payload, $headers = [])
    {
        $ch = curl_init($url);
        $jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

        $curlHeaders = ['Content-Type: application/json', 'Accept: application/json'];
        foreach ($headers as $k => $v) {
            if (is_int($k)) {
                $curlHeaders[] = $v;
            } else {
                $curlHeaders[] = "{$k}: {$v}";
            }
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_errno($ch) ? curl_error($ch) : null;
        $curlErrno = curl_errno($ch);
        curl_close($ch);

        return [
            'http_code' => $httpCode,
            'response' => $response,
            'curl_error' => $curlError,
            'curl_errno' => $curlErrno,
            'url' => $url,
            'payload_size' => strlen($jsonPayload),
        ];
    }

    /**
     * helper: fetch URL via cURL (robust)
     */
    protected function fetchUrl($url, $timeout = 10)
    {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        $http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err = curl_errno($ch) ? curl_error($ch) : null;
        curl_close($ch);

        if ($data === false || $http >= 400) {
            $this->logConsole("fetchUrl failed: {$url} http={$http} err=" . ($err ?? 'none'), 'warning');
            return false;
        }
        return $data;
    }

    /**
     * Log helper — writes to STDOUT with timestamp and also to Yii log (info).
     */
    protected function logConsole($message)
    {
        $time = date('Y-m-d H:i:s');
        $line = "[{$time}] {$message}" . PHP_EOL;
        // write to console
        $this->stdout($line);
        // write to Yii logger
        Yii::info($message, __METHOD__);
    }

    /**
     * Truncate long strings for logging. $maxBytes = null => no truncation.
     */
    protected function truncateForLog($text, $maxBytes = 4096)
    {
        if ($text === null) return '';
        if ($maxBytes === null) return $text;
        $len = strlen($text);
        if ($len <= $maxBytes) return $text;
        $prefix = substr($text, 0, $maxBytes);
        return $prefix . "\n...TRUNCATED... (original_length={$len})";
    }

    /** format timestamp */
    protected function formatTs()
    {
        return date('Y-m-d H:i:s');
    }


    /** helper: แปลง datetime เป็น dd/mm/YYYY H:i:s */
    public function dateTimeFormat($value)
    {
        if (empty($value)) return '';
        try {
            $dt = new \DateTime($value);
            return $dt->format('d/m/Y H:i:s');
        } catch (\Throwable $e) {
            return '';
        }
    }

    /**
     * ขอ token จาก API (คืนค่า associative array แบบ {ok, token, expires_at, http_code, raw, error})
     */
    protected function getApiToken($cfg)
    {
        $tokenUrl = $cfg['tokenUrl'] ?? Yii::$app->params['external_api']['tokenUrl'] ?? null;
        $username = $cfg['username'] ?? Yii::$app->params['external_api']['username'] ?? null;
        $password = $cfg['password'] ?? Yii::$app->params['external_api']['password'] ?? null;

        $payload = ['username' => $username, 'password' => $password];

        $ch = curl_init($tokenUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json']);
        curl_setopt($ch, CURLOPT_TIMEOUT, 15);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        $raw = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlErr = curl_errno($ch) ? curl_error($ch) : null;
        curl_close($ch);

        $result = ['ok' => false, 'token' => null, 'expires_at' => null, 'http_code' => $httpCode, 'raw' => $raw, 'error' => null];

        if ($raw === false || $curlErr) {
            $result['error'] = 'cURL error: ' . ($curlErr ?? 'unknown');
            return $result;
        }

        $decoded = json_decode($raw, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            $result['error'] = 'Invalid JSON response from token endpoint';
            return $result;
        }

        if (!empty($decoded['access_token'])) {
            $result['ok'] = true;
            $result['token'] = $decoded['access_token'];
            $expiresIn = intval($decoded['expires_in'] ?? 3600);
            $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn);
        } elseif (!empty($decoded['token'])) {
            $result['ok'] = true;
            $result['token'] = $decoded['token'];
            $expiresIn = intval($decoded['expires_in'] ?? 3600);
            $result['expires_at'] = date('Y-m-d H:i:s', time() + $expiresIn);
        } else {
            $result['error'] = $decoded['error'] ?? ($decoded['message'] ?? 'No token in response');
        }

        return $result;
    }


    /**
     * ดึง token ที่ valid (คืน token string หรือ null และตั้ง $tokenError หากมีปัญหา)
     * พร้อม debug log วันที่หมดอายุของ Token
     */
    protected function getValidApiToken($cfg, &$tokenError = null)
    {
        $apiName = $cfg['apiName'] ?? 'gems_api';
        $tokenModel = ApiToken::find()->where(['api_name' => $apiName])->one();
        $token = null;
        $nowTs = time();
        $refreshMargin = $cfg['refreshMargin'] ?? 300; // 5 นาที

        $needRequest = !$tokenModel || (strtotime($tokenModel->expires_at) <= ($nowTs + $refreshMargin));

        // 🔍 Debug token ที่มีอยู่ใน DB (ถ้ามี)
        if ($tokenModel) {
            $this->logConsole("Token in DB found for {$apiName}: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($tokenModel->token ?? '', 0, 15) . "...");
        } else {
            $this->logConsole("No token found in DB for {$apiName}, will request new token");
        }

        if ($needRequest) {
            $tokenResp = $this->getApiToken($cfg);

            if (empty($tokenResp) || empty($tokenResp['ok'])) {
                $tokenError = [
                    'message' => 'Failed to get token from API',
                    'tokenUrl' => $cfg['tokenUrl'] ?? null,
                    'error' => $tokenResp['error'] ?? 'Unknown error',
                    'http_code' => $tokenResp['http_code'] ?? 0,
                    'raw' => $tokenResp['raw'] ?? null,
                ];

                if ($tokenModel && !empty($tokenModel->token)) {
                    $this->logConsole("⚠️ Using old token as fallback (expires_at={$tokenModel->expires_at})");
                    return $tokenModel->token; // fallback
                }

                return null;
            }

            $token = $tokenResp['token'];
            $expiresAt = $tokenResp['expires_at'] ?? date('Y-m-d H:i:s', time() + 3600);

            if (!$tokenModel) {
                $tokenModel = new ApiToken();
                $tokenModel->api_name = $apiName;
            }

            $tokenModel->token = $token;
            $tokenModel->expires_at = $expiresAt;

            try {
                $tokenModel->save(false);
                $this->logConsole("New token saved: api_name={$apiName}, expires_at={$expiresAt}, token_prefix=" . substr($token, 0, 15) . "...");
            } catch (\Throwable $e) {
                $this->logConsole("Error saving new token: " . $e->getMessage(), 'error');
                if ($tokenModel && !empty($tokenModel->token)) {
                    return $tokenModel->token; // fallback
                }
                $tokenError = $e->getMessage();
                return null;
            }
        } else {
            $token = $tokenModel->token;
            $this->logConsole("Reusing valid token: expires_at={$tokenModel->expires_at}, token_prefix=" . substr($token, 0, 15) . "...");
        }

        return $token;
    }
}