from giara.constants import RESOURCE_PREFIX
from gettext import gettext as _
from gi.repository import Gtk, Adw, GLib, GObject
from giara.common_post_box import CommonPostBox, InteractiveEntityBox
from giara.new_post_window import NewCommentWindow
from giara.markdown_view import MarkdownView
from giara.confManager import ConfManager
from threading import Thread
from time import sleep
from giara.giara_clamp import new_clamp
from praw.models import MoreComments, Comment
from giara.children_box import ChildrenBox
from giara.ellipsized_label import make_ellipsized_label
from giara.scrolled_win import GiaraScrolledWin


def create_comment_widget(comment, refresh_func, me, level=0,
                          visible_dict=dict()):
    return (
        MoreCommentsBox(comment, refresh_func, me, level, visible_dict)
        if isinstance(comment, MoreComments) else
        CommentBox(comment, refresh_func, me, level, visible_dict)
    )


class MoreCommentsBox(Gtk.Box):
    def __init__(self, mc, refresh_func, me, level=0, visible_dict=dict(),
                 **kwargs):
        super().__init__(orientation=Gtk.Orientation.VERTICAL, **kwargs)
        self.mc = mc
        self.level = level
        self.refresh_func = refresh_func
        self.me = me
        self.visible_dict = visible_dict
        self.load_btn = Gtk.Button.new_with_label(_('Load more comments'))
        self.append(self.load_btn)
        self.load_btn_removed = False

        self.load_btn.connect('clicked', self.on_load_more)

    def on_load_more(self, *args):
        self.load_btn.set_sensitive(False)

        def af():
            comments = self.mc.comments()
            for comment in comments:
                if isinstance(comment, Comment):
                    comment.body
                GLib.idle_add(cb, comment)
            GLib.idle_add(final_cb)

        def cb(comment):
            if not self.load_btn_removed:
                self.remove(self.load_btn)
                self.load_btn_removed = True
            cbox = create_comment_widget(
                comment, self.refresh_func, self.me,
                level=self.level, visible_dict=self.visible_dict
            )
            self.append(cbox)
            cbox.show()

        def final_cb():
            self.show()

        Thread(target=af, daemon=True).start()


class CommentBox(InteractiveEntityBox):
    def __init__(self, comment, refresh_func, me, level=0, visible_dict=dict(),
                 **kwargs):
        super().__init__(
            Gtk.Builder.new_from_resource(
                f'{RESOURCE_PREFIX}/ui/comment_box.ui'
            ),
            **kwargs
        )
        self.comment = comment
        self.set_entity(comment)
        self.me = me
        self.refresh_func = refresh_func
        self.level = level
        self.visible_dict = visible_dict
        self.reply_btn = self.builder.get_object('reply_btn')
        self.reply_btn.connect(
            'clicked',
            self.on_reply_clicked
        )
        self.author_label = self.builder.get_object('author_label')
        self.op_icon = self.builder.get_object('op_icon')
        self.me_icon = self.builder.get_object('me_icon')
        self.comment_label = self.builder.get_object('comment_label')
        self.replies_container = ChildrenBox(
            orientation=Gtk.Orientation.VERTICAL
        )
        self.builder.get_object('replies_container').append(
            self.replies_container
        )

        self.comment_container = self.builder.get_object('comment_container')
        self.body_view = MarkdownView(self.comment.body)
        self.comment_container.append(self.body_view)
        author_name = _('Author unknown')
        if hasattr(self.comment, 'author') and self.comment.author is not None:
            author_name = f'u/{self.comment.author.name}'
        self.author_label.set_text(author_name)

        if self.level > 0:
            self.builder.get_object(
                'main_box'
            ).get_style_context().add_class(f'nested-{(self.level - 1) % 8}')
        author_label_style_context = self.author_label.get_style_context()
        if self.comment.is_submitter:
            author_label_style_context.add_class('op_comment')
            self.op_icon.set_visible(True)
        else:
            author_label_style_context.add_class('comment_author')
            self.op_icon.set_visible(False)
        if self.comment.author == self.me:
            for sc in ('op_comment', 'comment_author'):
                author_label_style_context.remove_class(sc)
            author_label_style_context.add_class('green')
            self.me_icon.set_visible(True)
        else:
            self.me_icon.set_visible(False)

        self.collapse_replies_btn = self.builder.get_object(
            'collapse_replies_btn'
        )
        self.collapse_replies_btn_icon = self.builder.get_object(
            'collapse_replies_btn_icon'
        )
        self.collapse_icon_style_context = \
            self.collapse_replies_btn_icon.get_style_context()

        self.replies_revealer = self.builder.get_object('replies_revealer')
        self.replies_visible = True

        def toggle_replies(*args):
            self.replies_visible = not self.replies_visible
            self.replies_revealer.set_reveal_child(self.replies_visible)
            if self.replies_visible:
                self.collapse_icon_style_context.remove_class('replies-closed')
            else:
                self.collapse_icon_style_context.add_class('replies-closed')

        if (
                self.comment.id in self.visible_dict.keys()
                and not self.visible_dict[self.comment.id]
        ):
            toggle_replies()

        self.collapse_replies_btn.connect('clicked', toggle_replies)

        self.populate_replies()
        # GLib.idle_add(self.populate_replies)

    def populate_replies(self):
        for reply in self.comment.replies:
            n_reply = create_comment_widget(
                reply, self.refresh_func, self.me, self.level+1,
                visible_dict=self.visible_dict
            )
            self.replies_container.append(n_reply)
            n_reply.show()

    def on_reply_clicked(self, *args):
        win = NewCommentWindow(
            self.comment,
            lambda *args: self.refresh_func(wait_for_comments_update=True)
        )
        win.set_transient_for(self.get_root())
        win.present()

    def get_comments(self, res=set()):
        res.add(self)
        for c in self.replies_container.get_children():
            if isinstance(c, CommentBox):
                c.get_comments(res)
        return res


class MultiCommentsBox(ChildrenBox):
    __gsignals__ = {
        'comments_loaded': (
            GObject.SignalFlags.RUN_LAST,
            None,
            (str,)
        )
    }

    def __init__(self, comments, refresh_func, me, level=0,
                 visible_dict=dict(), **kwargs):
        super().__init__(orientation=Gtk.Orientation.VERTICAL, **kwargs)
        self.me = me
        self.level = level
        self.refresh_func = refresh_func
        self.comments = comments
        self.populated = False
        self.visible_dict = visible_dict
        # self.populate()

    def populate(self, *args):
        self.populated = False

        def af():
            # self.comments.replace_more(limit=None)
            for comment in self.comments:
                if isinstance(comment, Comment):
                    comment.body
                GLib.idle_add(cb, comment)
            GLib.idle_add(final_cb)

        def cb(comment):
            cbox = create_comment_widget(
                comment, self.refresh_func, self.me,
                level=self.level, visible_dict=self.visible_dict
            )
            self.append(cbox)
            cbox.show()

        def final_cb():
            self.show()
            self.populated = True
            self.emit('comments_loaded', '')

        Thread(target=af, daemon=True).start()

    def get_comments(self, res=set()):
        for c in self.get_children():
            c.get_comments(res)
        return list(res)

    def get_visible_dict(self):
        res = dict()
        for cw in self.get_comments():
            res[cw.comment.id] = cw.replies_visible
        return res


class PostBody(CommonPostBox):
    def __init__(self, post, refresh_func, **kwargs):
        super().__init__(
            Gtk.Builder.new_from_resource(
                f'{RESOURCE_PREFIX}/ui/post_body.ui'
            ),
            **kwargs
        )
        self.set_entity(post)
        self.body_view = MarkdownView(self.post.selftext)
        self.body_container = self.builder.get_object('body_container')
        self.body_container.append(self.body_view)
        self.refresh_func = refresh_func

        self.reply_btn = self.builder.get_object('reply_btn')
        self.reply_btn.connect(
            'clicked',
            self.on_reply_clicked
        )

    def on_reply_clicked(self, *args):
        win = NewCommentWindow(
            self.post,
            lambda *args: self.refresh_func(wait_for_comments_update=True)
        )
        win.set_transient_for(self.get_root())
        win.present()


class PostDetailsHeaderbar(Gtk.WindowHandle):
    def __init__(self, post, back_func, refresh_func):
        super().__init__(vexpand=False, hexpand=True)
        self.post = post
        self.builder = Gtk.Builder.new_from_resource(
            f'{RESOURCE_PREFIX}/ui/post_details_headerbar.ui'
        )
        self.headerbar = self.builder.get_object('headerbar')
        self.headerbar.set_title_widget(make_ellipsized_label(self.post.title))

        self.back_btn = self.builder.get_object('back_btn')
        self.back_btn.connect('clicked', lambda *args: back_func())

        self.refresh_btn = self.builder.get_object('refresh_btn')
        self.refresh_btn.connect(
            'clicked', lambda *args: refresh_func(reload_post=True)
        )

        self.set_child(self.headerbar)


class PostDetailsView(Adw.Bin):
    def __init__(self, post, back_func, target_comment=None):
        super().__init__(hexpand=True, vexpand=True)
        self.confman = ConfManager()
        self.target_comment = target_comment
        self.reddit = self.confman.reddit
        self.post = post
        self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.inner_box = None
        self.multi_comments_box = None
        self.post_body = None

        self.headerbar = PostDetailsHeaderbar(
            self.post, back_func, self.refresh
        )
        self.main_box.append(self.headerbar)
        self.sw = GiaraScrolledWin()
        self.sw_viewport = Gtk.Viewport(
            vscroll_policy=Gtk.ScrollablePolicy.NATURAL
        )
        self.clamp = new_clamp()
        self.sw_viewport.set_child(self.clamp)
        self.sw.set_child(self.sw_viewport)
        self.main_box.append(self.sw)
        self.set_child(self.main_box)

        self.refresh(reload_post=False)

    def populate_inner_box(self):
        # removing from clamp cannot be done and isn't necessary
        self.inner_box = ChildrenBox(
            orientation=Gtk.Orientation.VERTICAL, vexpand=False
        )
        self.inner_box.get_style_context().add_class('card')
        self.clamp.set_child(self.inner_box)
        self.inner_box.append(self.post_body)
        separator = Gtk.Separator(
            orientation=Gtk.Orientation.HORIZONTAL, vexpand=False
        )
        self.inner_box.append(separator)
        self.inner_box.append(self.multi_comments_box)
        self.show()
        self.jump_to_target()

    def jump_to_target(self, *args):
        if self.target_comment is None:
            return

        def af():
            while not self.multi_comments_box.populated:
                sleep(1)
            GLib.idle_add(cb)

        def cb():
            comments_widgets = self.multi_comments_box.get_comments()
            comment_w = None
            for c in comments_widgets:
                if self.target_comment.id == c.comment.id:
                    comment_w = c
                    break
            if comment_w is not None:
                adj = self.sw.get_vadjustment()
                adj.set_value(
                    min(comment_w.get_allocation().y, adj.get_upper())
                )
                self.sw.set_vadjustment(adj)

        Thread(target=af, daemon=True).start()

    def refresh(self, reload_post=True, wait_for_comments_update=False):
        self.headerbar.refresh_btn.set_sensitive(False)

        def af():
            if reload_post:
                if wait_for_comments_update:
                    tries = 0
                    cl = self.post.comments.list()
                    self.post = self.reddit.submission(self.post.id)
                    while (
                            tries < 10 and
                            len(cl) == len(self.post.comments.list())
                    ):
                        tries += 1
                        sleep(1)
                        self.post = self.reddit.submission(self.post.id)
                else:
                    self.post = self.reddit.submission(self.post.id)
            GLib.idle_add(cb)

        def cb():
            self.post_body = PostBody(self.post, self.refresh)
            visible_dict = (
                self.multi_comments_box.get_visible_dict()
                if self.multi_comments_box is not None else dict()
            )
            self.multi_comments_box = MultiCommentsBox(
                self.post.comments, self.refresh, self.reddit.user.me(),
                visible_dict=visible_dict
            )
            self.multi_comments_box.populate()
            self.populate_inner_box()
            self.headerbar.refresh_btn.set_sensitive(True)

        Thread(target=af, daemon=True).start()
