package gov.va.vinci.dart.mail;

import gov.va.vinci.dart.AppPropertiesProcessor;
import gov.va.vinci.dart.service.DartObjectFactory;
import gov.va.vinci.dart.usr.UserManager;
import gov.va.vinci.security.userdetails.UserDetails;

import java.io.IOException;
import java.util.HashMap;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.naming.NameNotFoundException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

@Component
public class MailManagerImpl implements MailManager {

    // These values are set in the application context. We need to move the values into the app.properties files
    // and use @Value("#{<key>}")
	private String smtpHost;
	private String fromAddr;

	private boolean capture = false;
	
	private String copyAllAddr;

	private static Log log = LogFactory.getLog(MailManager.class);
	
	private HashMap<String,String> EMAIL_CACHE = new HashMap<String,String>();
	
	private boolean mockEmail = false;
	
	public MailManagerImpl() {
		// look for config overrides
		try {
			javax.naming.Context ctx = new javax.naming.InitialContext();
			
			try {
	
				Boolean mock = (Boolean) ctx.lookup( "java:comp/env/VINCIDARTMockEmail" );
				this.mockEmail = mock;
				
				log.debug("got mail manager mock setting from JNDI environment variable : " + mock);
			} 
			catch (NameNotFoundException e) {
				log.info("JNDI name not found : java:comp/env/VINCIDARTMockEmail");
			}
		}
		catch (Exception e) {
			log.error("Error loading mail manager configuration.", e);
		}
	}
	
	public void setSmtpHost(final String hostname) {
		smtpHost = hostname;
	}

	public void setFromAddr(final String addr) {
		fromAddr = addr;
	}

	public void sendPlainMail(final String userLoginId, final String subject, final String text) {
		new Thread(){
			public void run() {
				try {
					final String emailPrefix = AppPropertiesProcessor.getVINCIDARTEmailPrefix() + ": ";

					//log.debug("sending email to : " + userLoginId + "\n\tsubject = " + (emailPrefix + subject) + "\n\tbody = " + (emailPrefix + text));

					sendPlainMail1(userLoginId, (emailPrefix + subject), (emailPrefix + text));
				} catch (Exception e) {
					log.error("Error sending mail", e);
				}
			}
		}.start();
	}

	public void sendPlainMailToEmailDest(final String recipient, final String subject, final String text) {
		new Thread(){
			public void run() {
				try {
					final String emailPrefix = AppPropertiesProcessor.getVINCIDARTEmailPrefix() + ": ";

					//log.debug("sending email to : " + recipient + "\n\tsubject = " + (emailPrefix + subject) + "\n\tbody = " + (emailPrefix + text));					

					sendPlainMail0(recipient, (emailPrefix + subject), (emailPrefix + text));
				} catch (Exception e) {
					log.error("Error sending mail", e);
				}
			}
		}.start();
	}
	
	public void sendPlainMail1(final String userLoginId, final String subject, final String text) throws Exception {
		// get the user email address out of Active Directory
		
		String emailDest = EMAIL_CACHE.get(userLoginId);
		
		if (emailDest == null) {
			
			try {
				UserManager userManager = DartObjectFactory.getInstance().getUserManager();
//				userManager.setUserPreferences(new UserPreferencesImpl());
			
				UserDetails userDetails = userManager.getUserDetails(userLoginId);
				
				emailDest = userDetails.getEmailAddress();
				EMAIL_CACHE.put(userLoginId, emailDest);
			} catch (Exception e) {
				log.error("Error sending email", e);
				return;
			}
		}
		
		sendPlainMail0(emailDest, subject, text);
	}
	
	private void sendPlainMail0(final String recipient, final String subject, final String text) {
	
		System.out.println("Sending Message to " + recipient);
		
		if (mockEmail) {
			return;
		}
		
		// Get system properties
		Properties properties = System.getProperties(); 
	
		// Setup mail server
		properties.setProperty("mail.smtp.host", smtpHost); 
		properties.put("mail.smtp.starttls.enable", "true");
		
		// Get the default Session object.
		Session session = Session.getDefaultInstance(properties); 
	
		try {
			// Create a default MimeMessage object.
			MimeMessage message = new MimeMessage(session); 
			
			// Set the RFC 822 "From" header field using the
			// value of the InternetAddress.getLocalAddress method.
			message.setFrom(new InternetAddress(fromAddr));
		
			// Add the given addresses to the specified recipient type.
			message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient));
		
			// Set the "Subject" header field.
			String subjectWithoutNewlines = subject.replaceAll("(?:\\n|\\r)", " ");
			message.setSubject(subjectWithoutNewlines); 
		
			// Sets the given String as this part's content,
			// with a MIME type of "text/plain".
			message.setText(text); 
		
			// Send message
			Transport.send(message);
			
		} catch (MessagingException e) {
			throw new RuntimeException(e);
		}
	}

	
	public void sendHTMLMail(final String userLoginId, final String subject, final String text, final boolean bIncludePrefix) {
		new Thread(){
			public void run() {
				try {
					final String emailPrefix = AppPropertiesProcessor.getVINCIDARTEmailPrefix() + ": ";

					String subjectToSend = subject;
					String textToSend = text;
					if( bIncludePrefix ) {
						subjectToSend = (emailPrefix + subject);
						textToSend = (emailPrefix + text);
					}

					//log.debug("sending email to : " + userLoginId + "\n\tsubject = " + subjectToSend + "\n\tbody = " + textToSend);
					
					//sendHTMLMail1(userLoginId, (emailPrefix + subject), (emailPrefix + text));
					sendHTMLMail1(userLoginId, subjectToSend, textToSend);
				} catch (Exception e) {
					log.error("Error sending mail", e);
				}
			}
		}.start();
	}

	public void sendHTMLMailToEmailDest(final String recipient, final String subject, final String text, final boolean bIncludePrefix) {
		new Thread(){
			public void run() {
				try {
					final String emailPrefix = AppPropertiesProcessor.getVINCIDARTEmailPrefix() + ": ";

					String subjectToSend = subject;
					String textToSend = text;
					if( bIncludePrefix ) {
						subjectToSend = (emailPrefix + subject);
						textToSend = (emailPrefix + text);
					}

					//log.debug("sending email to : " + userLoginId + "\n\tsubject = " + subjectToSend + "\n\tbody = " + textToSend);
					
					//sendHTMLMail0(recipient, (emailPrefix + subject), (emailPrefix + text));
					sendHTMLMail0(recipient, subjectToSend, textToSend);
				} catch (Exception e) {
					log.error("Error sending mail", e);
				}
			}
		}.start();
	}
	
	public void sendHTMLMail1(final String userLoginId, final String subject, final String text) throws Exception {
		// get the user email address out of Active Directory
		
		String emailDest = EMAIL_CACHE.get(userLoginId);
		
		if (emailDest == null) {
			
			try {
				UserManager userManager = DartObjectFactory.getInstance().getUserManager();
//				userManager.setUserPreferences(new UserPreferencesImpl());
			
				UserDetails userDetails = userManager.getUserDetails(userLoginId);
				
				emailDest = userDetails.getEmailAddress();
				EMAIL_CACHE.put(userLoginId, emailDest);
			} catch (Exception e) {
				log.error("Error sending email", e);
				return;
			}
		}
		
		sendHTMLMail0(emailDest, subject, text);
	}
	
	private void sendHTMLMail0(final String recipient, final String subject, final String text) {
		
		System.out.println("Sending Message to " + recipient);
		
		if (mockEmail) {
			return;
		}
		
		// Get system properties
		Properties properties = System.getProperties(); 
	
		// Setup mail server
		properties.setProperty("mail.smtp.host", smtpHost); 
		properties.put("mail.smtp.starttls.enable", "true");

		// Get the default Session object.
		Session session = Session.getDefaultInstance(properties); 
	
		try {
			// Create a default MimeMessage object.
			MimeMessage message = new MimeMessage(session); 
			
			// Set the RFC 822 "From" header field using the
			// value of the InternetAddress.getLocalAddress method.
			message.setFrom(new InternetAddress(fromAddr));
		
			// Add the given addresses to the specified recipient type.
			message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient));
		
			// Set the "Subject" header field.
			message.setSubject(subject); 
		
			
			// Sets the given String as this part's content,
			// with a MIME type of "text/html".
			message.setText(text, "UTF-8", "html");

		
			// Send message
			Transport.send(message);
			
		} catch (MessagingException e) {
			throw new RuntimeException(e);
		}
	}

	
	public void sendPlainAndHTMLMail(final String userLoginId, final String subject, final String plainBody, final String htmlBody, final boolean bIncludePrefix) {
		new Thread(){
			public void run() {
				try {
					final String emailPrefix = AppPropertiesProcessor.getVINCIDARTEmailPrefix() + ": ";

					String subjectToSend = subject;
					String plainBodyToSend = plainBody;
					String htmlBodyToSend = htmlBody;
					if( bIncludePrefix ) {
						subjectToSend = (emailPrefix + subject);
						plainBodyToSend = (emailPrefix + plainBody);
						htmlBodyToSend = (emailPrefix + htmlBody);
					}

					//log.debug("sending email to : " + userLoginId + "\n\tsubject = " + subjectToSend + "\n\tbody = " + htmlBodyToSend);					
					
					sendPlainAndHTMLMail1(userLoginId, subjectToSend, plainBodyToSend, htmlBodyToSend);
				} catch (Exception e) {
					log.error("Error sending mail", e);
				}
			}
		}.start();
	}

	public void sendPlainAndHTMLMailToEmailDest(final String recipient, final String subject, final String plainBody, final String htmlBody, final boolean bIncludePrefix) {
		new Thread(){
			public void run() {
				try {
					final String emailPrefix = AppPropertiesProcessor.getVINCIDARTEmailPrefix() + ": ";

					String subjectToSend = subject;
					String plainBodyToSend = plainBody;
					String htmlBodyToSend = htmlBody;
					if( bIncludePrefix ) {
						subjectToSend = (emailPrefix + subject);
						plainBodyToSend = (emailPrefix + plainBody);
						htmlBodyToSend = (emailPrefix + htmlBody);
					}

					//log.debug("sending email to : " + userLoginId + "\n\tsubject = " + subjectToSend + "\n\tbody = " + htmlBodyToSend);					
					
					sendPlainAndHTMLMail0(recipient, subjectToSend, plainBodyToSend, htmlBodyToSend);
				} catch (Exception e) {
					log.error("Error sending mail", e);
				}
			}
		}.start();
	}
	
	public void sendPlainAndHTMLMail1(final String userLoginId, final String subject, final String plainBody, final String htmlBody) throws Exception {
		// get the user email address out of Active Directory
		
		String emailDest = EMAIL_CACHE.get(userLoginId);
		
		if (emailDest == null) {
			
			try {
				UserManager userManager = DartObjectFactory.getInstance().getUserManager();
//				userManager.setUserPreferences(new UserPreferencesImpl());
			
				UserDetails userDetails = userManager.getUserDetails(userLoginId);
				
				emailDest = userDetails.getEmailAddress();
				EMAIL_CACHE.put(userLoginId, emailDest);
			} catch (Exception e) {
				log.error("Error sending email", e);
				return;
			}
		}
		
		sendPlainAndHTMLMail0(emailDest, subject, plainBody, htmlBody);
	}
	
	private void sendPlainAndHTMLMail0(final String recipient, final String subject, final String plainBody, final String htmlBody) {
		
		System.out.println("Sending Message to " + recipient);
		
		if (mockEmail) {
			return;
		}
		
		// Get system properties
		Properties properties = System.getProperties(); 
	
		// Setup mail server
		properties.setProperty("mail.smtp.host", smtpHost); 
		properties.put("mail.smtp.starttls.enable", "true");

		// Get the default Session object.
		Session session = Session.getDefaultInstance(properties); 
	
		try {
			// Create a default MimeMessage object.
			MimeMessage message = new MimeMessage(session); 
			
			// Set the RFC 822 "From" header field using the
			// value of the InternetAddress.getLocalAddress method.
			message.setFrom(new InternetAddress(fromAddr));
		
			// Add the given addresses to the specified recipient type.
			message.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient));
		
			// Set the "Subject" header field.
			message.setSubject(subject); 
		
	
			//
			// Set the content for each type

	        // plain text version
	        final MimeBodyPart textPart = new MimeBodyPart();
	        textPart.setContent(plainBody, "text/plain"); 

	        // HTML version
	        final MimeBodyPart htmlPart = new MimeBodyPart();
	        htmlPart.setContent(htmlBody, "text/html");

	        // Create the multipart.  Add HTML and plain text BodyParts to it.
	        final Multipart multipart = new MimeMultipart("alternative");
	        multipart.addBodyPart(textPart);
	        multipart.addBodyPart(htmlPart);

	        // Set Multipart as the message's content
	        message.setContent(multipart);

		
			// Send message
			Transport.send(message);
			
		} catch (MessagingException e) {
			throw new RuntimeException(e);
		}
	}
	
	

	public void sendPlainAndHTMLMailWithAttachment(final String userLoginId, final String subject, final String plainBody, final String htmlBody, final boolean bIncludePrefix, final String attachmentFilename) {
		new Thread(){
			public void run() {
				try {
					final String emailPrefix = AppPropertiesProcessor.getVINCIDARTEmailPrefix() + ": ";

					String subjectToSend = subject;
					String plainBodyToSend = plainBody;
					String htmlBodyToSend = htmlBody;
					if( bIncludePrefix ) {
						subjectToSend = (emailPrefix + subject);
						plainBodyToSend = (emailPrefix + plainBody);
						htmlBodyToSend = (emailPrefix + htmlBody);
					}

					//log.debug("sending email to : " + userLoginId + "\n\tsubject = " + subjectToSend + "\n\tbody = " + htmlBodyToSend);					
					
					sendPlainAndHTMLMailWithAttachment1(userLoginId, subjectToSend, plainBodyToSend, htmlBodyToSend, attachmentFilename);
				} catch (Exception e) {
					log.error("Error sending mail", e);
				}
			}
		}.start();
	}

	public void sendPlainAndHTMLMailWithAttachmentToEmailDest(final String recipient, final String subject, final String plainBody, final String htmlBody, final boolean bIncludePrefix, final String attachmentFilename) {
		new Thread(){
			public void run() {
				try {
					final String emailPrefix = AppPropertiesProcessor.getVINCIDARTEmailPrefix() + ": ";

					String subjectToSend = subject;
					String plainBodyToSend = plainBody;
					String htmlBodyToSend = htmlBody;
					if( bIncludePrefix ) {
						subjectToSend = (emailPrefix + subject);
						plainBodyToSend = (emailPrefix + plainBody);
						htmlBodyToSend = (emailPrefix + htmlBody);
					}

					//log.debug("sending email to : " + userLoginId + "\n\tsubject = " + subjectToSend + "\n\tbody = " + htmlBodyToSend);					
					
					sendPlainAndHTMLMailWithAttachment0(recipient, subjectToSend, plainBodyToSend, htmlBodyToSend, attachmentFilename);
				} catch (Exception e) {
					log.error("Error sending mail", e);
				}
			}
		}.start();
	}
	
	public void sendPlainAndHTMLMailWithAttachment1(final String userLoginId, final String subject, final String plainBody, final String htmlBody, final String attachmentFilename) throws Exception {
		// get the user email address out of Active Directory
		
		String emailDest = EMAIL_CACHE.get(userLoginId);
		
		if (emailDest == null) {
			
			try {
				UserManager userManager = DartObjectFactory.getInstance().getUserManager();
//				userManager.setUserPreferences(new UserPreferencesImpl());
			
				UserDetails userDetails = userManager.getUserDetails(userLoginId);
				
				emailDest = userDetails.getEmailAddress();
				EMAIL_CACHE.put(userLoginId, emailDest);
			} catch (Exception e) {
				log.error("Error sending email", e);
				return;
			}
		}
		
		sendPlainAndHTMLMailWithAttachment0(emailDest, subject, plainBody, htmlBody, attachmentFilename);
	}
	
	private void sendPlainAndHTMLMailWithAttachment0(final String recipient, final String subject, final String plainBody, final String htmlBody, final String attachmentFilename) {
		
		System.out.println("Sending Message to " + recipient);
		
		if (mockEmail) {
			return;
		}
		
		// Get system properties
		Properties properties = System.getProperties(); 
	
		// Setup mail server
		properties.setProperty("mail.smtp.host", smtpHost); 
		properties.put("mail.smtp.starttls.enable", "true");

		// Get the default Session object.
		Session session = Session.getDefaultInstance(properties); 
	
		try {
			// Create a default MimeMessage object.
			MimeMessage message = new MimeMessage(session); 
			
			
			MimeMessageHelper helper = new MimeMessageHelper(message, true);	//multi-part message
			
			
			// Set the RFC 822 "From" header field using the
			// value of the InternetAddress.getLocalAddress method.
			helper.setFrom(new InternetAddress(fromAddr));
		
			// Add the given addresses to the specified recipient type.
			helper.setTo(new InternetAddress(recipient));
		
			// Set the "Subject" header field.
			helper.setSubject(subject); 
		
	
			// Set the multi-part message content
	        helper.setText(plainBody, htmlBody);


	        //add the attachment
	        try {
		        Resource attachmentResource = DartObjectFactory.getInstance().getApplicationContext().getResource("/WEB-INF/classes/" + attachmentFilename);
		        if( attachmentResource != null ) {
		        	helper.addAttachment(attachmentFilename, attachmentResource.getFile());
		        }//end if
			} catch (IOException e) {
				throw new RuntimeException(e);
			}

			
			// Send message
			Transport.send(message);
	        
		} catch (MessagingException e) {
			throw new RuntimeException(e);
		}
	}

	
	
	public void sendTLSMail() {
		 
		final String username = Xusername@gmail.com";
		final String password = Xpassword";
 
		Properties props = new Properties();
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.starttls.enable", "true");
		props.put("mail.smtp.host", "smtp.gmail.com");
		props.put("mail.smtp.port", "587");
 
		Session session = Session.getInstance(props,
		  new javax.mail.Authenticator() {
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication(username, password);
			}
		  });
 
		try {
 
			Message message = new MimeMessage(session);
			message.setFrom(new InternetAddress("from-email@gmail.com"));
			message.setRecipients(Message.RecipientType.TO,
				InternetAddress.parse("to-email@gmail.com"));
			message.setSubject("Testing Subject");
			message.setText("Dear Mail Crawler,"
				+ "\n\n No spam to my email, please!");
 
			Transport.send(message);
 
			System.out.println("Done");
 
		} catch (MessagingException e) {
			throw new RuntimeException(e);
		}
	}

}
