<?php

namespace platyFramework;

// require_once 'ptyCliLog.php';

class Elastic
{
    private $apiKey;
    private $baseUrl;
    private $cliLog;
    private $user;
    private $password;

    public function __construct($apiKey = null, $baseUrl = null, $cliLog = null, $user = null, $password = null)
    {
        $this->apiKey = $apiKey;
        $this->baseUrl = $baseUrl ?? 'https://elastic:9200';
        $this->cliLog = $cliLog ?: new ptyCliLog(true);
        $this->user = $user;
        $this->password = $password;

        // apiKey도 없고 user/password도 없으면 기본 apiKey 사용
        if (empty($this->apiKey) && empty($this->user)) {
            $this->apiKey = 'RWxCT2tvMEJyTUd0VTMwSDlleW06VHBNbmpmZE9TenkteThCMW5FOC1YUQ==';
        }
    }

    public function search(string $path, array $data)
    {
        $this->cliLog->elastic("Elastic.search() $path");
        return $this->request('POST', $path, $data);
    }

    public function get(string $path)
    {
        $this->cliLog->elastic("Elastic.get() $path");
        return $this->request('GET', $path);
    }

    public function post(string $path, array $data)
    {
        $this->cliLog->elastic("Elastic.post() $path");
        return $this->request('POST', $path, $data);
    }

    public function put(string $path, array $data)
    {
        $this->cliLog->elastic("Elastic.put() $path");
        return $this->request('PUT', $path, $data);
    }

    public function delete(string $path /* index명 */)
    {
        $this->cliLog->elastic("Elastic.delete() $path");
        return $this->request('DELETE', $path);
    }

    public function setDebug(bool $debug)
    {
        $this->cliLog->setDebug($debug);
    }

    public function isDebugEnabled()
    {
        return $this->cliLog->isDebugEnabled();
    }

    public function deleteIndex(string $indexName)
    {
        $this->delete($indexName);
    }


    public function indexExists(string $indexName)
    {
        $this->cliLog->elastic("Elastic 인덱스 존재 여부 확인중: {$indexName}");

        try {
            $this->get($indexName);
            $this->cliLog->elastic("Elastic 인덱스 존재함: {$indexName}");
            return true;
        } catch (\Exception $e) {
            if ($e->getCode() == 404) {
                $this->cliLog->elastic("Elastic 인덱스가 존재하지 않음: {$indexName}");
                return false;
            }
            $this->cliLog->elastic("Elastic 인덱스 확인 중 오류: " . $e->getMessage());
            throw $e;
        }
    }

    public function testConnection()
    {
        $this->cliLog->info("Elasticsearch 연결 테스트 시작");

        try {
            // 기본 헬스체크 엔드포인트
            $response = $this->get('_cluster/health');
            $this->cliLog->success("클러스터 상태: " . $response['status']);
            return true;
        } catch (Exception $e) {
            $this->cliLog->error("연결 테스트 실패: " . $e->getMessage());

            // 더 간단한 테스트
            $this->cliLog->info("기본 루트 엔드포인트 테스트 중");
            try {
                $response = $this->get('');
                $this->cliLog->success("루트 엔드포인트 응답 성공");
                return true;
            } catch (Exception $e2) {
                $this->cliLog->error("루트 엔드포인트도 실패: " . $e2->getMessage());
                return false;
            }
        }
    }


    public function getMapping(string $indexName)
    {
        $this->cliLog->elastic("Elastic 인덱스 매핑 조회: {$indexName}");

        try {
            $response = $this->get("{$indexName}/_mapping");
            $this->cliLog->elastic("Elastic 매핑 조회 완료");
            return $response;
        } catch (Exception $e) {
            $this->cliLog->error("Elastic 매핑 조회 실패: " . $e->getMessage());
            throw $e;
        }
    }

    public function updateMapping(string $indexName, array $properties)
    {
        $this->cliLog->elastic("Elastic 인덱스 매핑 업데이트: {$indexName}");

        $mappingData = [
            "properties" => $properties
        ];

        try {
            $response = $this->put("{$indexName}/_mapping", $mappingData);
            $this->cliLog->elastic("Elastic 매핑 업데이트 완료");
            return $response;
        } catch (Exception $e) {
            $this->cliLog->error("Elastic 매핑 업데이트 실패: " . $e->getMessage());
            throw $e;
        }
    }

    private function request(string $method, string $path, array $data = [])
    {
        $url = rtrim($this->baseUrl, '/') . '/' . ltrim($path, '/');

        // $this->cliLog->info("Elasticsearch 요청 시작");
        $this->cliLog->elastic("Elastic.request() method = $method, URL = " . $url);
        // $this->cliLog->info("HTTP 메소드: " . $method);

        if (!empty($data)) {
            $this->cliLog->elastic("Elastic.request() data = ", $data);
        }

        $startTime = microtime(true);

        $ch = curl_init();

        $headers = ['Content-Type: application/json'];
        if (!empty($this->apiKey)) {
            $headers[] = 'Authorization: ApiKey ' . $this->apiKey;
        } elseif (!empty($this->user) && !empty($this->password)) {
            $headers[] = 'Authorization: Basic ' . base64_encode($this->user . ':' . $this->password);
        }

        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 60 * 30,
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false
        ]);

        // print_r($headers); exit;

        if ($method !== 'GET' && !empty($data)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $totalTime = curl_getinfo($ch, CURLINFO_TOTAL_TIME);
        $error = curl_error($ch);

        // print_r($method);
        // print_r($response); exit;

        curl_close($ch);

        $endTime = microtime(true);
        $executionTime = round(($endTime - $startTime) * 1000, 2);

        if ($response === false) {
            $this->cliLog->error("cURL 오류: " . $error);
            throw new \Exception('cURL Error: ' . $error);
        }

        $this->cliLog->elastic("Elastic.request() 응답 시간: {$executionTime}ms");
        if ($httpCode >= 400) {
            $this->cliLog->elastic("Elastic.request() HTTP 상태: " . $httpCode);
        } else {
            $this->cliLog->elastic("Elastic.request() HTTP 상태 = " . $httpCode);
        }

        $decodedResponse = json_decode($response, true);

        if ($this->cliLog->isDebugEnabled() && $decodedResponse) {
            // $this->cliLog->response("응답 데이터:", $decodedResponse);
        }

        if ($httpCode >= 400) {
            $errorMessage = isset($decodedResponse['error']['reason'])
                ? $decodedResponse['error']['reason']
                : 'HTTP Error: ' . $httpCode;
            $this->cliLog->error("오류 발생: " . $errorMessage);
            throw new \Exception($errorMessage, $httpCode);
        }

        $this->cliLog->elastic("Elastic.request() success");
        // $this->cliLog->success("요청 완료");


        return $decodedResponse;
    }

    public function deleteByIdPattern(string $indexName, string $idPattern)
    {
        /*
        curl -X POST "https://elastic:9200/new_law_items_embedding/_delete_by_query" \
     -H 'Content-Type: application/json' \
     -d '{
       "query": {
         "query_string": {
           "query": "_id:law_01291120251125_279905_*"
         }
       }
     }'
        */

        $this->cliLog->elastic("Elastic deleteByIdPattern() indexName={$indexName}, idPattern={$idPattern}");

        // _id 필드에 대한 패턴 매칭은 query_string 방식 사용
        $data = [
            "query" => [
                "query_string" => [
                    "query" => "_id:" . $idPattern . "*"
                ]
            ]
        ];

        try {
            $response = $this->post("{$indexName}/_delete_by_query", $data);

            $deleted = $response['deleted'] ?? 0;
            $this->cliLog->success("Elastic deleteByIdPattern() 완료. {$deleted}건 삭제됨");

            return $response;
        } catch (\Exception $e) {
            $this->cliLog->error("Elastic deleteByIdPattern() 실패: " . $e->getMessage());
            throw $e;
        }
    }

    public function deleteBySourceField(string $indexName, string $fieldName, string $fieldValue)
    {
        /*
        curl -X POST "https://elastic:9200/new_law_items_embedding/_delete_by_query" \
     -H 'Content-Type: application/json' \
     -d '{
       "query": {
         "term": {
           "serviceId": "01291120251125_279905"
         }
       }
     }'
        */

        $this->cliLog->elastic("Elastic deleteBySourceField() indexName={$indexName}, fieldName={$fieldName}, fieldValue={$fieldValue}");

        // _source 내의 필드로 삭제 (term 쿼리 사용)
        $data = [
            "query" => [
                "term" => [
                    $fieldName => $fieldValue
                ]
            ]
        ];

        try {
            $response = $this->post("{$indexName}/_delete_by_query", $data);

            $deleted = $response['deleted'] ?? 0;
            $this->cliLog->success("Elastic deleteBySourceField() 완료. {$deleted}건 삭제됨");

            return $response;
        } catch (\Exception $e) {
            $this->cliLog->error("Elastic deleteBySourceField() 실패: " . $e->getMessage());
            throw $e;
        }
    }

}

?>