"""
utils.converter: Custom URL converters to match patterns in @app.route()

The Flask documentation is elusive on this topic. To add a new converter,
proceed as follows:

1. Define a new converter class.
2. Set the [regex] attribute to decide which portion of the URL will be
   considered for conversion (apparently the default is everything until next
   slash or end of string).
3. Define the to_python() and to_url() methods to actually convert.
4. Add the class to __all__ at the bottom of this file.
5. In app/__init__.py, add a dictionary entry to [app.url_map.converters].

For more information, see the Werkzeug documentation:
  <https://werkzeug.palletsprojects.com/en/0.15.x/routing/#custom-converters>
"""

from werkzeug.routing import BaseConverter, ValidationError
from app.models.forum import Forum
from app.models.topic import Topic
from app.models.program import Program
from slugify import slugify


class ForumConverter(BaseConverter):

    # This regex will decide which portion of the URL is converted.
    regex = r'[a-z/]+'
    # Allow slashes to be matched by the regex (this is not by default)
    part_isolating = False

    def to_python(self, url):
        url = '/' + url
        f = Forum.query.filter_by(url=url).first()
        if f is None:
            raise ValidationError(f"ForumConverter: no forum with url {url}")
        return f

    def to_url(self, forum):
        return forum.url[1:]

class PageConverter(BaseConverter):

    # Matches integers for the topic number, then either nothing, a page
    # number, a slug, or a page number followed by a slug
    regex = r'(\d+)(?:/(\d+)|/fin)?(?:/[\w-]+)?'
    part_isolating = False
    object = None
    get_title = lambda o: "empty-title"

    def to_python(self, url):
        tid, *args = url.split('/')
        tid = int(tid)

        if len(args) == 0:
            page = 1
        elif args[0] == "fin":
            page = -1
        else:
            # The argument is considered to be a slug if it's not "last" and it
            # doesn't convert to an int
            try:
                page = int(args[0])
            except ValueError:
                page = 1

        t = self.object.query.filter_by(id=tid).first()
        if t is None:
            raise ValidationError(f"BaseConverter: no object with id {url}")

        return (t, page)

    def to_url(self, object_and_page):
        o, page = object_and_page
        page = str(page) if page != -1 else "fin"
        slug = slugify(self.get_title(o))
        return f'{o.id}/{page}/{slug}'

class TopicPageConverter(PageConverter):
    object = Topic
    get_title = lambda self, t: t.title or "unnamed-topic-error"

class ProgramPageConverter(PageConverter):
    object = Program
    get_title = lambda self, p: p.name or "unnamed-program-error"

# Export only the converter classes
__all__ = ["ForumConverter", "TopicPageConverter", "ProgramPageConverter"]
