from mitama.app import Controller
from mitama.app.http import Response
from mitama.models import User, Node, Role, is_admin
import json
import re
import markdown
import magic

from .model import db, Board, Thread, Res, Permission
from .forms import (
    CreateBoardForm,
    CreateThreadForm,
    SettingForm,
    UpdateThreadForm,
    UpdateBoardForm
)
from .utils import hiroyuki


class HomeController(Controller):
    def handle(self, request):
        template = self.view.get_template("home.html")
        threads = list()
        for board in Board.list_subscribed(request.user):
            for thread in board.threads:
                if not thread.closed:
                    threads.append(thread)
        return Response.render(template, {
            "is_admin": is_admin,
            "threads": threads,
            "boards": Board.list_subscribed(request.user)
        })

    def settings(self, request):
        template = self.view.get_template("settings.html")
        error = ""
        if request.method == "POST":
            form = SettingForm(request.post())
            for screen_name, roles in form['permissions'].items():
                permission = Permission.retrieve(screen_name=screen_name)
                permission.roles = [
                    Role.retrieve(screen_name=r) for r in roles
                ]
            permission.update()
            error = "保存しました"
        return Response.render(template, {
            "permissions": Permission.list(),
            "roles": Role.list(),
            "error": error
        })


class BoardController(Controller):
    def create(self, request):
        form = CreateBoardForm(request.post())
        board = Board()
        board.name = form['name']
        board.owner = Node.retrieve(form['owner'])
        board.create()
        return Response.json({
            "_id": board._id,
        })

    def retrieve(self, request):
        template = self.view.get_template("board.html")
        board = Board.retrieve(request.params['board'])
        return Response.render(template, {
            "board": board,
            "boards": Board.list_subscribed(request.user)
        })

    def update(self, request):
        board = Board.retrieve(request.params['board'])
        form = UpdateBoardForm(request.post())
        board.name = form['name']
        board.update()
        return Response.json({
            "_id": board._id,
            "name": board.name,
        })

    def subscribe(self, request):
        board = Board.retrieve(request.params['board'])
        board.subscribers.append(request.user)
        board.update()
        return Response.json({
            "subscribe": True,
            "board": {
                "_id": board._id
            }
        })

    def unsubscribe(self, request):
        board = Board.retrieve(request.params['board'])
        board.subscribers.remove(request.user)
        board.update()
        return Response.json({
            "subscribe": False,
            "board": {
                "_id": board._id
            }
        })


class UserController(Controller):
    def icon(self, request):
        user = User.retrieve(request.params['user'])
        f = magic.Magic(mime=True, uncompress=True)
        mime = f.from_buffer(self.icon)
        return Response(user._icon, content_type=mime)


class ThreadController(Controller):
    def create(self, request):
        form = CreateThreadForm(request.post())
        board = Board.retrieve(request.params['board'])
        thread = Thread()
        thread.title = form['title']
        thread.board = board
        thread.user = request.user
        thread.create()
        for user in board.subscribers:
            user.push({
                "title": thread.title + " - Kyokusui",
                "body": "スレッドが立ちました",
                "icon": self.app.convert_fullurl(
                    request,
                    "/user/{}/icon".format(request.user._id)
                )
            })
        return Response.json({
            "_id": thread._id,
        })

    def retrieve(self, request):
        template = self.view.get_template("thread.html")
        board = Board.retrieve(request.params['board'])
        thread = Thread.retrieve(request.params['thread'])
        return Response.render(template, {
            "board": board,
            "thread": thread,
            "boards": Board.list_subscribed(request.user)
        })

    def update(self, request):
        thread = Thread.retrieve(request.params['thread'])
        form = UpdateThreadForm(request.post())
        thread.title = form['title']
        thread.update()
        return Response.json({
            "_id": thread._id,
            "title": thread.title,
        })


class WebSocketController(Controller):
    streams = {}

    def handle(self, request):
        ws = request.websocket
        board = Board.retrieve(request.params['board'])
        thread = Thread.retrieve(request.params['thread'])
        if board._id not in self.streams:
            self.streams[board._id] = dict()
        if thread._id not in self.streams[board._id]:
            self.streams[board._id][thread._id] = list()
        self.streams[board._id][thread._id].append(ws)
        db.manager.close_session()
        while True:
            received = ws.receive()
            if received is None:
                continue
            try:
                db.manager.start_session()
                data = json.loads(received)
                if data['type'] == "message":
                    res = Res()
                    res.data = json.dumps(data['data'])
                    res.user = request.user
                    res.thread = Thread.retrieve(request.params['thread'])
                    res.create()
                    mentions = re.findall(
                        r"\>\>([0-9a-zA-Z.-_]+)",
                        data['data']['message']
                    )
                    for mention in mentions:
                        try:
                            user = User.retrieve(
                                screen_name=mention
                            )
                            user.push({
                                "title": res.thread.title + " - Kyokusui",
                                "body": res.user.name+"さんがメンションしました",
                                "icon": self.app.convert_fullurl(
                                    request,
                                    "/user/{}/icon".format(res.user._id)
                                )
                            })
                        except Exception as err:
                            print(err, mention)
                            pass
                    remove = list()
                    for s in self.streams[board._id][thread._id]:
                        try:
                            s.send(json.dumps({
                                "type": "message",
                                "_id": res._id,
                                "data": {
                                    "message": markdown.markdown(
                                        hiroyuki(res.parsed_data["message"]),
                                        extensions=['fenced_code']
                                    ),
                                    "images": res.parsed_data["images"]
                                },
                                "user": {
                                    "_id": res.user._id,
                                    "name": res.user.name,
                                    "screen_name": res.user.screen_name
                                },
                                "datetime": res.datetime.strftime(
                                    "%Y-%m-%d %H:%M:%S"
                                ),
                                "thread": thread._id,
                            }))
                        except Exception as err:
                            print(err)
                            remove.append(s)
                    for s in remove:
                        self.streams[board._id][thread._id].remove(s)
                elif data['type'] == "close":
                    thread = Thread.retrieve(request.params['thread'])
                    thread.close()
                    remove = list()
                    for s in self.streams[board._id][thread._id]:
                        try:
                            s.send(json.dumps({
                                "type": "close"
                            }))
                        except Exception as err:
                            print(err)
                            pass
                    del self.streams[board._id][thread._id]
            except Exception as err:
                print(err)
                db.manager.rollback_session()
            finally:
                db.manager.close_session()
        return Response()
