package com.erp.service;

import com.erp.common.BaseService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Slf4j
public class ExternalCallService extends BaseService {

    private static final int DEFAULT_TIMEOUT = 30000;
    private static final Pattern TEMPLATE_PATTERN = Pattern.compile("\\{\\{(\\w+)}}");

    @Autowired private ObjectMapper objectMapper;

    // ── 외부 호출 실행 ──────────────────────────────────────────────────────────

    @SuppressWarnings("unchecked")
    public Map<String, Object> executeExternalCall(Map<String, Object> request) {
        long startTime = System.currentTimeMillis();
        try {
            Map<String, Object> settings = (Map<String, Object>) request.get("settings");
            Map<String, Object> templateData = (Map<String, Object>) request.get("template_data");

            String callType = (String) settings.get("call_type");
            Map<String, Object> result;

            switch (callType) {
                case "rest-api" -> result = executeRestApiCall(settings, templateData);
                case "email" -> throw new UnsupportedOperationException("이메일 발송 기능은 아직 구현되지 않았습니다.");
                case "ftp" -> throw new UnsupportedOperationException("FTP 호출은 아직 구현되지 않았습니다.");
                case "queue" -> throw new UnsupportedOperationException("메시지 큐 호출은 아직 구현되지 않았습니다.");
                default -> throw new IllegalArgumentException("지원되지 않는 호출 타입: " + callType);
            }

            result.put("execution_time", System.currentTimeMillis() - startTime);
            result.put("timestamp", new Date());
            return result;
        } catch (Exception e) {
            Map<String, Object> errorResult = new LinkedHashMap<>();
            errorResult.put("success", false);
            errorResult.put("error", e.getMessage());
            errorResult.put("execution_time", System.currentTimeMillis() - startTime);
            errorResult.put("timestamp", new Date());
            return errorResult;
        }
    }

    // ── REST API 호출 분기 ──────────────────────────────────────────────────────

    @SuppressWarnings("unchecked")
    private Map<String, Object> executeRestApiCall(Map<String, Object> settings,
                                                    Map<String, Object> templateData) {
        String apiType = (String) settings.getOrDefault("api_type", "generic");
        return switch (apiType) {
            case "slack" -> executeSlackWebhook(settings, templateData);
            case "kakao-talk" -> executeKakaoTalkApi(settings, templateData);
            case "discord" -> executeDiscordWebhook(settings, templateData);
            default -> executeGenericApi(settings, templateData);
        };
    }

    // ── 슬랙 웹훅 ──────────────────────────────────────────────────────────────

    private Map<String, Object> executeSlackWebhook(Map<String, Object> settings,
                                                     Map<String, Object> templateData) {
        String message = processTemplate((String) settings.get("message"), templateData);

        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("text", message);
        payload.put("channel", settings.get("channel"));
        payload.put("username", settings.getOrDefault("username", "DataFlow Bot"));
        payload.put("icon_emoji", settings.getOrDefault("icon_emoji", ":robot_face:"));

        Map<String, String> headers = Map.of("Content-Type", "application/json");
        int timeout = getTimeout(settings);

        return makeHttpRequest(
            (String) settings.get("webhook_url"), "POST", headers,
            toJsonString(payload), timeout);
    }

    // ── 카카오톡 API ────────────────────────────────────────────────────────────

    private Map<String, Object> executeKakaoTalkApi(Map<String, Object> settings,
                                                     Map<String, Object> templateData) {
        String message = processTemplate((String) settings.get("message"), templateData);

        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("object_type", "text");
        payload.put("text", message);
        payload.put("link", Map.of(
            "web_url", "https://developers.kakao.com",
            "mobile_web_url", "https://developers.kakao.com"
        ));

        String body = "template_object=" + URLEncoder.encode(toJsonString(payload), StandardCharsets.UTF_8);

        Map<String, String> headers = Map.of(
            "Authorization", "Bearer " + settings.get("access_token"),
            "Content-Type", "application/x-www-form-urlencoded"
        );
        int timeout = getTimeout(settings);

        return makeHttpRequest(
            "https://kapi.kakao.com/v2/api/talk/memo/default/send",
            "POST", headers, body, timeout);
    }

    // ── 디스코드 웹훅 ──────────────────────────────────────────────────────────

    private Map<String, Object> executeDiscordWebhook(Map<String, Object> settings,
                                                       Map<String, Object> templateData) {
        String message = processTemplate((String) settings.get("message"), templateData);

        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("content", message);
        payload.put("username", settings.getOrDefault("username", "시스템 알리미"));
        if (settings.get("avatar_url") != null) {
            payload.put("avatar_url", settings.get("avatar_url"));
        }

        Map<String, String> headers = Map.of("Content-Type", "application/json");
        int timeout = getTimeout(settings);

        return makeHttpRequest(
            (String) settings.get("webhook_url"), "POST", headers,
            toJsonString(payload), timeout);
    }

    // ── 일반 REST API ──────────────────────────────────────────────────────────

    @SuppressWarnings("unchecked")
    private Map<String, Object> executeGenericApi(Map<String, Object> settings,
                                                   Map<String, Object> templateData) {
        String body = (String) settings.get("body");
        if (body != null && templateData != null) {
            body = processTemplate(body, templateData);
        }

        Map<String, String> headers = new LinkedHashMap<>();
        Object rawHeaders = settings.get("headers");
        if (rawHeaders instanceof Map) {
            ((Map<String, Object>) rawHeaders).forEach((k, v) ->
                headers.put(k, v != null ? v.toString() : ""));
        }

        // 인증 처리
        Object authObj = settings.get("authentication");
        if (authObj instanceof Map) {
            Map<String, Object> auth = (Map<String, Object>) authObj;
            String authType = (String) auth.getOrDefault("type", "none");
            switch (authType) {
                case "api-key" -> {
                    String apiKey = (String) auth.get("api_key");
                    if (apiKey != null) headers.put("X-API-Key", apiKey);
                }
                case "basic" -> {
                    String username = (String) auth.get("username");
                    String password = (String) auth.get("password");
                    if (username != null && password != null) {
                        String credentials = Base64.getEncoder().encodeToString(
                            (username + ":" + password).getBytes(StandardCharsets.UTF_8));
                        headers.put("Authorization", "Basic " + credentials);
                    }
                }
                case "bearer" -> {
                    String token = (String) auth.get("token");
                    if (token != null) headers.put("Authorization", "Bearer " + token);
                }
                case "custom" -> {
                    String headerName = (String) auth.get("header_name");
                    String headerValue = (String) auth.get("header_value");
                    if (headerName != null && headerValue != null) {
                        headers.put(headerName, headerValue);
                    }
                }
            }
        }

        String method = (String) settings.getOrDefault("method", "GET");
        int timeout = getTimeout(settings);

        return makeHttpRequest((String) settings.get("url"), method, headers, body, timeout);
    }

    // ── HTTP 요청 공통 ──────────────────────────────────────────────────────────

    private Map<String, Object> makeHttpRequest(String url, String method,
                                                 Map<String, String> headers,
                                                 String body, int timeout) {
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            if (headers != null) {
                headers.forEach(httpHeaders::set);
            }

            HttpMethod httpMethod = HttpMethod.valueOf(method.toUpperCase());

            // GET, HEAD는 body 제외
            String requestBody = null;
            if (!"GET".equals(method.toUpperCase()) && !"HEAD".equals(method.toUpperCase())) {
                requestBody = body;
            }

            RequestEntity<String> requestEntity = new RequestEntity<>(
                requestBody, httpHeaders, httpMethod, URI.create(url));

            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<String> response = restTemplate.exchange(requestEntity, String.class);

            boolean success = response.getStatusCode().is2xxSuccessful();

            Map<String, Object> result = new LinkedHashMap<>();
            result.put("success", success);
            result.put("status_code", response.getStatusCode().value());
            result.put("response", response.getBody());
            return result;
        } catch (Exception e) {
            if (e.getMessage() != null && e.getMessage().contains("timeout")) {
                throw new RuntimeException("요청 시간 초과 (" + timeout + "ms)");
            }
            throw new RuntimeException("HTTP 요청 실패: " + e.getMessage(), e);
        }
    }

    // ── 설정 검증 ──────────────────────────────────────────────────────────────

    @SuppressWarnings("unchecked")
    public Map<String, Object> validateExternalCallSettings(Map<String, Object> settings) {
        List<String> errors = new ArrayList<>();

        String callType = (String) settings.get("call_type");
        if ("rest-api".equals(callType)) {
            String apiType = (String) settings.getOrDefault("api_type", "generic");
            switch (apiType) {
                case "slack" -> {
                    if (isBlank(settings, "webhook_url")) errors.add("슬랙 웹훅 URL이 필요합니다.");
                    if (isBlank(settings, "message")) errors.add("슬랙 메시지가 필요합니다.");
                }
                case "kakao-talk" -> {
                    if (isBlank(settings, "access_token")) errors.add("카카오톡 액세스 토큰이 필요합니다.");
                    if (isBlank(settings, "message")) errors.add("카카오톡 메시지가 필요합니다.");
                }
                case "discord" -> {
                    if (isBlank(settings, "webhook_url")) errors.add("디스코드 웹훅 URL이 필요합니다.");
                    if (isBlank(settings, "message")) errors.add("디스코드 메시지가 필요합니다.");
                }
                default -> {
                    if (isBlank(settings, "url")) errors.add("API URL이 필요합니다.");
                    if (isBlank(settings, "method")) errors.add("HTTP 메서드가 필요합니다.");
                }
            }
        } else if ("email".equals(callType)) {
            if (isBlank(settings, "smtp_host")) errors.add("SMTP 호스트가 필요합니다.");
            if (isBlank(settings, "to_email")) errors.add("수신 이메일이 필요합니다.");
            if (isBlank(settings, "subject")) errors.add("이메일 제목이 필요합니다.");
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("valid", errors.isEmpty());
        result.put("errors", errors);
        return result;
    }

    // ── 지원 타입 목록 ──────────────────────────────────────────────────────────

    public Map<String, Object> getExternalCallTypeList() {
        Map<String, Object> types = new LinkedHashMap<>();

        Map<String, Object> restApi = new LinkedHashMap<>();
        restApi.put("name", "REST API 호출");
        restApi.put("subtypes", Map.of(
            "slack", "슬랙 웹훅",
            "kakao-talk", "카카오톡 알림",
            "discord", "디스코드 웹훅",
            "generic", "일반 REST API"
        ));
        types.put("rest-api", restApi);

        types.put("email", Map.of("name", "이메일 전송", "status", "구현 예정"));
        types.put("ftp", Map.of("name", "FTP 업로드", "status", "구현 예정"));
        types.put("queue", Map.of("name", "메시지 큐", "status", "구현 예정"));

        return types;
    }

    // ── 템플릿 처리 ─────────────────────────────────────────────────────────────

    private String processTemplate(String template, Map<String, Object> data) {
        if (template == null || data == null || data.isEmpty()) return template;

        StringBuffer sb = new StringBuffer();
        Matcher matcher = TEMPLATE_PATTERN.matcher(template);
        while (matcher.find()) {
            String key = matcher.group(1);
            Object value = data.get(key);
            matcher.appendReplacement(sb, Matcher.quoteReplacement(
                value != null ? String.valueOf(value) : ""));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    // ── 유틸 ────────────────────────────────────────────────────────────────────

    private boolean isBlank(Map<String, Object> map, String key) {
        Object val = map.get(key);
        return val == null || (val instanceof String str && str.isBlank());
    }

    private int getTimeout(Map<String, Object> settings) {
        Object t = settings.get("timeout");
        if (t instanceof Number n) return n.intValue();
        if (t instanceof String s) {
            try { return Integer.parseInt(s); } catch (NumberFormatException ignore) {}
        }
        return DEFAULT_TIMEOUT;
    }

    private String toJsonString(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            return "{}";
        }
    }
}
