<?php

namespace console\controllers;

use common\models\ApiToken;
use yii\console\Controller;
use common\models\RiaLabD;
use common\models\RiaFrmHVLAB;
use common\models\HisRequest;
use common\models\LisResult;
use console\models\HanumanResultSikarinCheck;
use Yii;

class LisSendController extends Controller
{

    public function actionCreate()
    {
        $this->logConsole("=== Hanuman Result Sikarin START ===");

        // --- ดึงข้อมูล (ทดสอบ: limit 3) ---
        $rows = HanumanResultSikarinCheck::find()
            ->where(['not', ['RiaHDocID' => null]])
            ->andWhere(['not', ['PatHN' => null]])
            ->andWhere(['<>', 'RiaHDocID', ''])
            ->andWhere(['<>', 'PatHN', ''])
            ->limit(3) // <-- เอาออกเมื่อไป production
            ->orderBy(['RiaHDocDate' => SORT_DESC])
            ->asArray()
            ->all();

        $this->logConsole("Total records: " . count($rows));

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

        // --- ขอ token ที่ valid ---
        $tokenError = null;
        $token = $this->getValidApiToken($cfg, $tokenError);
        if (empty($token)) {
            $this->logConsole("Cannot obtain valid API token: " . (is_array($tokenError) ? json_encode($tokenError) : ($tokenError ?? 'unknown')), 'error');
            return 1;
        }

        // --- Group rows by LabNo ---
        $grouped = [];
        foreach ($rows as $r) {
            $labNo = $r['LabNo'] ?? '';
            if ($labNo === '') {
                $this->logConsole("Skipping row with empty LabNo: " . json_encode($r), 'warning');
                continue;
            }

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

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

            $grouped[$labNo]['TestList'][] = $testItem;
            $grouped[$labNo]['OriginalRows'][] = $r;
            $docId = $r['RiaHDocID'] ?? '';
            if ($docId !== '' && !in_array($docId, $grouped[$labNo]['DocIds'], true)) {
                $grouped[$labNo]['DocIds'][] = $docId;
            }
        }

        // --- ตั้งค่า limit ขนาดไฟล์ (ปรับได้) ---
        $maxFileBytes = 5 * 1024 * 1024; // 5 MB

        // --- ส่งทีละ LabNo ---
        foreach ($grouped as $labNo => $payload) {
            $this->logConsole("Preparing payload for LabNo={$labNo}");

            // รวบรวมไฟล์จาก docIds
            $filesData = [];
            $timestamp = date('dmYHis');

            foreach ($payload['DocIds'] as $docIdParam) {
                if (empty($docIdParam)) continue;

                // หา docDate จาก OriginalRows
                $docDate = null;
                foreach ($payload['OriginalRows'] as $r) {
                    if (($r['RiaHDocID'] ?? '') === $docIdParam) {
                        $docDate = date('Y-m-d', strtotime($r['RiaHDocDate'] ?? ''));
                        break;
                    }
                }

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

                // แนบไฟล์จาก local dir /var/www/html/pdflink/{docId}
                $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) {
                                $size = strlen($content);
                                if ($size <= $maxFileBytes) {
                                    $filesData[] = [
                                        'FileName' => "{$labNo}_{$timestamp}_{$fn}",
                                        'Filebase64' => base64_encode($content),
                                    ];
                                    $this->logConsole("Attached local file {$path} size={$size}");
                                } else {
                                    $this->logConsole("Skipping large local file {$path} size={$size}", 'warning');
                                }
                            }
                        }
                    }
                }
            }

            // แนบไฟล์เข้า TestList ถ้ามี
            if (!empty($filesData)) {
                foreach ($payload['TestList'] as &$t) {
                    $t['Filedata'] = $filesData;
                }
                unset($t);
            }

            // สร้าง payload ที่จะส่ง
            $finalPayload = [
                'LabNo' => $payload['LabNo'],
                'RequestRemark' => $payload['RequestRemark'],
                'ResultStatus' => $payload['ResultStatus'],
                'TestList' => $payload['TestList'],
            ];

            // --- retry loop (1 refresh token allowed) ---
            $maxAttempts = 2;
            $sent = false;
            for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
                $this->logConsole("Sending LabNo={$labNo} attempt=" . ($attempt + 1));
                $response = $this->postJsonCurl($url, $finalPayload, [
                    'Authorization' => "Bearer {$token}",
                    'Content-Type' => 'application/json',
                ]);

                $rawResp = $response['response'] ?? '';
                $httpCode = $response['http_code'] ?? 0;
                $curlErr = $response['curl_error'] ?? null;

                if (!empty($curlErr)) {
                    $this->logConsole("cURL error on attempt " . ($attempt + 1) . ": {$curlErr}", 'warning');
                }

                // ถ้า 401 และยังไม่ refresh ให้ refresh แล้ว retry
                if ($httpCode == 401 && $attempt === 0) {
                    $this->logConsole("401 Unauthorized received — refreshing token and retrying", 'warning');
                    $token = $this->getValidApiToken($cfg, $tokenError);
                    if (empty($token)) {
                        $this->logConsole("Failed to refresh token after 401", 'error');
                        break;
                    }
                    continue; // retry กับ token ใหม่
                }

                // ตรวจผลตอบกลับ
                $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 (is_string($messageVal) && strpos(strtolower($messageVal), 'success') !== false) {
                    $success = true;
                } elseif (is_string($rawResp) && strpos(strtolower($rawResp), '"status":0') !== false) {
                    $success = true;
                }

                $this->logConsole("Response (http={$httpCode}): " . $this->truncateForLog($rawResp, 8192));

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

                    // บันทึก LisResult สำหรับแต่ละแถวต้นฉบับ (เช็คซ้ำก่อน)
                    foreach ($payload['OriginalRows'] as $r) {
                        try {
                            $exists = LisResult::find()->where([
                                'lab_no' => $r['LabNo'],
                                'test_code' => $r['TestCode'],
                                'cust_id' => $r['CustID'],
                                'hn' => $r['PatHN'],
                            ])->exists();

                            if (!$exists) {
                                $LisResult = new LisResult([
                                    'lab_no' => $r['LabNo'],
                                    'riah_doc_id' => $r['RiaHDocID'],
                                    'riah_doc_date' => $r['RiaHDocDate'],
                                    'test_code' => $r['TestCode'],
                                    'test_name' => $r['TestName'],
                                    'cust_id' => $r['CustID'],
                                    'hn' => $r['PatHN'],
                                    'result_value' => $r['RiaLDRsltAnal'] ?? '',
                                    'result_unit' => $r['LabUnit'] ?? '',
                                    'reference_range' => trim(($r['LabNormal1'] ?? '') . ' ' . ($r['LabNormal2'] ?? '')),
                                    'status' => 1,
                                ]);
                                if (!$LisResult->save()) {
                                    $this->logConsole("Failed to save LisResult for LabNo={$r['LabNo']} TestCode={$r['TestCode']}: " . json_encode($LisResult->getErrors()), 'error');
                                }
                            } else {
                                $this->logConsole("Record already exists in LisResult, skipping insert: LabNo={$r['LabNo']}, TestCode={$r['TestCode']}, CustID={$r['CustID']}, HN={$r['PatHN']}", 'warning');
                            }
                        } catch (\Throwable $ex) {
                            $this->logConsole("Exception inserting LisResult: " . $ex->getMessage(), 'error');
                        }
                    }

                    $sent = true;
                    break; // เสร็จสำหรับ lab นี้
                } else {
                    $msg = $rawResp ?: ($curlErr ?? 'No response');
                    $this->logConsole("FAIL LabNo={$labNo}; last_http=" . ($httpCode ?: 'no_http') . "; message=" . $this->truncateForLog($msg, 1024), 'error');
                }
            } // end attempts

            if (!$sent) {
                $this->logConsole("Giving up on LabNo={$labNo} after {$maxAttempts} attempts", 'error');
            }
        } // end foreach grouped

        $this->logConsole("=== Hanuman Result Sikarin END ===");
        return 0;
    }



    public function actionCreate()
    {
        $this->logConsole("=== LisSend Console START ===");

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

        $groupedByLabNo = [];
        $globalDebug = [];

        foreach ($RiaLabD as $rows) {
            $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
            if (!$riaH) {
                $msg = "Missing RiaFrmHVLAB for {$rows->RiaHDocID}";
                $globalDebug[$rows->RiaHDocID] = [$msg];
                $this->logConsole($msg, 'warning');
                continue;
            }

            $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();
            $labNo = $hisReq->LabNo ?? '';

            $filesData = [];
            $timestamp = date('dmYHis');
            $docIdParam = $rows['RiaHDocID'] ?? '';
            $docDate = date('Y-m-d', strtotime($rows['RiaHDocDate'] ?? ''));

            if (!empty($docIdParam)) {
                $dirUrl = 'https://report.prolab.co.th/pdflink/' . $docIdParam . '/';
                $mydir = "/var/www/html/pdflink/" . $docIdParam;
                $mydirobj = is_dir($mydir) ? scandir($mydir) : [];

                $LisResult = 'https://report.prolab.co.th/prolab/printpdf.php?docID=' . $docIdParam . '&docDate=' . $docDate;
                $pdfContent = @file_get_contents($LisResult);
                $hasLisResult = ($pdfContent !== false && strlen($pdfContent) > 0);

                if ($hasLisResult) {
                    $newFileName = ($labNo ?: 'lab') . '_' . $timestamp . '.pdf';
                    $filesData[] = [
                        'FileName' => $newFileName,
                        'Filebase64' => base64_encode($pdfContent),
                    ];
                }

                if (!empty($mydirobj) && count($mydirobj) > 2) {
                    foreach ($mydirobj as $filename) {
                        if (preg_match('/\.(png|pdf|jpg|jpeg|doc|docx)$/i', $filename)) {
                            $fileUrl = $dirUrl . $filename;
                            $content = @file_get_contents($fileUrl);
                            if ($content !== false) {
                                $newFileName = ($labNo ?: 'lab') . '_' . $timestamp . '_outlab_' . $filename;
                                $filesData[] = [
                                    'FileName' => $newFileName,
                                    'Filebase64' => base64_encode($content),
                                ];
                            }
                        }
                    }
                }
            }

            $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' => $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' => '',
                    ]
                ],
            ];

            // ===== จัดกลุ่มตาม LabNo =====
            if (!isset($groupedByLabNo[$labNo])) {
                $groupedByLabNo[$labNo] = [
                    'LabNo' => $labNo,
                    'RequestRemark' => '',
                    'ResultStatus' => '',
                    'TestList' => []
                ];
            }
            $groupedByLabNo[$labNo]['TestList'][] = $testItem;
        }

        // ===== ตรวจ config =====
        $cfg = Yii::$app->params['external_api'] ?? null;
        if (!$cfg) {
            $this->logConsole('Config external_api not found', 'error');
            return 1;
        }
        if (empty($cfg['receiveUrl'])) {
            $this->logConsole('receiveUrl not configured', 'error');
            return 1;
        }

        // ===== ขอ token ONCE (log ระบุว่าเริ่มขอ token) =====
        $this->logConsole("Requesting API token (global) ...");
        $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'];
        $this->logConsole("Got API token (expires_at={$tokenResp['expires_at']})");

        // ===== ส่งทีละ Lab (log เริ่ม/สิ้นสุดสำหรับแต่ละ LabNo) =====
        $url = $cfg['receiveUrl'];
        foreach ($groupedByLabNo as $labNo => $payloadData) {
            $wrappedPayload = $payloadData;
            $payloadJson = json_encode($wrappedPayload, JSON_UNESCAPED_UNICODE);
            $payloadSize = strlen($payloadJson);

            // --- log payload (truncate ถ้ายาว) ---
            $this->logConsole("Start sending cURL for LabNo={$labNo}; payload_size={$payloadSize}");
            $this->logConsole("Payload JSON: " . $this->truncateForLog($payloadJson, 8192));

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

            $httpCode = $response['http_code'] ?? 'n/a';
            $rawResp = $response['response'] ?? '';
            $decoded = null;
            if (!empty($rawResp)) {
                $decoded = json_decode($rawResp, true);
            }

            // --- log response raw + decoded (truncate raw ถ้ายาว) ---
            $this->logConsole("Finished sending LabNo={$labNo}; http_code={$httpCode}");
            $this->logConsole("Response RAW: " . $this->truncateForLog($rawResp, 8192));
            $this->logConsole("Response DECODED: " . var_export($decoded, true));

            // ถ้าสำเร็จให้ update LisResult
            $success = false;
            if (is_array($decoded)) {
                $statusVal = $decoded['Status'] ?? ($decoded['status'] ?? null);
                if ($statusVal !== null) {
                    if (is_numeric($statusVal)) {
                        $success = (intval($statusVal) === 0);
                    } else {
                        $s = strtolower(trim((string)$statusVal));
                        if ($s === '0' || $s === 'success' || $s === 'ok') {
                            $success = true;
                        }
                    }
                }
                if (!$success) {
                    $msg = strtolower(trim($decoded['Message'] ?? ($decoded['message'] ?? '')));
                    if ($msg !== '' && (strpos($msg, 'success') !== false || strpos($msg, 'successful') !== false)) {
                        $success = true;
                    }
                }
            }

            if ($success) {
                foreach ($RiaLabD as $rows) {
                    $riaH = RiaFrmHVLAB::findOne(['RiaHDocID' => $rows->RiaHDocID]);
                    if (!$riaH) continue;
                    $hisReq = HisRequest::find()->where(['HN' => $riaH->PatHN, 'CustomerCode' => $riaH->CustID])->one();
                    if (!$hisReq) {
                        Yii::warning("No HisRequest for HN={$riaH->PatHN} Cust={$riaH->CustID} RiaHDocID={$rows->RiaHDocID}", __METHOD__);
                        continue;
                    }

                    $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,
                        ]);
                        if (!$LisResult->save(false)) {
                            Yii::error("Failed to save LisResult for lab_no={$hisReq->LabNo} test_code={$rows->LabID}", __METHOD__);
                        } else {
                            $this->stdout("Saved LisResult: lab_no={$hisReq->LabNo} test_code={$rows->LabID}\n");
                            Yii::info("Saved LisResult for lab_no={$hisReq->LabNo} test_code={$rows->LabID}", __METHOD__);
                        }
                    } else {
                        // ถ้ามีอยู่แล้ว ให้แสดงข้อความว่า "ถูกบันทึกแล้ว"
                        $msg = "Already exists LisResult: lab_no={$LisResult->lab_no} test_code={$LisResult->test_code} riah_doc_id={$LisResult->riah_doc_id}\n";
                        // Console output (สำหรับรันจาก CLI)
                        if (method_exists($this, 'stdout')) {
                            $this->stdout($msg);
                        } else {
                            echo $msg;
                        }
                        // เขียนลง log ด้วย
                        Yii::info("Skipped saving — LisResult already exists for lab_no={$LisResult->lab_no} test_code={$LisResult->test_code}", __METHOD__);
                    }
                }
            }
        }

        $this->logConsole("=== LisSend Console END ===");
        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),
        ];
    }

    /**
     * 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})";
    }


    /**
     * Helper: log to Yii + console with timestamp
     * $level: 'info' | 'warning' | 'error'
     */
    protected function logConsole($message, $level = 'info', $echo = true)
    {
        $ts = $this->formatTs();
        $line = "[{$ts}] " . $message;

        switch ($level) {
            case 'warning':
                Yii::warning($line, __METHOD__);
                break;
            case 'error':
                Yii::error($line, __METHOD__);
                break;
            default:
                Yii::info($line, __METHOD__);
                break;
        }

        if ($echo && php_sapi_name() === 'cli') {
            $this->stdout($line . PHP_EOL);
        }
    }

    /** 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 หากมีปัญหา)
     */
    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,
                ];

                if ($tokenModel && !empty($tokenModel->token)) {
                    return $tokenModel->token; // fallback: ใช้ 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;
    }
}
