import asyncio
import mimetypes
import pathlib
import urllib.parse
from os.path import isfile

import aiofiles
from aiohttp import web
from PIL import Image
from pillow_heif import register_heif_opener

from snek.system.view import BaseView

register_heif_opener()

class ChannelAttachmentView(BaseView):
    async def get(self):
        relative_path = self.request.match_info.get("relative_url")
        channel_attachment = await self.services.channel_attachment.get(
            relative_url=relative_path
        )

        if not channel_attachment or not isfile(channel_attachment["path"]):
            return web.HTTPNotFound()

        original_format = mimetypes.guess_type(channel_attachment["path"])[0]
        format_ = self.request.query.get("format")
        width = self.request.query.get("width")
        height = self.request.query.get("height")

        if any([format_, width, height]) and original_format.startswith("image/"):
            if not format_:
                format_ = original_format.split("/")[1]
            with Image.open(channel_attachment["path"]) as image:
                response = web.StreamResponse(
                    status=200,
                    reason="OK",
                    headers={
                        "Cache-Control": f"public, max-age={1337 * 420}",
                        "Content-Type": f"image/{format_}",
                        "Content-Disposition": f'attachment; filename="{channel_attachment["name"]}"',
                    },
                )

                if width or height:
                    width = min(int(width), image.size[0]) if width else None
                    height = min(int(height), image.size[1]) if height else None

                    if width and height:
                        smallest_ratio = max(
                            image.size[0] / int(width), image.size[1] / int(height)
                        )
                        image.thumbnail(
                            (
                                int(image.size[0] / smallest_ratio),
                                int(image.size[1] / smallest_ratio),
                            )
                        )
                    elif width:
                        image.thumbnail(
                            (
                                int(width),
                                int(image.size[1] * image.size[0] / int(width)),
                            )
                        )
                    elif height:
                        image.thumbnail(
                            (
                                int(image.size[0] * image.size[1] / int(height)),
                                int(height),
                            )
                        )

                await response.prepare(self.request)
                # response.write is async but image.save is not
                naughty_steal = response.write
                tasks = []

                def sync_writer(*args, **kwargs):
                    tasks.append(naughty_steal(*args, **kwargs))
                    return True

                setattr(response, "write", sync_writer)

                image.save(
                    response, format=format_, quality=100, optimize=True, save_all=True
                )

                setattr(response, "write", naughty_steal)

                await asyncio.gather(*tasks)

                return response
        else:
            response = web.FileResponse(channel_attachment["path"])
            response.headers["Cache-Control"] = f"public, max-age={1337 * 420}"
            response.headers["Content-Disposition"] = (
                f'attachment; filename="{channel_attachment["name"]}"'
            )
            response.headers["Content-Type"] = original_format
            return response

    async def post(self):
        channel_uid = self.request.match_info.get("channel_uid")
        user_uid = self.request.session.get("uid")

        channel_member = await self.services.channel_member.get(
            user_uid=user_uid, channel_uid=channel_uid, deleted_at=None, is_banned=False
        )

        if not channel_member:
            return web.HTTPNotFound()

        reader = await self.request.multipart()
        attachments = []

        while field := await reader.next():
            filename = field.filename
            if not filename:
                continue

            attachment = await self.services.channel_attachment.create_file(
                channel_uid=channel_uid, name=filename, user_uid=user_uid
            )

            attachments.append(attachment)
            pathlib.Path(attachment["path"]).parent.mkdir(parents=True, exist_ok=True)
            async with aiofiles.open(attachment["path"], "wb") as f:
                while chunk := await field.read_chunk():
                    await f.write(chunk)

        attachment_records = []
        for attachment in attachments:
            attachment_record = attachment.record
            attachment_record["relative_url"] = urllib.parse.quote(
                attachment_record["relative_url"]
            )
            attachment_records.append(attachment_record)

        return web.json_response(
            {
                "message": "Files uploaded successfully",
                "files": attachment_records,
                "channel_uid": channel_uid,
            }
        )


class ChannelView(BaseView):
    async def get(self):
        channel_name = self.request.match_info.get("channel")
        if channel_name is None:
            return web.HTTPNotFound()
        channel = await self.services.channel.get(label="#" + channel_name)
        if channel is None:
            channel = await self.services.channel.get(label=channel_name)
        channel = await self.services.channel.get(channel_name)
        if channel is None:
            channel = await self.services.channel.get(label=channel_name)
        if channel is None:
            user = await self.services.user.get(uid=self.session.get("uid"))
            is_listed = self.request.query.get("listed", False) == "true"
            is_private = self.request.query.get("private", False) == "true"
            channel = await self.services.channel.create(
                label=channel_name,
                created_by_uid=user["uid"],
                description="No description provided.",
                tag="user",
                is_private=is_private,
                is_listed=is_listed,
            )
            await self.services.channel_member.create(
                channel_uid=channel["uid"],
                user_uid=user["uid"],
                is_moderator=True,
                is_read_only=False,
                is_muted=False,
                is_banned=False,
            )

        return web.HTTPFound("/channel/{}.html".format(channel["uid"]))
