#!/usr/bin/python
# -*- coding: utf-8 -*-

# thumbor imaging service
# https://github.com/thumbor/thumbor/wiki

# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com thumbor@googlegroups.com

import mimetypes
import random
from io import BytesIO
from os.path import dirname, join, realpath
from unittest import mock
from urllib.parse import urlencode

from PIL import Image
from ssim import compute_ssim
from tornado.testing import AsyncHTTPTestCase

from thumbor.app import ThumborServiceApp
from thumbor.config import Config
from thumbor.context import Context, RequestParameters
from thumbor.engines.pil import Engine as PilEngine
from thumbor.importer import Importer
from thumbor.transformer import Transformer


def get_ssim(actual, expected):
    if (
        actual.size[0] != expected.size[0]
        or actual.size[1] != expected.size[1]
    ):
        raise RuntimeError(
            "Can't calculate SSIM for images of different sizes ("
            f"one is {actual.size[0]}x{actual.size[1]}, "
            f"the other {expected.size[0]}x{expected.size[1]}).",
        )

    return compute_ssim(actual, expected)


def encode_multipart_formdata(fields, files):
    boundary = b"thumborUploadFormBoundary"
    crlf = b"\r\n"
    lines = []
    for key, value in fields.items():
        lines.append(b"--" + boundary)
        lines.append(
            b'Content-Disposition: form-data; name="%s"' % key.encode()
        )
        lines.append(b"")
        lines.append(value)
    for (key, filename, value) in files:
        lines.append(b"--" + boundary)
        lines.append(
            b'Content-Disposition: form-data; name="%s"; filename="%s"'
            % (key.encode(), filename.encode())
        )
        lines.append(
            b"Content-Type: %s" % mimetypes.guess_type(filename)[0].encode()
            or b"application/octet-stream"
        )
        lines.append(b"")
        lines.append(value)
    lines.append(b"")
    lines.append(b"")
    lines.append(b"--" + boundary + b"--")
    body = crlf.join(lines)
    content_type = b"multipart/form-data; boundary=%s" % boundary
    return content_type, body


class TestCase(AsyncHTTPTestCase):
    _multiprocess_can_split_ = True

    def get_app(self):
        self.config = (  # This is a test case pylint: disable=attribute-defined-outside-init
            self.get_config()
        )
        self.server = (  # This is a test case pylint: disable=attribute-defined-outside-init
            self.get_server()
        )
        self.importer = (  # This is a test case pylint: disable=attribute-defined-outside-init
            self.get_importer()
        )
        self.request_handler = (  # This is a test case pylint: disable=attribute-defined-outside-init
            self.get_request_handler()
        )
        self.importer.import_modules()

        self.context = self.get_context()
        return ThumborServiceApp(self.context)

    def get_config(self):  # Meant to be overriden pylint: disable=no-self-use
        return Config()

    def get_server(self):  # Meant to be overriden pylint: disable=no-self-use
        return None

    def get_importer(self):
        importer = Importer(self.config)
        importer.import_modules()
        return importer

    def get_request_handler(
        self,
    ):  # Meant to be overriden pylint: disable=no-self-use
        return None

    def get_context(self):
        self.importer.import_modules()
        return Context(
            self.server, self.config, self.importer, self.request_handler
        )

    async def async_fetch(self, path, method="GET", body=None, headers=None):
        return await self.http_client.fetch(
            self.get_url(path),
            method=method,
            body=body,
            headers=headers,
            allow_nonstandard_methods=True,
            raise_error=False,
        )

    async def async_get(self, path, headers=None):
        return await self.async_fetch(
            path, method="GET", body=urlencode({}, doseq=True), headers=headers
        )

    async def async_post(self, path, headers, body):
        return await self.async_fetch(
            path,