package ru.yandex.webmaster3.viewer.http.turbo.logo;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;

import lombok.RequiredArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.http.WriteAction;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.turbo.model.TurboHostHeaderType;
import ru.yandex.webmaster3.core.util.GzipUtils;
import ru.yandex.webmaster3.storage.avatars.AvatarImageStoreService;
import ru.yandex.webmaster3.storage.avatars.AvatarPicture;
import ru.yandex.webmaster3.storage.avatars.AvatarsException;
import ru.yandex.webmaster3.storage.avatars.UploadPictureResult;
import ru.yandex.webmaster3.storage.turbo.logo.TurboLogoData;
import ru.yandex.webmaster3.storage.turbo.logo.TurboLogoYDao;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostAction;

/**
 * Created by ifilippov5 on 13.07.17.
 */
@WriteAction
@Description(value = "Загрузить логотип в сторадж")
@Category("turbo")
@Component("/turbo/logo/upload")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UploadTurboLogoAction extends AbstractUserVerifiedHostAction<UploadTurboLogoRequest, UploadTurboLogoResponse> {
    private static final Logger log = LoggerFactory.getLogger(UploadTurboLogoAction.class);

    private static final int MAX_LOGO_FILE_SIZE = 1024 * 1024;
    private static final int MAX_SVG_LOGO_FILE_SIZE = 10 * 1024;
    private final TurboLogoYDao turboLogoYDao;
    private final AvatarImageStoreService avatarImageStoreService;

    @Override
    public UploadTurboLogoResponse process(UploadTurboLogoRequest request) {
        String logoFileName;
        UploadPictureResult uploadLogoResult;

        if (request.getLogoUrl() != null) {
            logoFileName = request.getLogoUrl();
            log.debug("Download turbo logo url = {}", logoFileName);
            try {
                uploadLogoResult = avatarImageStoreService.uploadPicture(request.getLogoUrl(), (Integer) null);
            } catch (AvatarsException e) {
                return new UploadTurboLogoResponse.UnsupportedFormatResponse(this.getClass());
            }
        } else if (request.getLogoFile() != null) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(MAX_LOGO_FILE_SIZE);
            logoFileName = request.getLogoFile().getFileName();
            log.debug("Turbo logo fileName = {}", logoFileName);

            InputStream inputStream = null;
            long logoSize;
            try {
                inputStream = request.getLogoFile().getInputStream();
                logoSize = toDataArray(inputStream, baos);
                log.debug("logo file size = {}", logoSize);
            } catch (IOException e) {
                throw new WebmasterException("IO error occurred while download turbo logo from file " + logoFileName,
                        new WebmasterErrorResponse.IllegalFileParameterResponse(getClass(), "logoFile", null), e);
            } finally {
                IOUtils.closeQuietly(inputStream);
            }
            byte[] logoData = baos.toByteArray();
            if (logoData == null) {
                throw new WebmasterException("No logo in file " + logoFileName,
                        new WebmasterErrorResponse.IllegalFileParameterResponse(this.getClass(), "logoFile", null));
            }
            if (logoSize > MAX_LOGO_FILE_SIZE) {
                return new UploadTurboLogoResponse.LogoIsTooLongResponse(this.getClass(), "logo have size = " + logoSize
                        + ", but maximum acceptable size = " + MAX_LOGO_FILE_SIZE);
            }
            if (logoSize == 0) {
                return new UploadTurboLogoResponse.LogoIsEmptyResponse(this.getClass());
            }
            try {
                uploadLogoResult = avatarImageStoreService.uploadPicture(logoFileName, baos.toByteArray());
                if (uploadLogoResult.isSvg() && logoSize > MAX_SVG_LOGO_FILE_SIZE) {
                    // подсчитаем размер сжатого логотипа
                    ByteArrayOutputStream compressedBytes = new ByteArrayOutputStream();
                    GZIPOutputStream gzipOutputStream = new GZIPOutputStream(compressedBytes);
                    gzipOutputStream.write(logoData);
                    gzipOutputStream.finish();
                    gzipOutputStream.flush();
                    int compressedLogoSize = compressedBytes.toByteArray().length;
                    if (compressedLogoSize > MAX_SVG_LOGO_FILE_SIZE) {
                        return new UploadTurboLogoResponse.LogoIsTooLongResponse(this.getClass(), "svg logo have size = " + logoSize
                                + ", but maximum acceptable size = " + MAX_SVG_LOGO_FILE_SIZE);
                    }
                }
            } catch (AvatarsException e) {
                return new UploadTurboLogoResponse.UnsupportedFormatResponse(this.getClass());
            } catch (IOException e) {
                throw new WebmasterException("IO error occurred while compressing turbo logo from file " + logoFileName,
                        new WebmasterErrorResponse.IllegalFileParameterResponse(getClass(), "logoFile", null), e);
            }
        } else {
            throw new WebmasterException("No logo",
                    new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(), "logoFile", null));
        }

        if (uploadLogoResult.getInvalidPictureErrorCode() != null) {
            if (uploadLogoResult.getInvalidPictureErrorCode() == UploadPictureResult.InvalidPictureErrorCode.IMAGE_IS_TOO_SMALL) {
                return new UploadTurboLogoResponse.LogoIsTooSmallResponse(this.getClass());
            }
            if (uploadLogoResult.getInvalidPictureErrorCode() == UploadPictureResult.InvalidPictureErrorCode.UNABLE_TO_DOWNLOAD_IMAGE_BY_URL) {
                return new UploadTurboLogoResponse.UnableToDownloadLogoByUrlResponse(this.getClass());
            }
        }

        List<AvatarPicture> logos = uploadLogoResult.getPictures();
        if (logos == null || logos.isEmpty() || logos.get(0) == null) {
            throw new WebmasterException("Failed to upload turbo logo",
                    new WebmasterErrorResponse.AvatarsErrorResponse(getClass(), null, "avatars respond invalid data"), null);
        }

        log.info("Found sizes: {}", logos.stream().map(AvatarPicture::getSize).collect(Collectors.joining(",")));

        AvatarPicture logo = logos.get(0);
        String logoId = logo.getFilename();
        long groupId = logo.getGroupId();
        log.debug("Generated logo id: {}, group id: {}", logoId, groupId);
        String publicUrlMds = avatarImageStoreService.getPictureUrl(logo);
        log.debug("Mds logo url: {}", publicUrlMds);

        TurboLogoData logoToSave = new TurboLogoData(request.getHostId(), logoId, groupId, publicUrlMds, DateTime.now(), false,
                uploadLogoResult.getWidth(), uploadLogoResult.getHeight());
        turboLogoYDao.add(logoToSave);
        EnumMap<TurboHostHeaderType, String> logoUrls = new EnumMap<>(TurboHostHeaderType.class);
        for (TurboHostHeaderType type : TurboHostHeaderType.values()) {
            if (type.getLogoSize() != null) {
                logoUrls.put(type, avatarImageStoreService.getPictureUrl(logo, uploadLogoResult.isSvg() ? "svg" : type.getLogoSize()));
            }
        }
        // для фронтэнда склеиваем id в один
        return new UploadTurboLogoResponse.NormalResponse(logoId + TurboLogoData.FRONT_LOGO_ID_SEPARATOR + groupId,
                logoUrls, uploadLogoResult.isSvg());
    }

    private static long toDataArray(InputStream contentInputStream, ByteArrayOutputStream baso) throws IOException {
        contentInputStream = GzipUtils.unGzip(new BufferedInputStream(contentInputStream, 8));
        return IOUtils.copyLarge(contentInputStream, baso);
    }
}
