# events.py
#
# Copyright 2020-2025 Fabio Comuni, et al.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from typing import Optional

from gi.repository import GObject
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import Adw

from ..utils import convert_markdown_links, clean_markup, markup_to_text
from ..widgets import EventActionRow, Author, StarButton
from ..widgets import WebLinkActionRow, weblinkactionrowactivated
from ..models import Event

# Detail page can link to other detail page via the "overlapping events" list
# Overlapping events are circular (as in event B overlaps A , so event A overlaps B)
# so just pushing pages to the stack will can get the user to a very long stack:
#   List -> Event A -> Event B -> A -> B -> A -> B
#
# So we use tags to find if a detail page is already in stack and pop back to that:
#   List -> Event A -> Event B
#       user clicks on overlapping Event A, goes back to
#   List -> Event A
#
# The tag name is based on 'tagfmt' propery, which defaults to 'TAG_FMT'.
#
# But then there is search.
# If an user is on a detail page and trigger search, the search page is pushed
# to the stack and from there any result will push a detail page.
#   List -> Event A -> Search -> Event C
#
# And the user can look at results and go back to were it was gout out from search
# (either navigating back the stack or triggering the 'search' action)
#
# But if user in search page clicks on an event which detail page is already on
# the stack, it will popped back before the search page
#   List -> Event A -> Search
#      user clicks on Avent A, then goes to
#   List -> Event A
#
# Which exit the 'search' state (the togglebutton in side headbar deactivate) and is
# inconsistent with previous search navigation behaviour
#
# So detail pages created from search gets different 'tagfmt' value (see 'PageEvents::on_litstbox_activated')
# this will keep the navigation behaviour of going back to already pushed detail page clicking
# on overlapping events, but keeping the user in the 'search state'
#   List -> Event A -> Search
#      user clicks on Avent A, then goes to
#   List -> Event A -> Search -> Event A
#       clicks on overlapping Event B
#   List -> Event A -> Search -> Event A -> Event B
#       clicks on overlapping Event A
#   List -> Event A -> Search -> Event A
#       goes back
#   List -> Event A -> Search
#       goes back
#   List -> Event A
#
# Also, when user clicks on incoming event notification, the detail page for the
# event is pushed with yet another 'tagfmt' (see 'MainWindow::_on_notification_action')
#

TAG_FMT = "details-{0.id}"


@Gtk.Template(resource_path="/net/kirgroup/confy/pages/details.ui")
class PageDetails(Adw.NavigationPage):
    __gtype_name__ = "ConfyPageDetails"

    scroll: Gtk.ScrolledWindow = Gtk.Template.Child()  # type: ignore
    star_button: StarButton = Gtk.Template.Child()  # type: ignore
    conflicts_list: Gtk.ListBox = Gtk.Template.Child()  # type: ignore
    links_list: Gtk.ListBox = Gtk.Template.Child()  # type: ignore
    author_box: Gtk.FlowBox = Gtk.Template.Child()  # type: ignore

    tagfmt: str = GObject.property(type=str, default=TAG_FMT)

    _subtitle = ""
    _authors = ""
    _time = ""
    _room = ""
    _track = ""
    _abstract = ""
    _description = ""

    @GObject.Property(type=str, default="")
    def subtitle(self):
        return self._subtitle

    @subtitle.setter
    def subtitle_setter(self, v):
        self._subtitle = v
        self.notify("has-subtitle")

    @GObject.Property(type=bool, default=False)
    def has_subtitle(self):
        return self._subtitle != ""

    @GObject.Property(type=str, default="")
    def authors(self):
        return self._authors

    @authors.setter
    def authors_setter(self, v):
        self._authors = v
        self.notify("has-authors")

    @GObject.Property(type=bool, default=False)
    def has_authors(self):
        return self._authors != ""

    @GObject.Property(type=str, default="")
    def time(self):
        return self._time

    @time.setter
    def time_setter(self, v):
        self._time = v

    @GObject.Property(type=str, default="")
    def room(self):
        return self._room

    @room.setter
    def room_setter(self, v):
        self._room = v

    @GObject.Property(type=str, default="")
    def track(self):
        return self._track

    @track.setter
    def track_setter(self, v):
        self._track = v

    @GObject.Property(type=str, default="")
    def abstract(self):
        return self._abstract

    @abstract.setter
    def abstract_setter(self, v):
        self._abstract = v
        self.notify("has-abstract")

    @GObject.Property(type=bool, default=False)
    def has_abstract(self):
        return self._abstract is not None and self._abstract != "" and self._abstract != "None"

    @GObject.Property(type=str, default="")
    def description(self):
        return self._description

    @description.setter
    def description_setter(self, v):
        self._description = v
        self.notify("has-description")

    @GObject.Property(type=bool, default=False)
    def has_description(self):
        return self._description is not None and self._description != "" and self._description != "None"

    _handler_id: Optional[int] = None
    obj: Optional[Event] = None

    def __init__(self, obj, **kwargs):
        super().__init__(**kwargs)
        self.set_obj(obj)

    def set_obj(self, obj):
        if self._handler_id:
            self.obj.disconnect(self._handler_id)
        self.obj = obj
        self._handler_id = obj.connect('update', self._on_obj_update)

        self.scroll.get_vadjustment().set_value(0)
        self._on_obj_update()

    def _on_obj_update(self, *_):
        self.props.tag = self.tagfmt.format(self.obj)
        print(f"PageDetails obj_update {self.obj} tag:{self.props.tag}")
        self.props.title = markup_to_text(self.obj.title)
        self.props.authors = ", ".join([clean_markup(str(p)) for p in self.obj.persons()])
        self.props.subtitle = markup_to_text(self.obj.subtitle)
        self.props.time = "{}-{}".format(
            self.obj.start_in_tz().strftime("%A %H:%M"),
            self.obj.end_in_tz().strftime("%H:%M"))
        self.props.room = clean_markup(self.obj.room)
        self.props.track = clean_markup(self.obj.track)

        self.props.abstract = clean_markup(convert_markdown_links(self.obj.abstract))
        self.props.description = clean_markup(convert_markdown_links(self.obj.description))

        self.author_box.remove_all()
        for author in self.obj.persons():
            widget = Author(author)
            wrap = Gtk.FlowBoxChild(child=widget)
            self.author_box.append(wrap)

        self._conflicts = conflicts = list(self.obj.get_conflicts())
        self.conflicts_list.props.visible = len(conflicts) > 0
        w = self.conflicts_list.get_last_child()
        while w:
            self.conflicts_list.remove(w)
            w = self.conflicts_list.get_last_child()
        for c in conflicts:
            row = EventActionRow(c)
            self.conflicts_list.append(row)

        links = list(self.obj.links())
        self.links_list.props.visible = len(links) > 0
        w = self.links_list.get_last_child()
        while w:
            self.links_list.remove(w)
            w = self.links_list.get_last_child()
        for link in links:
            row = WebLinkActionRow(link.name, link.href)
            self.links_list.append(row)

        self.star_button.props.is_starred = self.obj.starred

    def copy_to_clipboard(self):
        cb = Gdk.Display.get_default().get_clipboard()
        data = [self.get_title()]

        def _append(txt):
            if txt != "":
                data.append(txt)

        _append(self._subtitle)
        _append(self._authors)
        data.append("")
        _append(self._time)
        _append(self._room)
        _append(self._track)
        data.append("")
        _append(self._abstract)
        _append(self._description)
        data.append("")
        for link in self.obj.links():
            data.append(f"{link.name}\n{link.href}\n")

        text = "\n".join(data)
        text = markup_to_text(text)
        print(text)
        cb.set(text)

    @Gtk.Template.Callback()
    def toggle_starred(self, *_):
        self.obj.set_star(not self.obj.starred)

    @Gtk.Template.Callback()
    def on_conflict_activated(self, listbox, row):
        obj = row.obj
        tag = self.tagfmt.format(obj)
        stack = self.get_parent()
        page = stack.find_page(tag)
        print(obj, tag, page)
        if page:
            stack.pop_to_page(page)
        else:
            page = PageDetails(obj=obj, tagfmt=self.tagfmt)
            stack.push(page)

    @Gtk.Template.Callback()
    def on_link_activated(self, listbox, row):
        weblinkactionrowactivated(listbox, row)
