#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

import brbn
import email.utils as _email
import json as _json
import logging as _logging
import os as _os
import quopri as _quopri
import re as _re
import sqlite3 as _sqlite
import time as _time
import textwrap as _textwrap

from datetime import datetime as _datetime
from pencil import *

_log = _logging.getLogger("haystack")
_strings = StringCatalog(__file__)
_topics = _json.loads(_strings["topics"])

class Haystack(brbn.Application):
    def __init__(self, home_dir):
        super().__init__(home_dir)

        path = _os.path.join(self.home, "data", "data.sqlite")
        self.database = Database(path)

        self.root_resource = _IndexPage(self)
        self.search_page = _SearchPage(self)
        self.thread_page = _ThreadPage(self)
        self.message_page = _MessagePage(self)

    def receive_request(self, request):
        request.database_connection = self.database.connect()

        try:
            return super().receive_request(request)
        finally:
            request.database_connection.close()

class _IndexPage(brbn.Page):
    def __init__(self, app):
        super().__init__(app, "/", _strings["index_page_body"])

    def get_title(self, request):
        return "Haystack"

    @brbn.xml
    def render_topics(self, request):
        items = list()

        for topic in _topics:
            href = self.app.search_page.get_href(request, query=topic)
            text = xml_escape(topic)

            items.append(html_a(text, href))

        return html_ul(items, class_="four-column")

class _SearchPage(brbn.Page):
    def __init__(self, app):
        super().__init__(app, "/search", _strings["search_page_body"])

    def get_title(self, request):
        query = request.get("query")
        return "Search '{}'".format(query)

    def render_query(self, request):
        return request.get("query")

    @brbn.xml
    def render_threads(self, request):
        query = request.get("query")

        sql = ("select * from messages where id in "
               "(select distinct thread_id from messages_fts "
               " where messages_fts match ? limit 1000) "
               "order by date desc")

        escaped_query = query.replace("\"", "\"\"")

        records = self.app.database.query(request, sql, escaped_query)
        thread = Thread()
        rows = list()

        for record in records:
            thread.load_from_record(record)

            thread_link = thread.get_link(request)

            row = [
                thread_link,
                xml_escape(thread.from_address),
                thread.authored_words,
                xml_escape(str(_email.formatdate(thread.date)[:-6])),
            ]

            rows.append(row)

        return html_table(rows, False, class_="messages four")

class _ThreadPage(brbn.Page):
    def __init__(self, app):
        super().__init__(app, "/thread", _strings["thread_page_body"])

    def get_title(self, request):
        return "Thread '{}'".format(request.thread.subject)

    def process(self, request):
        id = request.get("id")
        request.thread = self.app.database.get(request, Message, id)

        sql = ("select * from messages "
               "where thread_id = ? "
               "order by thread_position, date asc "
               "limit 1000")

        records = self.app.database.query(request, sql, request.thread.id)

        request.messages = list()
        request.messages_by_id = dict()

        for record in records:
            message = Message()
            message.load_from_record(record)

            request.messages.append(message)
            request.messages_by_id[message.id] = message

    def render_title(self, request):
        return request.thread.subject

    @brbn.xml
    def render_index(self, request):
        rows = list()

        for i, message in enumerate(request.messages):
            date = _time.strftime("%d %b %Y", _time.gmtime(message.date))
            number = i + 1
            title = self.get_message_title(request, message, number)

            row = [
                html_a(xml_escape(title), "#{}".format(number)),
                xml_escape(date),
                message.authored_words,
            ]

            rows.append(row)

        return html_table(rows, False, class_="messages")

    @brbn.xml
    def render_messages(self, request):
        out = list()

        for i, message in enumerate(request.messages):
            number = i + 1
            title = self.get_message_title(request, message, number)

            out.append(html_elem("h2", title, id=str(number)))
            out.append(html_elem("pre", xml_escape(message.content)))

        return "\n".join(out)

    def get_message_title(self, request, message, number):
        title = "{}. {}".format(number, message.from_name)

        if message.in_reply_to_id is not None:
            rmessage = request.messages_by_id.get(message.in_reply_to_id)

            if rmessage is not None:
                rperson = rmessage.from_name
                title = "{} replying to {}".format(title, rperson)

        return title

class _MessagePage(brbn.Page):
    def __init__(self, app):
        super().__init__(app, "/message", _strings["message_page_body"])

    def get_title(self, request):
        return "Message '{}'".format(request.message.subject)

    def process(self, request):
        id = request.get("id")
        request.message = self.app.database.get(request, Message, id)
    
    def render_title(self, request):
        return request.message.subject

    @brbn.xml
    def render_thread_link(self, request):
        thread = None
        thread_id = request.message.thread_id
        thread_link = xml_escape(thread_id)

        if thread_id is not None:
            try:
                thread = self.app.database.get(request, Message, thread_id)
            except ObjectNotFound:
                pass

            if thread is not None: