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

    // === แทนที่ method ที่เกี่ยวข้องทั้งหมดด้วยโค้ดด้านล่าง ===

    /**
     * actionCreate (แก้ไข) - คืนค่า JSON
     */
    public function actionCreate()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        $baseDir = 'https://report.prolab.co.th/api/get_gems_data.php';
        // ===== ดึงข้อมูล Lab =====
        $RiaLabD = LisApiD::find()
            ->where(['RiaHDocID' => '252940159', 'CustID' => '1087', 'RiaLDApprvFlag' => '1'])
            ->orderBy(['RiaHDocDate' => SORT_DESC])
            ->all();

        $testList = [];
        $sendMapping = [];

        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');

            // เรียก API ตรวจสอบไฟล์ (ถ้าจำเป็น)
            $payloadCheck = [
                'mode' => 'writeresult',
                'LIS_Brand' => 'SIKARIN',
                'CustID' => $riaH->CustID,
            ];

            $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: */*',
                'Authorization: Basic Z3JvdXAxOlByb2otMzI1',
            ]);
            $responseCheck = curl_exec($ch);
            $httpCodeCheck = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            if ($httpCodeCheck == 200 && !empty($responseCheck)) {
                // สมมติ API คืน binary/pdf หรือ content ที่ต้องแปลง
                $fileBase64 = base64_encode($responseCheck);
                $fileName = "{$labNo}_{$timestamp}.pdf";
            }

            // ===== สร้าง testList =====
            // แปลง TestList และ ResultList เป็น array เพื่อความเข้ากันได้กับหลายรายการ
            $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' => $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' => '',
                            ]
                        ],
                    ]
                ]
            ];

            $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
            ];
        }

        // ===== ขอ 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'];

        // ===== ห่อ payload ด้วย dataJson ตามที่ API ต้องการ =====
        $wrappedPayload = ['dataJson' => $testList];

        $debugInfo = [
            'url' => $url,
            'tokenUrl' => $cfg['tokenUrl'] ?? 'not set',
            'token_exists' => !empty($token),
            'token_length' => strlen($token ?? ''),
            'payload_count' => count($testList),
            'payload_structure' => $wrappedPayload, // แสดง payload ที่ส่งจริง
        ];

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

        $response['debug'] = $debugInfo;
        $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 '';
        }
    }

    /**
     * ขอ 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,
        ];
    }
}