package study.springcaps;

import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.GreenMailUtil;
import com.icegreen.greenmail.util.ServerSetup;
import jakarta.mail.Folder;
import jakarta.mail.Message;
import jakarta.mail.internet.MimeMessage;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.MailAuthenticationException;
import org.springframework.mail.MailSendException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;

import java.util.Properties;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * U49 邮件：JavaMailSenderImpl 真实连 GreenMail（嵌入式 SMTP+IMAP）。
 * 端口为 ServerSetupTest 的 3025/3110；只监听 loopback，扩展停止即回收。
 */
class MailGreenMailTest {

    /** GreenMail 端口 0 = 自动分配；启动后从运行中的服务读实际端口。手动管理生命周期。 */
    static final GreenMail GREEN_MAIL = new GreenMail(new ServerSetup[]{
            new ServerSetup(0, "127.0.0.1", ServerSetup.PROTOCOL_SMTP),
            new ServerSetup(0, "127.0.0.1", ServerSetup.PROTOCOL_IMAP)});

    private static String smtpHost;
    private static int smtpPort;
    private static String imapHost;
    private static int imapPort;

    private static synchronized void ensureStarted() {
        if (!GREEN_MAIL.isRunning()) {
            GREEN_MAIL.start();
        }
        smtpHost = GREEN_MAIL.getSmtp().getBindTo();
        smtpPort = GREEN_MAIL.getSmtp().getPort();
        imapHost = GREEN_MAIL.getImap().getBindTo();
        imapPort = GREEN_MAIL.getImap().getPort();
    }

    @AfterAll
    static void stopMail() {
        GREEN_MAIL.stop();
    }

    @Configuration
    static class MailCfg {
        @Bean
        JavaMailSender mailSender() {
            JavaMailSenderImpl sender = new JavaMailSenderImpl();
            ensureStarted();
            sender.setHost(smtpHost);
            sender.setPort(smtpPort);
            Properties props = sender.getJavaMailProperties();
            props.put("mail.smtp.connectiontimeout", "2000");
            props.put("mail.smtp.timeout", "2000");
            props.put("mail.smtp.writetimeout", "2000");
            props.put("mail.transport.protocol", "smtp");
            return sender;
        }
    }

    @Test
    void mailSendAndReadBackViaImap() throws Exception {
        ensureStarted();
        GREEN_MAIL.setUser("alice@example.test", "alice-pw");

        try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MailCfg.class)) {
            JavaMailSender sender = ctx.getBean(JavaMailSender.class);

            SimpleMailMessage simple = new SimpleMailMessage();
            simple.setFrom("app@example.test");
            simple.setTo("alice@example.test");
            simple.setSubject("U49 simple");
            simple.setText("纯文本正文");
            sender.send(simple);

            MimeMessage rich = sender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(rich, false, "UTF-8");
            helper.setFrom("app@example.test");
            helper.setTo("alice@example.test");
            helper.setSubject("U49 mime");
            helper.setText("<b>HTML 正文</b>", true);
            sender.send(rich);

            // IMAP 读回：真实协议读取，不只是 GreenMail 内存 API
            jakarta.mail.Session imapSession = jakarta.mail.Session.getInstance(new Properties());
            jakarta.mail.Store store = imapSession.getStore("imap");
            ensureStarted();
            store.connect(imapHost, imapPort, "alice@example.test", "alice-pw");
            Folder inbox = store.getFolder("INBOX");
            inbox.open(Folder.READ_ONLY);
            Message[] messages = inbox.getMessages();
            assertEquals(2, messages.length, "SMTP 收下的两封信应出现在 IMAP INBOX");
            java.util.Set<String> subjects = new java.util.HashSet<>();
            for (Message m : messages) {
                subjects.add(m.getSubject());
            }
            assertTrue(subjects.contains("U49 simple") && subjects.contains("U49 mime"),
                    "两封信主题都应在 INBOX：" + subjects);
            String decoded = null;
            for (Message m : messages) {
                if ("U49 simple".equals(m.getSubject())) {
                    // 非 ASCII 正文按 MIME（base64）传输：DataHandler 内容经 MIME 解码后比对
                    Object content = m.getContent();
                    decoded = String.valueOf(content);
                }
            }
            assertTrue(decoded != null && decoded.contains("纯文本正文"),
                    "simple 信件正文经 MIME 解码后应可读，实际=" + decoded);
            inbox.close(false);
            store.close();
            System.out.println("U49 mailSend=PASS smtpMessages=2 imapReadBack=true");
        }
    }

    @Test
    void mailAuthFailureIsWrapped() {
        ensureStarted();
        GREEN_MAIL.setUser("alice@example.test", "correct-pw");

        JavaMailSenderImpl sender = new JavaMailSenderImpl();
        ensureStarted();
        sender.setHost(smtpHost);
        sender.setPort(smtpPort);
        sender.setUsername("alice@example.test");
        sender.setPassword("wrong-pw");
        Properties props = sender.getJavaMailProperties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.connectiontimeout", "2000");
        props.put("mail.smtp.timeout", "2000");

        SimpleMailMessage msg = new SimpleMailMessage();
        msg.setFrom("app@example.test");
        msg.setTo("alice@example.test");
        msg.setSubject("auth");
        assertThrows(MailAuthenticationException.class, () -> sender.send(msg),
                "认证失败应转成 MailAuthenticationException");
        System.out.println("U49 mailAuthFail=PASS wrapped=MailAuthenticationException");
    }

    @Test
    void mailConnectionRefusedAndPartialFailure() {
        // 连接拒绝：随机不可能端口
        JavaMailSenderImpl sender = new JavaMailSenderImpl();
        sender.setHost("127.0.0.1");
        sender.setPort(1);
        Properties props = sender.getJavaMailProperties();
        props.put("mail.smtp.connectiontimeout", "500");
        props.put("mail.smtp.timeout", "500");
        SimpleMailMessage msg = new SimpleMailMessage();
        msg.setFrom("a@example.test");
        msg.setTo("b@example.test");
        msg.setSubject("refused");
        assertThrows(MailSendException.class, () -> sender.send(msg));
        System.out.println("U49 mailRefused=PASS wrapped=MailSendException");

        // 批量发送语义：连接失败 → 剩余全部记入 getFailedMessages 并抛 MailSendException（doSend 真实分支）
        ensureStarted();
        GREEN_MAIL.setUser("ok@example.test", "pw");
        JavaMailSenderImpl sender2 = new JavaMailSenderImpl();
        sender2.setHost(smtpHost);
        sender2.setPort(smtpPort);
        sender2.setUsername("ok@example.test");
        sender2.setPassword("pw");
        Properties p2 = sender2.getJavaMailProperties();
        p2.put("mail.smtp.auth", "true");
        p2.put("mail.smtp.connectiontimeout", "2000");
        p2.put("mail.smtp.timeout", "2000");

        MimeMessage m1 = sender2.createMimeMessage();
        MimeMessage m2 = sender2.createMimeMessage();
        try {
            MimeMessageHelper h1 = new MimeMessageHelper(m1, false, "UTF-8");
            h1.setFrom("app@example.test");
            h1.setTo("ok@example.test");
            h1.setSubject("m1");
            h1.setText("one");
            MimeMessageHelper h2 = new MimeMessageHelper(m2, false, "UTF-8");
            h2.setFrom("app@example.test");
            h2.setTo("ok@example.test");
            h2.setSubject("m2");
            h2.setText("two");
        } catch (jakarta.mail.MessagingException e) {
            throw new IllegalStateException(e);
        }
        sender2.send(m1);            // 第一封：连接建立并成功
        GREEN_MAIL.stop();           // 服务端停止 → 已建连接断开
        MailSendException partial = assertThrows(MailSendException.class, () -> sender2.send(m2),
                "服务端停止后发送必须失败");
        assertTrue(partial.getFailedMessages().containsKey(m2) || !partial.getFailedMessages().isEmpty(),
                "失败消息映射不应为空：" + partial.getFailedMessages().size());
        System.out.println("U49 mailPartial=PASS failedMessages=" + partial.getFailedMessages().size());
    }
}
