<?php
if (!function_exists('limitHscpMarketPriceInfo')) {
    /**
     * 해당 매물의 시세를 확인후 상한가의 200% ~ 하한가의 70% 이내의 범위에 값이 설정되어 있는지 확인
     * @param string $trade_type 거래종류
     * @param string $hscp_no 단지코드 
     * @param string $ptp_no 평형코드
     * @param string $atcl_amt 가격
     */
    function limitHscpMarketPriceInfo($CI, $trade_type, $hscp_no, $ptp_no, $atcl_amt)
    {
        $return = array();

        if (!empty($hscp_no) && !empty($ptp_no)) {

            $hscpMarketPriceInfo = $CI->call_kiso_api->hscpMarketPriceInfo($hscp_no, $ptp_no);

            if (isset($hscpMarketPriceInfo['error'])) {	//결과값 확인
                if ($hscpMarketPriceInfo['error']['code'] == 'VC027') {
                    $return = array();
                } else {
                    $return = $hscpMarketPriceInfo['error'];
                }
            } else {
                $limitH = 0;
                $limitL = 0;
                // 상한가, 하한가 체크 ( 상한가 * 2, 하한가 * 0.7) 이내의 범위에 가격이 있어야 함.
                if ($trade_type == 'A1') {
                    // 매매
                    if (isset($hscpMarketPriceInfo['result']['deal_uplmt_prc'])) {
                        $limitH = $hscpMarketPriceInfo['result']['deal_uplmt_prc'];
                    }
                    if (isset($hscpMarketPriceInfo['result']['deal_lwlmt_prc'])) {
                        $limitL = $hscpMarketPriceInfo['result']['deal_lwlmt_prc'];
                    }
                } elseif ($trade_type == 'B1') {
                    // 전세
                    if (isset($hscpMarketPriceInfo['result']['lease_uplmt_prc'])) {
                        $limitH = $hscpMarketPriceInfo['result']['lease_uplmt_prc'];
                    }
                    if (isset($hscpMarketPriceInfo['result']['lease_lwlmt_prc'])) {
                        $limitL = $hscpMarketPriceInfo['result']['lease_lwlmt_prc'];
                    }
                }

                if (!empty($limitH)) {
                    $limitH = $limitH * 2;

                    if ($limitH < $atcl_amt) {
                        $return = array('code' => 'ERC_02', 'message' => '최근 시세 하한가 70% ~ 상한가 200% 혹은 네이버 분양 서비스 내 평균 분양가격 하한가 60%(시세가 없는 분양권)를 벗어나는 가격입니다.');
                    }
                }

                if (!empty($limitL)) {
                    $limitL = $limitL * 0.7;

                    if ($limitL > $atcl_amt) {
                        $return = array('code' => 'ERC_03', 'message' => '최근 시세 하한가 70% ~ 상한가 200% 혹은 네이버 분양 서비스 내 평균 분양가격 하한가 60%(시세가 없는 분양권)를 벗어나는 가격입니다.');
                    }
                }

            }
        }
        return $return;
    }
}

/**
 * 공동중개 매물 stat_cd 이름 변경 
 */
function getCdChangeNm($stat_cd)
{
    switch ($stat_cd) {
        case "30":
            return "공동중개 확인중";
            break;
    }
}

/**
 * 클라이언트 아이피 체크 
 */
function getRealClientIp()
{
    $ipaddress = '';

    // HTTP_CLIENT_IP 확인 (일부 프록시에서 사용)
    if (isset($_SERVER['HTTP_CLIENT_IP'])) {
        $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
    }
    // HTTP_X_FORWARDED_FOR 확인 (프록시/로드 밸런서에서 가장 흔하게 사용)
    else if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
        // HTTP_X_FORWARDED_FOR는 쉼표로 구분된 여러 IP를 포함할 수 있음
        // 보통 첫 번째 IP가 실제 클라이언트 IP
        if (strpos($ipaddress, ',') !== false) {
            $ips = explode(',', $ipaddress);
            $ipaddress = trim($ips[0]);
        }
    }
    // 다른 X-Forwarded 헤더들 확인 (덜 흔하지만 확인 필요)
    else if (isset($_SERVER['HTTP_X_FORWARDED'])) {
        $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
    } else if (isset($_SERVER['HTTP_FORWARDED_FOR'])) {
        $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
    } else if (isset($_SERVER['HTTP_FORWARDED'])) {
        $ipaddress = $_SERVER['HTTP_FORWARDED'];
    }
    // X-Real-IP 확인 (Nginx와 같은 리버스 프록시에서 주로 사용)
    else if (isset($_SERVER['X-Real-IP'])) {
        $ipaddress = $_SERVER['X-Real-IP'];
    }
    // REMOTE_ADDR 확인 (가장 직접적인 연결에서 신뢰할 수 있음)
    else if (isset($_SERVER['REMOTE_ADDR'])) {
        $ipaddress = $_SERVER['REMOTE_ADDR'];
    } else {
        $ipaddress = '알수없음';
    }

    return $ipaddress;
}

function specailCharChange($str)
{

    if (!$str)
        return '';

    $str = str_replace(">", "〉", $str);
    $str = str_replace("<", "〈", $str);

    return $str;
}
/**
 * csv 용 
 * @param mixed $str
 * @return array|string
 */
function csvDataReplace($str)
{
    $str = str_replace("\r\n", "", $str);
    $str = str_replace(",", " ", $str);
    return $str;
}

if (!function_exists('str_contains')) {
    function str_contains($haystack, $needle)
    {
        return '' === $needle || false !== strpos($haystack, $needle);
    }
}


/**
 * Checks if a file exists at a given URL by attempting to retrieve its HTTP headers.
 * This function is specifically designed to work with NCLOUD Object Storage URLs.
 * It includes a timeout to prevent indefinite blocking due to unresponsive external resources.
 *
 * @param string $url The relative URL of the file within NCLOUD Object Storage.
 * @return bool True if the file exists (HTTP 2xx status), false otherwise or on error/timeout.
 */
function url_fileExist($url)
{
    // 134: 입력 URL이 비어있는지 확인. 비어있으면 즉시 false 반환.
    if (empty($url)) {
        return false;
    }

    // 135: NCLOUD_OBJECT_STORAGE_URL 상수를 사용하여 완전한 URL을 구성.
    // 이 상수는 NCLOUD Object Storage의 기본 URL (예: 'https://kr.object.ncloudstorage.com/')을 포함해야 합니다.
    $fullUrl = NCLOUD_OBJECT_STORAGE_URL . $url;

    // 136: (빈 라인)

    // 137: get_headers() 호출에 타임아웃을 적용하기 위한 스트림 컨텍스트 생성.
    // 'http' 옵션에 'timeout'을 설정하여 PHP가 응답을 기다리는 최대 시간을 지정합니다.
    // 'method'를 'HEAD'로 설정하여 파일 내용을 다운로드하지 않고 헤더만 요청하므로 효율적입니다.
    // 'ssl' 옵션은 HTTPS 연결 시 SSL/TLS 인증서 검증 오류를 방지합니다.
    // 운영 환경에서는 'verify_peer'와 'verify_peer_name'을 'true'로 설정하여 보안을 강화하는 것을 권장하며,
    // 적절한 CA 인증서 번들 경로를 'cafile' 또는 'capath'로 지정해야 합니다.
    $context = stream_context_create(array(
        'http' => array(
            'timeout' => 5,    // 응답을 5초 동안 기다림 (이 값은 네트워크 환경과 외부 서비스 응답 시간에 따라 조정 가능)
            'method' => 'HEAD' // HEAD 요청은 파일 존재 여부 확인에 충분하며 대역폭을 절약합니다.
        ),
        'ssl' => array(
            'verify_peer' => false, // 경고: 프로덕션 환경에서는 'true'로 설정하고 CA 인증서를 구성하여 보안을 강화해야 합니다.
            'verify_peer_name' => false, // 경고: 프로덕션 환경에서는 'true'로 설정하고 CA 인증서를 구성하여 보안을 강화해야 합니다.
        )
    ));

    // get_headers() 함수를 호출하여 HTTP 헤더를 가져옵니다.
    // @ suppression operator를 사용하여 네트워크 오류 등으로 인한 PHP 경고를 억제합니다.
    // $context를 세 번째 인자로 전달하여 위에서 정의한 타임아웃 및 기타 옵션을 적용합니다.
    $array = @get_headers($fullUrl, 0, $context);

    // 헤더를 가져오지 못했거나 (예: 타임아웃, 네트워크 오류) false가 반환된 경우 처리.
    // 이 경우 파일이 존재하지 않거나 접근할 수 없는 것으로 간주합니다.
    if ($array === false) {
        // 이 곳에 오류 로깅 또는 추가적인 오류 처리 로직을 추가할 수 있습니다.
        // 예: error_log("Failed to get headers for URL: " . $fullUrl . " at " . __FILE__ . ":" . __LINE__);
        return false;
    }

    // HTTP 응답의 첫 번째 줄(상태 코드)에서 숫자로 된 상태 코드를 추출합니다.
    // 정규식을 사용하여 "HTTP/1.1 200 OK"와 같은 문자열에서 "200" 부분을 추출합니다.
    if (preg_match('/^HTTP\/\d\.\d\s(\d{3})/', $array[0], $matches)) {
        $statusCode = (int) $matches[1];
        // HTTP 상태 코드가 2xx (성공) 범위인지 확인합니다.
        // 200 OK, 201 Created, 202 Accepted 등 성공적인 응답을 모두 포함합니다.
        return ($statusCode >= 200 && $statusCode < 300);
    }

    // 만약 위 preg_match가 실패하거나, 응답 형식이 예상과 다르면,
    // 원래 로직대로 "200 OK" 문자열 포함 여부로 한 번 더 확인합니다.
    // 이는 후방 호환성을 위한 것이며, 위의 상태 코드 확인 로직이 더 견고합니다.
    return str_contains($array[0], "200 OK");
}

function realFilePath($url)
{
    if (empty($url))
        return;

    if (url_fileExist($url)) {
        $return_url = NCLOUD_OBJECT_STORAGE_URL . $url;
    } else {
        $return_url = $url;
    }

    return $return_url;
}

function han($s)
{
    return reset(json_decode('{"s":"' . $s . '"}'));
}
// function to_han ($str) { return preg_replace('/(\\\u[a-f0-9]+)+/e','han("$0")',$str); }

if (!function_exists('db_now')) {
    /**
     * DB의 현재 시간을 지정된 포맷으로 반환하는 RawSql 생성
     * @param string|null $format MariaDB 포맷 (예: '%Y-%m-%d %H:%i:%s')
     */
    function db_now(?string $format = null)
    {
        if ($format) {

        $mysqlFormat = strtr($format, [
            'Y' => '%Y', 'y' => '%y',
            'm' => '%m', 'n' => '%c',
            'd' => '%d', 'j' => '%e',
            'H' => '%H', 'h' => '%h',
            'i' => '%i', 's' => '%s',
            'A' => '%p', 'a' => '%p',
        ]);
            // 포맷이 있으면 DATE_FORMAT(NOW(), '포맷') 형태로 생성
            return new \CodeIgniter\Database\RawSql("DATE_FORMAT(NOW(), '$mysqlFormat')");
        }
        // 포맷이 없으면 기본 NOW() 반환
        return new \CodeIgniter\Database\RawSql('NOW()');
    }
}

/**
 * 비밀번호 문자 조합 검사
 * - 영문 대문자 / 소문자 / 숫자 / 특수문자 중 최소 $minTypes 종류 이상
 */
function checkPasswordTypes(string $password, int $minTypes = 2): bool
{
    $types = 0;

    // 대문자
    if (preg_match('/[A-Z]/', $password)) {
        $types++;
    }

    // 소문자
    if (preg_match('/[a-z]/', $password)) {
        $types++;
    }

    // 숫자
    if (preg_match('/[0-9]/', $password)) {
        $types++;
    }

    // 특수문자 (공백 제외)
    if (preg_match('/[^A-Za-z0-9]/', $password)) {
        $types++;
    }

    return $types >= $minTypes;
}

/**
 * 소유자 구분코드 변환
 */
function getOwnerTypeCodeNo($code)
{
    switch ($code) {
        case "INDIV":
            return "0";
            break;
        case "CORP":
            return "1";
            break;
        case "FRGNR":
            return "2";
            break;
        case "DELEG":
            return "3";
            break;
        default:
            return null;
            break;
    }
}
