package com.mossle.javamail.service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import java.util.Date;
import java.util.Properties;

import javax.annotation.Resource;

import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

import com.mossle.javamail.persistence.domain.JavamailConfig;
import com.mossle.javamail.persistence.domain.JavamailMessage;
import com.mossle.javamail.persistence.manager.JavamailConfigManager;
import com.mossle.javamail.persistence.manager.JavamailMessageManager;
import com.mossle.javamail.support.SmtpAuthenticator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Service;

@Service
public class JavamailService {
    private static Logger logger = LoggerFactory
            .getLogger(JavamailService.class);
    private JavamailConfigManager javamailConfigManager;
    private JavamailMessageManager javamailMessageManager;

    public Properties createSmtpProperties(JavamailConfig javamailConfig) {
        Properties props = new Properties();
        props.setProperty("mail.transport.protocol",
                javamailConfig.getSendType());
        props.setProperty("mail.smtp.host", javamailConfig.getSendHost());
        props.setProperty("mail.smtp.port", javamailConfig.getSendPort());
        props.setProperty("mail.smtp.auth", "true");

        if ("ssl".equals(javamailConfig.getSendSecure())) {
            props.setProperty("mail.smtp.ssl.enable", "true");
            props.setProperty("mail.smtp.ssl.trust",
                    javamailConfig.getSendHost());
        } else if ("ssl-all".equals(javamailConfig.getSendSecure())) {
            props.setProperty("mail.smtp.ssl.enable", "true");
            props.setProperty("mail.smtp.ssl.trust", "*");
        } else {
            logger.info("unsuppport : {}", javamailConfig.getSendSecure());
        }

        return props;
    }

    public Properties createPop3Properties(JavamailConfig javamailConfig) {
        Properties props = new Properties();
        props.setProperty("mail.store.protocol",
                javamailConfig.getReceiveType());
        props.setProperty("mail.pop3.host", javamailConfig.getReceiveHost());
        props.setProperty("mail.pop3.port", javamailConfig.getReceivePort());

        if ("ssl".equals(javamailConfig.getReceiveSecure())) {
            props.setProperty("mail.pop3.ssl.enable", "true");
            props.setProperty("mail.pop3.ssl.trust",
                    javamailConfig.getReceiveHost());
        } else if ("ssl-all".equals(javamailConfig.getReceiveSecure())) {
            props.setProperty("mail.pop3.ssl.enable", "true");
            props.setProperty("mail.pop3.ssl.trust", "*");
        } else {
            logger.info("unsuppport : {}", javamailConfig.getReceiveSecure());
        }

        return props;
    }

    public void send(String from, String to, String cc, String bcc,
            String subject, String content) throws MessagingException {
        JavamailConfig javamailConfig = javamailConfigManager.findUniqueBy(
                "userId", from);
        this.send(to, cc, bcc, subject, content, javamailConfig);
    }

    public void send(String to, String subject, String content,
            JavamailConfig javamailConfig) throws MessagingException {
        this.send(to, null, null, subject, content, javamailConfig);
    }

    public void send(String to, String cc, String bcc, String subject,
            String content, JavamailConfig javamailConfig)
            throws MessagingException {
        logger.debug("send : {}, {}", to, subject);

        try {
            Properties props = createSmtpProperties(javamailConfig);
            String username = javamailConfig.getUsername();
            String password = javamailConfig.getPassword();

            // 创建Session实例对象
            Session session = Session.getInstance(props, new SmtpAuthenticator(
                    username, password));
            session.setDebug(false);

            // 创建MimeMessage实例对象
            MimeMessage message = new MimeMessage(session);
            // 设置邮件主题
            message.setSubject(subject);
            // 设置发送人
            message.setFrom(new InternetAddress(username));
            // 设置发送时间
            message.setSentDate(new Date());
            // 设置收件人
            message.setRecipients(RecipientType.TO, InternetAddress.parse(to));
            // 设置html内容为邮件正文，指定MIME类型为text/html类型，并指定字符编码为gbk
            message.setContent(content, "text/html;charset=gbk");

            // 保存并生成最终的邮件内容
            message.saveChanges();

            // 发送邮件
            Transport.send(message);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    public void receive(String userId) throws MessagingException, IOException {
        JavamailConfig javamailConfig = javamailConfigManager.findUniqueBy(
                "userId", userId);
        this.receive(javamailConfig);
    }

    public void receive(JavamailConfig javamailConfig)
            throws MessagingException, IOException {
        this.receivePop3(javamailConfig);
    }

    public void receivePop3(JavamailConfig javamailConfig)
            throws MessagingException, IOException {
        // 准备连接服务器的会话信息
        Properties props = createPop3Properties(javamailConfig);

        // 创建Session实例对象
        Session session = Session.getInstance(props);
        session.setDebug(false);

        Store store = session.getStore(javamailConfig.getReceiveType());
        store.connect(javamailConfig.getUsername(),
                javamailConfig.getPassword());

        Folder defaultFolder = store.getDefaultFolder();
        logger.info("default folder : {}", defaultFolder);

        this.receiveByFolder(defaultFolder, javamailConfig);

        logger.info("personal folder");

        for (Folder folder : store.getPersonalNamespaces()) {
            logger.info("personal folder : {}", folder);

            this.receiveByFolder(folder, javamailConfig);
        }

        logger.info("shared folder");

        for (Folder folder : store.getSharedNamespaces()) {
            logger.info("shared folder : {}", folder);

            this.receiveByFolder(folder, javamailConfig);
        }

        logger.info("user folder : {}", javamailConfig.getUsername());

        for (Folder folder : store.getUserNamespaces(javamailConfig
                .getUsername())) {
            logger.info("user folder : {}", folder);

            this.receiveByFolder(folder, javamailConfig);
        }

        store.close();
    }

    public void receiveByFolder(Folder folder, JavamailConfig javamailConfig)
            throws MessagingException, IOException {
        logger.info("receive : {}", folder);

        if ((Folder.HOLDS_MESSAGES & folder.getType()) != 0) {
            this.receiveMessageByFolder(folder, javamailConfig);
        }

        if ((Folder.HOLDS_FOLDERS & folder.getType()) != 0) {
            for (Folder childFolder : folder.list()) {
                this.receiveByFolder(childFolder, javamailConfig);
            }
        }

        if (folder.isOpen()) {
            // 关闭资源
            folder.close(false);
        }
    }

    public void receiveMessageByFolder(Folder folder,
            JavamailConfig javamailConfig) {
        try {
            /*
             * Folder.READ_ONLY：只读权限 Folder.READ_WRITE：可读可写（可以修改邮件的状态）
             */
            folder.open(Folder.READ_WRITE); // 打开收件箱

            // 获得收件箱的邮件列表
            Message[] messages = folder.getMessages();

            // 打印不同状态的邮件数量
            logger.debug("收件箱中共" + messages.length + "封邮件!");
            logger.debug("收件箱中共" + folder.getUnreadMessageCount() + "封未读邮件!");
            logger.debug("收件箱中共" + folder.getNewMessageCount() + "封新邮件!");
            logger.debug("收件箱中共" + folder.getDeletedMessageCount() + "封已删除邮件!");

            logger.debug("------------------------开始解析邮件----------------------------------");

            // 解析邮件
            for (Message message : messages) {
                // IMAPMessage msg = (IMAPMessage) message;
                MimeMessage mimeMessage = (MimeMessage) message;

                try {
                    if (javamailMessageManager.findUniqueBy("messageId",
                            mimeMessage.getMessageID()) != null) {
                        continue;
                    }
                } catch (Exception ex) {
                    logger.error(ex.getMessage(), ex);

                    continue;
                }

                String subject = this.getSubject(mimeMessage);
                logger.debug("[" + subject + "]未读，是否需要阅读此邮件（yes/no）？");

                // BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                // String answer = reader.readLine();
                // String answer = "no";
                // if ("yes".equalsIgnoreCase(answer)) {
                // POP3ReceiveMailTest.parseMessage(msg); // 解析邮件
                // 第二个参数如果设置为true，则将修改反馈给服务器。false则不反馈给服务器
                // msg.setFlag(Flag.SEEN, true); //设置已读标志
                String from = this.getFrom(mimeMessage);
                logger.debug("from : " + from);

                JavamailMessage javamailMessage = new JavamailMessage();

                if (subject.length() > 255) {
                    logger.info("{} length {} larger than 255", subject,
                            subject.length());
                    subject = subject.substring(0, 255);
                }

                javamailMessage.setSubject(subject);
                javamailMessage.setSender(from);
                javamailMessage.setSendTime(mimeMessage.getSentDate());
                javamailMessage.setReceiveTime(mimeMessage.getReceivedDate());
                javamailMessage
                        .setMessageNumber(mimeMessage.getMessageNumber());
                javamailMessage.setMessageId(mimeMessage.getMessageID());
                javamailMessage.setFolder("INBOX");
                logger.debug("before content");

                StringBuffer content = new StringBuffer(30);
                getMailTextContent(message, content);
                logger.debug("content : " + content);
                javamailMessage.setContent(content.toString());
                javamailMessage.setJavamailConfig(javamailConfig);
                javamailMessageManager.save(javamailMessage);
                logger.debug("end");

                // }
            }
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    public static String getSubject(MimeMessage msg)
            throws UnsupportedEncodingException, MessagingException {
        return MimeUtility.decodeText(msg.getSubject());
    }

    public static String getFrom(MimeMessage msg) throws MessagingException,
            UnsupportedEncodingException {
        String from = "";
        Address[] froms = msg.getFrom();

        if (froms.length < 1) {
            throw new MessagingException("没有发件人!");
        }

        InternetAddress address = (InternetAddress) froms[0];
        String person = address.getPersonal();

        if (person != null) {
            person = MimeUtility.decodeText(person) + " ";
        } else {
            person = "";
        }

        from = person + "<" + address.getAddress() + ">";

        return from;
    }

    public void getMailTextContent(Part part, StringBuffer content)
            throws MessagingException, IOException {
        // 如果是文本类型的附件，通过getContent方法可以取到文本内容，但这不是我们需要的结果，所以在这里要做判断
        boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;

        if (part.isMimeType("text/*") && !isContainTextAttach) {
            content.append(part.getContent().toString());
        } else if (part.isMimeType("message/rfc822")) {
            getMailTextContent((Part) part.getContent(), content);
        } else if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();
            int partCount = multipart.getCount();

            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                getMailTextContent(bodyPart, content);
            }
        }
    }

    @Resource
    public void setJavamailMessageManager(
            JavamailMessageManager javamailMessageManager) {
        this.javamailMessageManager = javamailMessageManager;
    }

    @Resource
    public void setJavamailConfigManager(
            JavamailConfigManager javamailConfigManager) {
        this.javamailConfigManager = javamailConfigManager;
    }
}
