package com.erp.service;

import com.erp.common.BaseService;
import com.erp.util.PasswordEncryption;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class MailSendSimpleService extends BaseService {

    private static final String NS_SEND     = "mailSendSimple.";
    private static final String NS_HISTORY  = "mailSentHistory.";
    private static final String NS_TEMPLATE = "mailTemplateFile.";

    @Autowired
    private ObjectMapper objectMapper;

    @Value("${encryption.secret-key:ilshin-erp-encryption-key-2024}")
    private String encryptionSecretKey;

    private PasswordEncryption passwordEncryption;

    @PostConstruct
    public void init() {
        this.passwordEncryption = new PasswordEncryption(encryptionSecretKey);
    }

    // ── 단건 메일 발송 ────────────────────────────────────────────────────────

    /**
     * POST /simple
     * 단건 메일 발송 (첨부파일 지원, multipart/form-data)
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> sendMail(Map<String, Object> params) {
        String accountId   = str(params.get("account_id"));
        String companyCode = str(params.get("company_code"));
        String templateId  = str(params.get("template_id"));
        String subject     = str(params.get("subject"));
        String customHtml  = str(params.get("custom_html"));

        // JSON 문자열 → List 파싱 (multipart form-data의 JSON 문자열 필드)
        List<String> to  = parseJsonList(str(params.get("to")));
        List<String> cc  = parseJsonList(str(params.get("cc")));
        List<String> bcc = parseJsonList(str(params.get("bcc")));
        Map<String, String> variables = parseJsonMap(str(params.get("variables")));
        List<Object> modifiedComponents = parseJsonObjectList(str(params.get("modified_template_components")));
        List<MultipartFile> attachments = (List<MultipartFile>) params.get("attachments");

        // 계정 조회
        Map<String, Object> accountParams = new HashMap<>();
        accountParams.put("account_id", accountId);
        accountParams.put("company_code", companyCode);
        Map<String, Object> account = sqlSession.selectOne(NS_SEND + "get_mail_account", accountParams);
        if (account == null) {
            throw new IllegalArgumentException("메일 계정을 찾을 수 없습니다.");
        }
        if (!"active".equals(str(account.get("status")))) {
            throw new IllegalStateException("비활성 상태의 계정입니다.");
        }

        // HTML 생성
        String htmlContent;
        String templateName = null;
        if (templateId != null && !templateId.isBlank()) {
            Map<String, Object> tplParams = new HashMap<>();
            tplParams.put("id", templateId);
            tplParams.put("company_code", companyCode);
            Map<String, Object> template = sqlSession.selectOne(NS_TEMPLATE + "get_mail_template_file_info", tplParams);
            if (template == null) throw new IllegalArgumentException("템플릿을 찾을 수 없습니다.");
            templateName = str(template.get("name"));
            if (modifiedComponents != null && !modifiedComponents.isEmpty()) {
                template.put("components", modifiedComponents);
            }
            htmlContent = renderTemplate(template, variables);
            if (customHtml != null && !customHtml.isBlank()) {
                htmlContent = mergeTemplateAndCustomContent(htmlContent, customHtml);
            }
        } else {
            htmlContent = customHtml != null ? customHtml : "";
        }
        if (htmlContent.isBlank()) throw new IllegalArgumentException("메일 내용이 없습니다.");

        // 비밀번호 복호화
        String decryptedPassword = passwordEncryption.decrypt(str(account.get("smtp_password")));

        // JavaMailSender 동적 생성 및 발송
        JavaMailSenderImpl sender = createMailSender(account, decryptedPassword);
        String messageId = null;
        List<String> accepted = new ArrayList<>();
        List<String> rejected = new ArrayList<>();
        boolean success = false;
        String errorMsg  = null;

        try {
            MimeMessage message = sender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

            String accountEmail = str(account.get("email"));
            String accountName  = str(account.get("name"));
            helper.setFrom(new InternetAddress(accountEmail, accountName, StandardCharsets.UTF_8.name()));
            helper.setTo(to.toArray(new String[0]));
            helper.setSubject(replaceVariables(subject, variables));
            helper.setText(htmlContent, true);

            if (!cc.isEmpty())  helper.setCc(cc.toArray(new String[0]));
            if (!bcc.isEmpty()) helper.setBcc(bcc.toArray(new String[0]));

            // 첨부파일
            if (attachments != null) {
                List<String> parsedFileNames = parseJsonList(str(params.get("file_names")));
                for (int i = 0; i < attachments.size(); i++) {
                    MultipartFile file = attachments.get(i);
                    String filename = (i < parsedFileNames.size() && !parsedFileNames.get(i).isBlank())
                            ? parsedFileNames.get(i)
                            : file.getOriginalFilename();
                    if (filename == null) filename = "attachment_" + i;
                    helper.addAttachment(filename, file);
                }
            }

            sender.send(message);
            messageId = message.getMessageID();
            accepted.addAll(to);
            success = true;
            log.info("메일 발송 성공: accountId={}, to={}", accountId, to);
        } catch (Exception e) {
            rejected.addAll(to);
            errorMsg = e.getMessage();
            log.error("메일 발송 실패: accountId={}, error={}", accountId, e.getMessage());
        }

        // 발송 이력 저장 (실패해도 메인 결과에 영향 없음)
        saveHistory(account, companyCode, to, cc, bcc,
                replaceVariables(subject, variables), htmlContent,
                templateId, templateName, attachments,
                success ? "success" : "failed",
                messageId, accepted, rejected, errorMsg);

        if (success) {
            Map<String, Object> result = new LinkedHashMap<>();
            result.put("success", true);
            result.put("message_id", messageId);
            result.put("accepted", accepted);
            result.put("rejected", rejected);
            return result;
        } else {
            throw new RuntimeException(errorMsg != null ? errorMsg : "메일 발송 실패");
        }
    }

    // ── 대량 발송 ────────────────────────────────────────────────────────────

    /**
     * POST /bulk
     * 대량 메일 발송 (순차 처리, 500ms 간격)
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> sendBulkMail(Map<String, Object> body) {
        String accountId   = str(body.get("account_id"));
        String companyCode = str(body.get("company_code"));
        String templateId  = str(body.get("template_id"));
        String customHtml  = str(body.get("custom_html"));
        String subject     = str(body.get("subject"));

        List<Map<String, Object>> recipients;
        try {
            Object raw = body.get("recipients");
            if (raw instanceof List) {
                recipients = (List<Map<String, Object>>) raw;
            } else {
                recipients = objectMapper.readValue(str(raw), new TypeReference<>() {});
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("수신자 목록 형식이 올바르지 않습니다.");
        }

        List<Map<String, Object>> results = new ArrayList<>();
        int successCount = 0;
        int failedCount  = 0;

        log.info("대량 발송 시작: {}명", recipients.size());

        for (Map<String, Object> recipient : recipients) {
            String email = str(recipient.get("email"));
            Map<String, String> variables = recipient.get("variables") instanceof Map
                    ? (Map<String, String>) recipient.get("variables") : Collections.emptyMap();

            Map<String, Object> sendParams = new LinkedHashMap<>();
            sendParams.put("company_code", companyCode);
            sendParams.put("account_id",  accountId);
            sendParams.put("template_id", templateId);
            sendParams.put("custom_html", customHtml);
            sendParams.put("to", "[\"" + email + "\"]");
            sendParams.put("subject", subject);
            try {
                sendParams.put("variables", objectMapper.writeValueAsString(variables));
            } catch (Exception ignored) {}

            Map<String, Object> item = new LinkedHashMap<>();
            item.put("email", email);
            try {
                Map<String, Object> r = sendMail(sendParams);
                item.put("success",   true);
                item.put("message_id", r.get("message_id"));
                successCount++;
            } catch (Exception e) {
                item.put("success", false);
                item.put("error",   e.getMessage());
                failedCount++;
            }
            results.add(item);

            // 스팸 방지: 500ms 간격
            try { Thread.sleep(500); } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }

        log.info("대량 발송 완료: 성공 {}, 실패 {}", successCount, failedCount);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("total",   recipients.size());
        result.put("success", successCount);
        result.put("failed",  failedCount);
        result.put("results", results);
        return result;
    }

    // ── SMTP 연결 테스트 ──────────────────────────────────────────────────────

    /**
     * POST /test-connection
     */
    public Map<String, Object> testConnection(Map<String, Object> body) {
        String accountId   = str(body.get("account_id"));
        String companyCode = str(body.get("company_code"));

        Map<String, Object> params = new HashMap<>();
        params.put("account_id",   accountId);
        params.put("company_code", companyCode);
        Map<String, Object> account = sqlSession.selectOne(NS_SEND + "get_mail_account", params);
        if (account == null) {
            Map<String, Object> r = new LinkedHashMap<>();
            r.put("success", false);
            r.put("message", "메일 계정을 찾을 수 없습니다.");
            return r;
        }
        try {
            String decryptedPassword = passwordEncryption.decrypt(str(account.get("smtp_password")));
            JavaMailSenderImpl sender = createMailSender(account, decryptedPassword);
            sender.testConnection();
            Map<String, Object> r = new LinkedHashMap<>();
            r.put("success", true);
            r.put("message", "SMTP 연결이 성공했습니다.");
            return r;
        } catch (Exception e) {
            log.warn("SMTP 연결 테스트 실패: {}", e.getMessage());
            Map<String, Object> r = new LinkedHashMap<>();
            r.put("success", false);
            r.put("message", "SMTP 연결 실패: " + e.getMessage());
            return r;
        }
    }

    // ── JavaMailSender 동적 설정 ──────────────────────────────────────────────

    private JavaMailSenderImpl createMailSender(Map<String, Object> account, String decryptedPassword) {
        JavaMailSenderImpl sender = new JavaMailSenderImpl();
        sender.setHost(str(account.get("smtp_host")));

        Object portObj = account.get("smtp_port");
        int port = portObj != null ? ((Number) portObj).intValue() : 587;
        sender.setPort(port);
        sender.setUsername(str(account.get("smtp_username")));
        sender.setPassword(decryptedPassword);
        sender.setDefaultEncoding("UTF-8");

        Properties props = sender.getJavaMailProperties();
        boolean secure = (port == 465);
        if (!secure) {
            Object smtpSecureObj = account.get("smtp_secure");
            secure = Boolean.TRUE.equals(smtpSecureObj)
                    || "true".equalsIgnoreCase(str(smtpSecureObj));
        }
        props.put("mail.smtp.auth", "true");
        if (secure) {
            props.put("mail.smtp.ssl.enable", "true");
        } else {
            props.put("mail.smtp.starttls.enable", "true");
        }
        props.put("mail.smtp.connectiontimeout", "30000");
        props.put("mail.smtp.timeout",           "30000");
        props.put("mail.smtp.writetimeout",      "30000");
        return sender;
    }

    // ── 발송 이력 저장 ────────────────────────────────────────────────────────

    private void saveHistory(Map<String, Object> account, String companyCode,
                              List<String> to, List<String> cc, List<String> bcc,
                              String subject, String htmlContent,
                              String templateId, String templateName,
                              List<MultipartFile> attachments,
                              String status, String messageId,
                              List<String> accepted, List<String> rejected, String errorMsg) {
        try {
            Map<String, Object> h = new LinkedHashMap<>();
            h.put("company_code",   companyCode);
            h.put("account_id",     account.get("id"));
            h.put("account_name",   account.get("name"));
            h.put("account_email",  account.get("email"));
            h.put("to_addresses",   String.join(",", to));
            h.put("cc_addresses",   cc.isEmpty()  ? null : String.join(",", cc));
            h.put("bcc_addresses",  bcc.isEmpty() ? null : String.join(",", bcc));
            h.put("subject",       subject);
            h.put("html_content",   htmlContent);
            h.put("template_id",    templateId);
            h.put("template_name",  templateName);
            h.put("status",        status);
            h.put("message_id",     messageId);
            h.put("accepted",      accepted.isEmpty() ? null : String.join(",", accepted));
            h.put("rejected",      rejected.isEmpty() ? null : String.join(",", rejected));
            h.put("error_message",  errorMsg);
            if (attachments != null && !attachments.isEmpty()) {
                h.put("attachment_count", attachments.size());
            }
            sqlSession.insert(NS_HISTORY + "insert_mail_sent_history", h);
        } catch (Exception e) {
            log.warn("발송 이력 저장 실패 (무시됨): {}", e.getMessage());
        }
    }

    // ── 템플릿 렌더링 ─────────────────────────────────────────────────────────

    @SuppressWarnings("unchecked")
    private String renderTemplate(Map<String, Object> template, Map<String, String> variables) {
        List<Map<String, Object>> components = new ArrayList<>();
        Object rawComponents = template.get("components");
        if (rawComponents instanceof List) {
            components = (List<Map<String, Object>>) rawComponents;
        } else if (rawComponents != null) {
            try {
                components = objectMapper.readValue(rawComponents.toString(), new TypeReference<>() {});
            } catch (Exception e) {
                log.warn("템플릿 컴포넌트 파싱 실패: {}", e.getMessage());
            }
        }

        StringBuilder html = new StringBuilder("""
                <!DOCTYPE html>
                <html>
                <head>
                  <meta charset="UTF-8">
                  <meta name="viewport" content="width=device-width, initial-scale=1.0">
                </head>
                <body style="margin:0;padding:0;background-color:#ffffff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;">
                  <table role="presentation" style="width:100%;border-collapse:collapse;">
                    <tr><td style="padding:20px;">
                """);

        for (Map<String, Object> comp : components) {
            String type = str(comp.get("type"));
            if (type == null) continue;
            switch (type) {
                case "text" -> {
                    String content = replaceVariables(str(comp.get("content")), variables);
                    html.append("<div style=\"margin:0 0 20px 0;color:")
                            .append(str(comp.getOrDefault("color", "#333")))
                            .append(";font-size:").append(str(comp.getOrDefault("font_size", "15px")))
                            .append(";line-height:1.6;\">").append(content != null ? content : "").append("</div>\n");
                }
                case "button" -> {
                    String btnText = replaceVariables(str(comp.get("text")), variables);
                    Object styles  = comp.get("styles");
                    Map<String, Object> stylesMap = styles instanceof Map ? (Map<String, Object>) styles : Collections.emptyMap();
                    String bgColor   = str(stylesMap.getOrDefault("background_color", comp.getOrDefault("background_color", "#007bff")));
                    String textColor = str(stylesMap.getOrDefault("color", comp.getOrDefault("text_color", "#fff")));
                    html.append("<div style=\"margin:30px 0;\"><a href=\"")
                            .append(str(comp.getOrDefault("url", "#")))
                            .append("\" style=\"display:inline-block;padding:14px 28px;background-color:")
                            .append(bgColor).append(";color:").append(textColor)
                            .append(";text-decoration:none;border-radius:6px;font-weight:600;\">")
                            .append(btnText != null ? btnText : "Button").append("</a></div>\n");
                }
                case "image" ->
                    html.append("<div style=\"margin:20px 0;\"><img src=\"")
                            .append(str(comp.get("src"))).append("\" alt=\"")
                            .append(str(comp.getOrDefault("alt", "")))
                            .append("\" style=\"max-width:100%;height:auto;\"/></div>\n");
                case "spacer" ->
                    html.append("<div style=\"height:").append(str(comp.getOrDefault("height", "20px"))).append(";\"></div>\n");
                case "divider" ->
                    html.append("<hr style=\"border:none;border-top:1px solid #e5e7eb;margin:20px 0;\">\n");
                case "footer" -> {
                    html.append("<div style=\"text-align:center;padding:24px 16px;background:#f9fafb;border-top:1px solid #e5e7eb;font-size:14px;color:#6b7280;\">");
                    if (comp.get("company_name") != null)
                        html.append("<div style=\"font-weight:600;\">").append(str(comp.get("company_name"))).append("</div>");
                    if (comp.get("copyright") != null)
                        html.append("<div style=\"font-size:12px;\">").append(str(comp.get("copyright"))).append("</div>");
                    html.append("</div>\n");
                }
                default -> { /* 다른 컴포넌트 타입은 무시 */ }
            }
        }

        html.append("    </td></tr>\n  </table>\n</body>\n</html>");
        return html.toString();
    }

    private String mergeTemplateAndCustomContent(String templateHtml, String customContent) {
        String formatted;
        if (!customContent.contains("<")) {
            String paragraphs = Arrays.stream(customContent.split("\n\n"))
                    .filter(p -> !p.isBlank())
                    .map(p -> "<p style=\"margin:16px 0;\">" + p.replace("\n", "<br>") + "</p>")
                    .collect(Collectors.joining());
            formatted = "<div style=\"margin-top:32px;padding-top:24px;border-top:1px solid #e5e7eb;\">" + paragraphs + "</div>";
        } else {
            formatted = "<div style=\"margin-top:32px;padding-top:24px;border-top:1px solid #e5e7eb;\">" + customContent + "</div>";
        }
        if (templateHtml.contains("</body>")) {
            return templateHtml.replace("</body>", formatted + "</body>");
        }
        int lastDiv = templateHtml.lastIndexOf("</div>");
        if (lastDiv >= 0) {
            return templateHtml.substring(0, lastDiv) + formatted + templateHtml.substring(lastDiv);
        }
        return templateHtml + formatted;
    }

    private String replaceVariables(String content, Map<String, String> variables) {
        if (content == null || variables == null || variables.isEmpty()) return content;
        for (Map.Entry<String, String> e : variables.entrySet()) {
            content = content.replace("{" + e.getKey() + "}", e.getValue() != null ? e.getValue() : "");
        }
        return content;
    }

    // ── JSON 파싱 헬퍼 ────────────────────────────────────────────────────────

    private List<String> parseJsonList(String json) {
        if (json == null || json.isBlank()) return new ArrayList<>();
        try {
            return objectMapper.readValue(json, new TypeReference<List<String>>() {});
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    private Map<String, String> parseJsonMap(String json) {
        if (json == null || json.isBlank()) return Collections.emptyMap();
        try {
            return objectMapper.readValue(json, new TypeReference<Map<String, String>>() {});
        } catch (Exception e) {
            return Collections.emptyMap();
        }
    }

    private List<Object> parseJsonObjectList(String json) {
        if (json == null || json.isBlank()) return Collections.emptyList();
        try {
            return objectMapper.readValue(json, new TypeReference<List<Object>>() {});
        } catch (Exception e) {
            return Collections.emptyList();
        }
    }

    private String str(Object val) {
        return val != null ? val.toString() : null;
    }
}
