/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.mail;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.MimeMessage;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.mail.resolver.DataSourceUrlResolver;
import org.apache.commons.mail.settings.EmailConfiguration;
import org.junit.Before;
import org.junit.Test;

/**
 * This are regression test sending REAL email to REAL mail
 * servers using REAL recipients.
 *
 * The intention is to field-test certain aspects
 * of email using a variety of mail clients since I'm not a mockist
 * (see http://martinfowler.com/articles/mocksArentStubs.html#ClassicalAndMockistTesting).
 */
public class EmailLiveTest extends AbstractEmailTest
{
    @Before
    public void setUpLiveTest()
    {
        // enforce a default charset UTF-8 otherwise non-ASCII attachment names will not work
        System.setProperty("mail.mime.charset", "utf-8");

        // enforce encoding of non-ASCII characters (violating the MIME specification - see
        // http://java.sun.com/products/javamail/javadocs/javax/mail/internet/package-summary.html
        System.setProperty("mail.mime.encodefilename", "true");
    }

    protected Email send(final Email email) throws EmailException {

        if( EmailConfiguration.MAIL_FORCE_SEND ) {
            email.send();
        }
        else {
            email.buildMimeMessage();
        }

        return email;
    }

    protected String getFromUrl(final URL url) throws Exception {

        final URLDataSource dataSource = new URLDataSource(url);
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        IOUtils.copy(dataSource.getInputStream(), baos);
        return new String(baos.toByteArray(), "UTF-8");
    }

    /**
     * Factory method to create a pre-configured email instance.
     *
     * @param clazz the requested implementation class
     * @return the new instance
     * @throws Exception creating the Email instance failed
     */
    private Email create(final Class<? extends Email> clazz) throws Exception {

        final Email email = clazz.newInstance();

        email.setStartTLSEnabled(EmailConfiguration.MAIL_USE_STARTTLS);
        email.setStartTLSRequired(EmailConfiguration.MAIL_STARTTLS_REQUIRED);
        email.setSSLOnConnect(EmailConfiguration.MAIL_USE_SSL);
        email.setSSLCheckServerIdentity(EmailConfiguration.MAIL_SSL_CHECKSERVERIDENTITY);
        email.setHostName(EmailConfiguration.MAIL_SERVER);
        email.setSmtpPort(EmailConfiguration.MAIL_SERVER_PORT);
        email.setBounceAddress(EmailConfiguration.TEST_FROM);
        email.setDebug(EmailConfiguration.MAIL_DEBUG);
        email.setCharset(EmailConfiguration.MAIL_CHARSET);
        email.setFrom(EmailConfiguration.TEST_FROM);
        email.addTo(EmailConfiguration.TEST_TO);

        if(EmailConfiguration.TEST_USER != null) {
            email.setAuthenticator(new DefaultAuthenticator(EmailConfiguration.TEST_USER, EmailConfiguration.TEST_PASSWD));
        }

        return email;
    }

    // ======================================================================
    // Start of Tests
    // ======================================================================

    /**
     * A sanity check that a simple email also works in reality.
     *
     * @throws Exception the test failed
     */
    @Test
    public void testSimpleEmail() throws Exception
    {
        final SimpleEmail email = (SimpleEmail) create(SimpleEmail.class);
        email.setSubject("TestSimpleMail");
        email.setMsg("This is a test mail ... :-)");

        EmailUtils.writeMimeMessage( new File("./target/test-emails/simplemail.eml"), send(email).getMimeMessage());
    }

    /**
     * A sanity check that a header folding works correctly.
     *
     * @throws Exception the test failed
     */
    @Test
    public void testFoldedHeaderValue() throws Exception
    {
        final SimpleEmail email = (SimpleEmail) create(SimpleEmail.class);
        email.setSubject("TestFoldedHeaderMail");
        email.setMsg("This is a test mail with a folded header value... :-)");
        email.addHeader("X-TestHeader", "This is a very long header value which should be folded into two lines, hopefully");

        EmailUtils.writeMimeMessage( new File("./target/test-emails/foldedheader.eml"), send(email).getMimeMessage());
    }

    /**
     * A sanity check that a simple email also works in reality.
     *
     * @throws Exception the test failed
     */
    @Test
    public void testMultiPartEmail() throws Exception
    {
        final MultiPartEmail email = (MultiPartEmail) create(MultiPartEmail.class);
        email.setSubject("TestMultiPartMail");
        email.setMsg("This is a test mail ... :-)");
        email.attach(new File("./src/test/resources/attachments/logo.pdf"));

        EmailUtils.writeMimeMessage( new File("./target/test-emails/multipart.eml"), send(email).getMimeMessage());
    }

    /**
     * This test checks the various options of building a HTML email.
     *
     * https://issues.apache.org/jira/browse/EMAIL-65
     *
     * @throws Exception the test failed
     */
    @Test
    public void testHtmlMailMimeLayout() throws Exception
    {
        String textMsg;
        String htmlMsg;

        // prepare attachments

        String cid;

        final URL url = new URL(EmailConfiguration.TEST_URL);
        final File imageFile = new File("./src/test/resources/images/asf_logo_wide.gif");

        final EmailAttachment attachment = new EmailAttachment();
        final File attachmentFile = new File("./src/test/resources/attachments/logo.pdf");
        attachment.setName("logo.pdf");
        attachment.setDescription("The official Apache logo");
        attachment.setPath(attachmentFile.getAbsolutePath());

        // 1) text + html content

        final HtmlEmail htmlEmail1 = (HtmlEmail) create(HtmlEmail.class);
        textMsg = "Your email client does not support HTML messages";
        htmlMsg = "<html><b>This is a HTML message without any image</b><html>";

        htmlEmail1.setSubject( "[email] 1.Test: text + html content");
        htmlEmail1.setTextMsg(textMsg);
        htmlEmail1.setHtmlMsg(htmlMsg);

        EmailUtils.writeMimeMessage( new File("./target/test-emails/htmlemail1.eml"), send(htmlEmail1).getMimeMessage());

        // 2) text + html content + image as attachment

        final HtmlEmail htmlEmail2 = (HtmlEmail) create(HtmlEmail.class);
        textMsg = "Your email client does not support HTML messages";
        htmlMsg = "<html><b>This is a HTML message with an image attachment</b><html>";

        htmlEmail2.setSubject( "[email] 2.Test: text + html content + image as attachment");
        htmlEmail2.setTextMsg(textMsg);
        htmlEmail2.setHtmlMsg(htmlMsg);
        htmlEmail2.attach(url, "Apache Logo", "The official Apache logo" );

        EmailUtils.writeMimeMessage( new File("./target/test-emails/htmlemail2.eml"), send(htmlEmail2).getMimeMessage());

        // 3) text + html content + inline image

        final HtmlEmail htmlEmail3 = (HtmlEmail) create(HtmlEmail.class);
        textMsg = "Your email client does not support HTML messages";
        cid = htmlEmail3.embed(imageFile, "Apache Logo");

        htmlMsg = "<html><b>This is a HTML message with an inline image - <img src=\"cid:"
            + cid + "\"> and NO attachment</b><html>";

        htmlEmail3.setSubject( "[email] 3.Test: text + html content + inline image");
        htmlEmail3.setTextMsg(textMsg);
        htmlEmail3.setHtmlMsg(htmlMsg);

        EmailUtils.writeMimeMessage( new File("./target/test-emails/htmlemail3.eml"), send(htmlEmail3).getMimeMessage());

        // 4) text + html content + inline image + attachment

        final HtmlEmail htmlEmail4 = (HtmlEmail) create(HtmlEmail.class);
        textMsg = "Your email client does not support HTML messages";
        cid = htmlEmail4.embed(imageFile, "Apache Logo");
        htmlMsg = "<html><b>This is a HTML message with an inline image - <img src=\"cid:" + cid + "\"> and attachment</b><html>";

        htmlEmail4.setSubject( "[email] 4.Test: text + html content + inline image + attachment");
        htmlEmail4.setTextMsg(textMsg);
        htmlEmail4.setHtmlMsg(htmlMsg);
        htmlEmail4.attach(attachment);

        EmailUtils.writeMimeMessage( new File("./target/test-emails/htmlemail4.eml"), send(htmlEmail4).getMimeMessage());        
    }

    /**
     * This test checks the correct character encoding when sending
     * non-ASCII content using SimpleEmail.
     *
     * https://issues.apache.org/jira/browse/EMAIL-79
     *
     * @throws Exception the test failed
     */
    @Test
    public void testCorrectCharacterEncoding() throws Exception
    {
        // U+03B1 : GREEK SMALL LETTER ALPHA
        // U+03B2 : GREEK SMALL LETTER BETA
        // U+03B3 : GREEK SMALL LETTER GAMMA

        final String subject = "[email] 5.Test: Subject with three greek UTF-8 characters : \u03B1\u03B2\u03B3";
        final String textMsg = "My test body with with three greek UTF-8 characters : \u03B1\u03B2\u03B3\n";
        final String attachmentName = "\u03B1\u03B2\u03B3.txt";

        // make sure to set the charset before adding the message content
        final MultiPartEmail email = (MultiPartEmail) create(MultiPartEmail.class);
        email.setSubject(subject);
        email.setMsg(textMsg);

        // create a proper UTF-8 sequence for the text attachment (matching our default charset)
        final DataSource attachment = new javax.mail.util.ByteArrayDataSource(textMsg.getBytes("utf-8"), "text/plain");
        email.attach(attachment, attachmentName, "Attachment in Greek");

        EmailUtils.writeMimeMessage( new File("./target/test-emails/correct-encoding.eml"), send(email).getMimeMessage());
    }

    /**
     * Test sending a image HTML mail bases on a local HTML page and local image.
     *
     * @throws Exception the test failed
     */
    @Test
    public void testImageHtmlEmailLocal() throws Exception
    {
        // use a simple HTML page with one image

        final File htmlFile = new File("./src/test/resources/html/www.apache.org.html");
        final String htmlMsg1 = FileUtils.readFileToString(htmlFile, "ISO-8859-1");

        final ImageHtmlEmail email = (ImageHtmlEmail) create(ImageHtmlEmail.class);
        email.setDataSourceResolver(new DataSourceUrlResolver(htmlFile.getParentFile().toURI().toURL(), false));
        email.setSubject("[testImageHtmlEmail] 1.Test: simple html content");
        email.setHtmlMsg(htmlMsg1);

        EmailUtils.writeMimeMessage( new File("./target/test-emails/testImageHtmlEmailLocal.eml"), send(email).getMimeMessage());
    }

    /**
     * Test sending a image HTML mail based on a real world website. We
     * would expect to see the ApacheCon logo at the bottom of the email.
     * Please note that not all major email clients can display the email
     * properly.
     *
     * @throws Exception the test failed
     */
    @Test
    public void testImageHtmlEmailRemote() throws Exception
    {
        if(EmailConfiguration.MAIL_FORCE_SEND)
        {
            final URL url = new URL("http://commons.apache.org/email/");
            // URL url = new URL("http://www.dzone.com/links/index.html");
            final String htmlMsg = getFromUrl(url);

            final ImageHtmlEmail email = (ImageHtmlEmail) create(ImageHtmlEmail.class);
            email.setDataSourceResolver(new DataSourceUrlResolver(url, true));
            email.setSubject("[testImageHtmlEmail] 2.Test: complex html content");
            email.setHtmlMsg(htmlMsg);

            EmailUtils.writeMimeMessage( new File("./target/test-emails/testImageHtmlEmailRemote.eml"), send(email).getMimeMessage());
        }
    }

    /**
     * Testing if we are able to send a few emails in a batch, i.e.
     * using a single authenticated <code>Transport</code> instance.
     * Use a single instance speeds up processing since the
     * authorization is only done once.
     *
     * https://issues.apache.org/jira/browse/EMAIL-72
     *
     * @throws Exception the test failed.
     */
    @Test
    public void testSendingEmailsInBatch() throws Exception
    {
        final List<SimpleEmail> emails = new ArrayList<SimpleEmail>();

        // we need to instantiate an email to provide the mail session - a bit ugly
        final Session session = create(SimpleEmail.class).getMailSession();
        final Transport transport = session.getTransport();

        // simulate creating a bunch of emails using an existing mail session
        for(int i = 0; i<3; i++)
        {
            final SimpleEmail personalizedEmail = (SimpleEmail) create(SimpleEmail.class);
            personalizedEmail.setMailSession(session);
            personalizedEmail.setSubject("Personalized Test Mail Nr. " + i);
            personalizedEmail.setMsg("This is a personalized test mail ... :-)");
            personalizedEmail.buildMimeMessage();
            emails.add(personalizedEmail);
        }

        // send the list of emails using a single 'Transport' instance.
        if( EmailConfiguration.MAIL_FORCE_SEND )
        {
            transport.connect();

            for (final SimpleEmail personalizedEmail : emails)
            {
                final MimeMessage mimeMessage = personalizedEmail.getMimeMessage();
                Transport.send(mimeMessage);
                System.out.println("Successfully sent the following email : " + mimeMessage.getMessageID());
            }

            transport.close();
         }
    }

    /**
     * Testing if we are able to send a partial email with an invalid address.
     *
     * https://issues.apache.org/jira/browse/EMAIL-132
     *
     * @throws Exception the test failed.
     */
    @Test
    public void testPartialSend() throws Exception
    {
        final SimpleEmail email = (SimpleEmail) create(SimpleEmail.class);
        email.addTo(EmailConfiguration.TEST_TO);
        email.addTo("nobody@is.invalid");
        email.setSubject("TestPartialMail");
        email.setMsg("This is a test mail ... :-)");

        email.setSendPartial(true);

        EmailUtils.writeMimeMessage( new File("./target/test-emails/partialmail.eml"), send(email).getMimeMessage());
    }

}
