package ru.yandex.wmconsole.servantlet.feedback;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.internet.MimeUtility;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.validator.routines.EmailValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.InputStreamSource;

import ru.yandex.common.framework.core.RemoteFile;
import ru.yandex.common.framework.core.ServRequest;
import ru.yandex.common.framework.core.ServResponse;
import ru.yandex.common.util.collections.CollectionFactory;
import ru.yandex.common.util.collections.Pair;
import ru.yandex.wmconsole.data.info.BriefHostInfo;
import ru.yandex.wmconsole.servantlet.WMCAuthenticationServantlet;
import ru.yandex.wmconsole.service.error.WMCUserProblem;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.InternalProblem;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.service.MailService;

/**
 * User: baton
 * Date: 28.06.2007
 * Time: 20:45:45
 */
public class FeedbackSendServantlet extends WMCAuthenticationServantlet {
    private static final Logger log = LoggerFactory.getLogger(FeedbackSendServantlet.class);

    private static final String PARAM_CONTENT = "content";
    private static final String PARAM_NAME = "name";
    private static final String PARAM_EMAIL = "email";
    private static final String PARAM_IP = "ip";
    private static final String PARAM_SUBJECT = "subject";
    private static final String PARAM_URL = "url";
    private static final String PARAM_FULLURL = "fullurl";

    // Примеры url
    private static final String PARAM_EXAMPLE_PAGE_PREFIX = "example_page";
    private static final int EXAMPLE_PAGES_COUNT = 3;
    private static final String HEADER_EXAMPLE_URL_PREFIX = "X-OTRS-urlsample";

    // Примеры запросов
    private static final String PARAM_EXAMPLE_QUERY_PREFIX = "example_query";
    private static final String HEADER_EXAMPLE_QUERY_PREFIX = "X-OTRS-enquirysample";
    private static final int EXAMPLE_QUERIES_COUNT = 3;

    private static final String PARAM_UF = "uf"; // разрешать для неподтвержденных сайтов (see WMCON-3798)

    private static final int MAX_ATTACHMENT_LENGTH = 1024 * 1024;

    private String feedbackAddress;

    private MailService mailService;

    @Override
    protected void doProcess(ServRequest req, ServResponse res, long userId) throws UserException, InternalException {
        assertUserExists(userId);
        String content = getRequiredStringParam(req, PARAM_CONTENT);
        String fromName = getRequiredStringParam(req, PARAM_NAME);
        String fromEmail = getRequiredStringParam(req, PARAM_EMAIL);

        String ip = getRequiredStringParam(req, PARAM_IP);
        if(!EmailValidator.getInstance().isValid(fromEmail)) {
            throw new UserException(WMCUserProblem.EMAIL_NOT_VALID, "email is not valid", PARAM_EMAIL, fromEmail);
        }

        String subject = getRequiredStringParam(req, PARAM_SUBJECT);
        subject = subject.replaceAll("\\[Ticket#\\d+\\]", "");
        log.debug("subject=" + subject);
        String url = getStringParam(req, PARAM_URL, "no url");
        if (url.length() > 255) {
            url = url.substring(0, 255);
        }
        String fullUrl = getStringParam(req, PARAM_FULLURL, "no url");
        fullUrl = StringUtils.substring(fullUrl, 0, 255);

        log.debug("url=" + url);
        log.debug("fullurl=" + fullUrl);
        log.debug("feedbackAddress=" + feedbackAddress);
        Boolean allowNotVerifiedHost = getBooleanParam(req, PARAM_UF);
        if (allowNotVerifiedHost == null) {
            allowNotVerifiedHost = false;
        }
        log.debug("uf=" + allowNotVerifiedHost);

        // Получаем примеры страниц из параметров
        final List<String> examplePages = getExampleParams(req, PARAM_EXAMPLE_PAGE_PREFIX, EXAMPLE_PAGES_COUNT);
        // Если есть примеры, то дописываем в начало письма
        if (examplePages.size() > 0) {
            content = insertExamples(content, examplePages);
        }

        // Получаем примеры запросов из параметров
        final List<String> exampleQueries = getExampleParams(req, PARAM_EXAMPLE_QUERY_PREFIX, EXAMPLE_QUERIES_COUNT);
        // Если есть примеры, то дописываем в начало письма
        if (exampleQueries.size() > 0) {
            content = insertExamples(content, exampleQueries);
        }

        BriefHostInfo hostInfo = getHostInfo(req, userId, true, !allowNotVerifiedHost);
        content = "[ " + hostInfo.getName() + " ]\r\n" + content;
        String fromLogin = getUserInfoService().getUserInfo(userId).getLogin();

        Pair<Boolean, Boolean> options = getUserInfoService().getUserOptions(userId);
        boolean needConfirmation = ((options != null) && options.second);

        try {
            MailService.AttachmentData[] attachments = null;
            @SuppressWarnings({"unchecked"})
            List<RemoteFile> files = req.getRemoteFiles();

            log.debug("Send to: " + feedbackAddress);
            log.debug("Content: " + content + "\n" +
                    "subject: " + subject + "\n" +
                    "name: " + fromName + "\n" +
                    "email: " + fromEmail + "\n" +
                    "ip: " + ip);

            if (files != null && !files.isEmpty() && files.get(0).getSize() > 0) {
                log.debug("I have an attachment! (" + files.size() + ")");
                final RemoteFile file = files.get(0);
                log.debug("Attach name: \"" + file.getOriginalFilename() + "\", size: " + file.getSize());
                final InputStreamSource fileISS = getBufferedFileContent(file, MAX_ATTACHMENT_LENGTH);
                attachments = new MailService.AttachmentData[]{new MailService.AttachmentData() {
                    @Override
                    public String getName() {
                        return file.getOriginalFilename();
                    }

                    @Override
                    public InputStreamSource getData() {
                        return fileISS;
                    }
                }};
            }

            final ArrayList<Header> headers = CollectionFactory.newArrayList(CollectionFactory.array(
                    new Header("X-OTRS-TicketKey1", "site"),
                    new Header("X-OTRS-TicketValue1", hostInfo.getName()),
                    new Header("X-OTRS-TicketKey2", "login"),
                    new Header("X-OTRS-TicketValue2", fromLogin),
                    new Header("X-OTRS-TicketKey3", "url"),
                    new Header("X-OTRS-TicketValue3", url),
                    new Header("X-Address", ip),
                    new Header("X-Otrs-Ticketkey13", "NeedConfirmation"),
                    new Header("X-Otrs-Ticketvalue13", needConfirmation ? "yes" : "no"),
                    new Header("X-Referer", fullUrl)
            ));

            // Добавляем заголовки для примеров url
            appendExampleHeader(headers, HEADER_EXAMPLE_URL_PREFIX, examplePages);
            // Добавляем заголовки для примеров запросов
            appendExampleHeader(headers, HEADER_EXAMPLE_QUERY_PREFIX, exampleQueries);

            Header hs[] = new Header[headers.size()];
            headers.toArray(hs);

            mailService.sendWithAttachments(
                    subject,
                    content,
                    new String[]{feedbackAddress},
                    null,
                    fromName,
                    fromEmail,
                    attachments,
                    hs
            );
        } catch (MessagingException e) {
            throw new InternalException(InternalProblem.MESSAGING_ERROR, "Messaging exception.", e);
        } catch (UnsupportedEncodingException e) {
            throw new InternalException(InternalProblem.MESSAGING_ERROR, "Unsupported encoding.", e);
        }
    }

    /**
     * Получаем примеры страниц или запросов из параметров
     *
     * @param req объект запроса
     * @return  список
     */
    private List<String> getExampleParams(ServRequest req, String paramPrefix, int paramCount) {
        List<String> result = new LinkedList<String>();
        for (int i = 1; i <= paramCount; i++) {
            final String url = getStringParam(req, paramPrefix + i);
            if (url != null && !url.trim().isEmpty()) {
                result.add(url);
            }
        }
        return result;
    }

    private String insertExamples(String content, List<String> examples) {
        StringBuilder sb = new StringBuilder();
        for (String examplePage : examples) {
            sb.append(examplePage);
            sb.append("\n");
        }
        sb.append("\n").append(content);
        return sb.toString();
    }

    private void appendExampleHeader(ArrayList<Header> headers, String headerPrefix, List<String> examples) throws UnsupportedEncodingException {
        int i = 1;
        for (String example : examples) {
            headers.add(new Header(headerPrefix + i, MimeUtility.encodeText(example, "UTF-8", null)));
            i++;
        }
    }

    @Required
    public void setMailService(MailService mailService) {
        this.mailService = mailService;
    }

    @Required
    public void setFeedbackAddress(String feedbackAddress) {
        this.feedbackAddress = feedbackAddress;
    }
}
