package com.bb.util;

import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Date;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

// XML Parser 패키지 (Spring Boot 3/JDK 17 환경에 맞춰 javax 유지)
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import com.fasterxml.jackson.databind.ObjectMapper;

public class StringUtils {
    
    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 문자열이 JSON 형태인지 검사
     */
    public static boolean isJson(String input) {
        if (input == null || input.trim().isEmpty()) return false;

        try {
            objectMapper.readTree(input);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 문자열이 URL 형태인지 정규식으로 검사
     */
    public static boolean isUrl(String input) {
        if (input == null || input.trim().isEmpty()) return false;

        // http, https, ftp 등 다양한 프로토콜 대응
        String regex = "^(https?|ftp)://[^\\s/$.?#].[^\\s]*$";
        return input.matches(regex);
    }

    /**
     * 문자열이 JSON / URL / 기타 중 어떤 형태인지 구분
     */
    public static String getType(String input) {
        if (isJson(input)) return "JSON";
        if (isUrl(input)) return "URL";
        return "UNKNOWN";
    }

    /**
     * 문자열이 NULL이거나 빈값인지 검사
     * * @param string
     * @return
     */
    public static boolean isEmpty(String string) {
        return string == null || string.trim().isEmpty();
    }

    /**
     * null값인 문자열 기본값으로 대체
     * * @param string
     * @param defaultValue
     * @return
     */
    public static String nullValue(String string, String defaultValue) {
        if (isEmpty(string)) {
            return defaultValue;
        } else {
            return string;
        }
    }

    /**
     * null값인 문자열 기본값으로 대체
     * * @param string
     * @param defaultValue
     * @return
     */
    public static String nullValue(String string, int defaultValue) {
        if (isEmpty(string)) {
            return String.valueOf(defaultValue);
        } else {
            return string;
        }
    }

    /**
     * null값인 문자열 기본값으로 대체
     * * @param string
     * @param defaultValue
     * @return
     */
    public static String nullValue(String string, float defaultValue) {
        if (isEmpty(string)) {
            return String.valueOf(defaultValue);
        } else {
            return string;
        }
    }

    /**
     * null값인 문자열 기본값으로 대체
     * * @param string
     * @return
     */
    public static String nullValue(String string) {
        return nullValue(string, "");
    }

    /**
     * 문자열 배열을 합친다
     * * @param strings
     * @param separater
     * @return
     */
    public static String join(String[] strings, String separater) {
        String joinedString = "";

        for (int i = 0; i < strings.length; i++) {
            joinedString += (joinedString.length() > 0 ? separater : "") + strings[i];
        }

        return joinedString;
    }

    /**
     * Map의 키값을 문자열 배열로 얻는다
     * * @param map
     * @return
     */
    public static String[] getArrayFrom(Map map) {
        String[] strings = new String[map.size()];

        int i = 0;
        for (Object key : map.keySet()) {
            strings[i++] = key.toString();
        }

        return strings;
    }

    /**
     * Map의 키값을 문자열로 얻는다
     * * @param map
     * @param separater
     * @return
     */
    public static String getStringFrom(Map map, String separater) {
        return join(getArrayFrom(map), separater);
    }

    /**
     * 닉네임용 문자 난수발생기
     * * @param length
     * @return
     */
    public static String generateString(int length) {

        Random rnd = new Random();
        StringBuffer buf = new StringBuffer();
        String returnStr = "";

        for (int i = 0; i < length; i++) {
            if (i == 0 || rnd.nextBoolean()) {
                buf.append((char) ((int) (rnd.nextInt(26)) + 97)); // 영문소문자 a ~ z
            } else {
                buf.append((rnd.nextInt(9) + 1)); // 숫자 1 ~ 9
            }

        }
        returnStr = buf.toString();

        if (returnStr.indexOf("xxx") + returnStr.indexOf("sex") + returnStr.indexOf("fuck") + returnStr.indexOf("nude")
                + returnStr.indexOf("666") >= 0) {
            returnStr = generateString(length);
        }
        return returnStr;
    }

    /**
     * 인증번호용 난수발생기
     * * @param length
     * @return
     */
    public static String generateNumber(int length) {

        String numStr = "1";
        String plusNumStr = "1";

        for (int i = 0; i < length; i++) {
            numStr += "0";

            if (i != length - 1) {
                plusNumStr += "0";
            }
        }

        Random random = new Random();
        int result = random.nextInt(Integer.parseInt(numStr)) + Integer.parseInt(plusNumStr);

        if (result > Integer.parseInt(numStr)) {
            result = result - Integer.parseInt(plusNumStr);
        }

        return String.valueOf(result);
    }

    public static String mask(String origin, int length, String mask) {
        StringBuilder masked = new StringBuilder(origin.substring(0, length));

        for (int i = 0; i < origin.length() - length; i++) {
            masked.append(mask);
        }

        return masked.toString();
    }

    public static String numberFormat(Object number, String pattern) {
        DecimalFormat df = new DecimalFormat(pattern);
        return df.format(number);
    }

    public static String randomCharNum(int wordLength) {
        Random r = new Random();
        StringBuilder sb = new StringBuilder(wordLength);
        for(int i = 0; i < wordLength; i++) {
            if(r.nextBoolean()){
                sb.append((char)((int)(r.nextInt(26))+97));
            }else{
                sb.append((r.nextInt(10)));
            }
        }
        return sb.toString();
    }

    /**
     * MD5 해시 후 Hex 문자열 변환
     * (에러 원인인 DatatypeConverter 패키지 대신 표준 StringBuilder 사용)
     */
    public static String md5AndHex(String plainText) throws NoSuchAlgorithmException {
        
        //MessageDigest 인스턴스 생성(MD5)
        MessageDigest md = MessageDigest.getInstance("MD5");
        
        //해쉬값 업데이트
        md.update(plainText.getBytes());
        byte[] digest = md.digest();

        //Byte To Hex String (기존 DatatypeConverter 대체 로직)
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
    
    
     public static String makeApiKey(String param) {
          
            int n = 20; // n자리 쿠폰 
            char[] chs = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
                 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
                 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ,'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };

        
            Random rd = new Random();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < n; i++) {
                char ch = chs[rd.nextInt(chs.length)];
                sb.append(ch);
            }
            return param + sb.toString();
          
          
      }
     
     public static String makeApiKey(String param, int n) {
          
           
            char[] chs = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
                 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
                 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ,'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };

        
            Random rd = new Random();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < n; i++) {
                char ch = chs[rd.nextInt(chs.length)];
                sb.append(ch);
            }
            return param + sb.toString();
          
          
      }
        
    public static String makeRandomKey(int length) {
        int leftLimit = 48; // numeral '0'
        int rightLimit = 122; // letter 'z'
        int targetStringLength = length;
        Random random = new Random();
        String generatedString = random.ints(leftLimit, rightLimit + 1)
                                       .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97))
                                       .limit(targetStringLength)
                                       .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
                                       .toString();
        System.out.println("### makeRandomKey : " + generatedString);
        return generatedString;
    }
    
    public static String replaceAll(String str, String p, String a) {
        

        return str.replaceAll(p, a);
    }
    
     public static Document convertStringToXMLDocument(String xmlString) {
            //Parser that produces DOM object trees from XML content
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            //API to obtain DOM Document instance
            DocumentBuilder builder = null;
            try {
              //Create DocumentBuilder with default configuration
              builder = factory.newDocumentBuilder();

              //Parse the content to Document object
              Document doc = builder.parse(new InputSource(new StringReader(xmlString)));
              return doc;
            } catch (Exception e) {
              e.printStackTrace();
            }
            return null;
          }
     
     public static String hashing(String msg, String key)  {
         String result ="";
         try {
            Encoder encoder = Base64.getEncoder();
            
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            
            byte[] bt = sha256_HMAC.doFinal(msg.getBytes());
            String hash = encoder.encodeToString(bt);
             result = hash;
         }catch(Exception e) {}
            return result;
        }
     
     
     
     public static class StringBuilderPlus {

            private StringBuilder sb;

            public StringBuilderPlus(){
                 sb = new StringBuilder();
            }

            public void append(String str)
            {
                sb.append(str != null ? str : "");
            }

            public void appendLine(String str)
            {
                sb.append(str != null ? str : "").append(System.getProperty("line.separator"));
            }

            public String toString()
            {
                return sb.toString();
            }
        }

     
    public static boolean isNumberic(String str) {
        boolean result = true;
        for(char c : str.toCharArray()){
            if(!Character.isDigit(c)) {
                result = false;
                break;
            }
        }
        return result;
    }
    
    public static String extractLink(String script) { // 정규 표현식을 사용하여 location.href 값 추출
        String regex = "location\\.href='(.*?)'";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(script);

        if (matcher.find()) {
            return matcher.group(1);
        } else {
            throw new IllegalArgumentException("유효한 링크를 찾을 수 없습니다.");
        }
    }
    
    public static String convertToDateOnly(String dateTime) {
        String result = "";
        try { // Define the input and output date formats
            SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd");

            // Parse the input date string
            Date date = inputFormat.parse(dateTime);

            // Format the date to the desired output format
            result = outputFormat.format(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return result;
    }
    
    /**
     * DB 저장용 문자열을 안전하게 잘라주는 메서드
     * @param body 자를 대상 문자열 (예: response.getBody())
     * @param limitBytes 최대 허용 바이트 수 (예: VARCHAR(100) → 100)
     * @return 잘린 문자열 (너무 길면 ... 표시 포함)
     */
    public static String truncateBodyForDb(String body, int limitBytes) {
        if (body == null) return null;

        byte[] utf8Bytes = body.getBytes(StandardCharsets.UTF_8);
        if (utf8Bytes.length <= limitBytes) {
            return body;
        }

        // 바이트 단위로 자르기
        int endIndex = body.length();
        while (endIndex > 0) {
            String candidate = body.substring(0, endIndex);
            if (candidate.getBytes(StandardCharsets.UTF_8).length <= limitBytes - 3) { // "..." 고려
                return candidate + "...";
            }
            endIndex--;
        }

        // 혹시라도 한 글자도 안 남는 경우 (이론상 거의 없음)
        return body.substring(0, Math.min(body.length(), 3)) + "...";
    }
}