#
# Copyright (c) Daniel Sheffield 2023
# All rights reserved
#
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
import os
from threading import Thread
from typing import Tuple
from bottle import (
    route, request, response,
    static_file,
    FormsDict,
)
from psycopg import Cursor, connect
from psycopg.rows import TupleRow
from urllib.parse import parse_qs

from .QueryCache import QueryCache
from .route_decorators import cache, cursor
from .query_to_xml import get_categories, get_groups, get_products, get_tags
from .CachedLoadingPage import CachedLoadingPage
from .PageCache import PageCache
from . import trend as worker

host = f"host={os.getenv('HOST')}"
db = f"dbname={os.getenv('DB', 'grocery')}"
user = f"user={os.getenv('USER', 'das')}"
password = f"password={os.getenv('PASSWORD','')}"
if not password.split('=',1)[1]:
    password = ''
conn = connect(f"{host} {db} {user} {password}")

@route('/grocery/static/<filename:path>')
def send_static(filename):
    return static_file(filename, root='app/rest/static')

def new_thread(target, conn, path, forms):
    def cb(queue):
        return Thread(target=target, args=(
            queue, conn, path, forms
        )).start()
    return cb

PAGE_CACHE = PageCache(100)
QUERY_CACHE = QueryCache(None)

@route('/grocery/volume', method=['GET', 'POST'])
@cache(query_cache=QUERY_CACHE, page_cache=PAGE_CACHE)
def volume(key: Tuple[str, int], cache: PageCache):
    _, _, path, *_ = request.urlparts

    page = cache[key]
    if page is None:
        form = key_to_form(key)
        page = cache.add(key, CachedLoadingPage([], new_thread(worker.volume, conn, path, form)))
    
    for i in iter_page(page):
        yield i


@route('/grocery/trend', method=['GET', 'POST'])
@cache(query_cache=QUERY_CACHE, page_cache=PAGE_CACHE)
def trend(key: Tuple[str, int], cache: PageCache):
    _, _, path, *_ = request.urlparts

    page = cache[key]
    if page is None:
        form = key_to_form(key)
        page = cache.add(key, CachedLoadingPage([], new_thread(worker.trend, conn, path, form)))
    
    for i in iter_page(page):
        yield i


def query_to_form(query):
    form = FormsDict()
    for k, v in parse_qs(query).items():
        for item in v:
            form.append(k, item)
    return form


def key_to_form(key):
    query, _ = key
    return query_to_form(query.split('?', 1)[-1])


def iter_page(page):
    # copy first to avoid races
    resp = list(page.value)
    pos = len(resp)
    yield ''.join(resp)
    
    while not page.loaded:
        page.update()
        # all changes since last yield
        resp = list(page.value[pos:])
        pos = pos + len(resp)
        yield ''.join(resp)
    
    # possibly have not yielded the entire page
    if pos < len(page.value):
        yield ''.join(page.value[pos:])


@route('/grocery/groups', method=['GET', 'POST'])
@cache(query_cache=QUERY_CACHE, page_cache=PAGE_CACHE)
@cursor(connection=conn)
def groups(cur: Cursor[TupleRow], key: Tuple[str, int], cache: PageCache):
    form = key_to_form(key)
    response.content_type = 'application/xhtml+xml; charset=utf-8'
    return get_groups(cur, form)


@route('/grocery/categories', method=['GET', 'POST'])
@cache(query_cache=QUERY_CACHE, page_cache=PAGE_CACHE)
@cursor(connection=conn)
def categories(cur: Cursor[TupleRow], key: Tuple[str, int], cache: PageCache):
    form = key_to_form(key)
    response.content_type = 'application/xhtml+xml; charset=utf-8'
    return get_categories(cur, form)


@route('/grocery/products', method=['GET', 'POST'])
@cache(query_cache=QUERY_CACHE, page_cache=PAGE_CACHE)
@cursor(connection=conn)
def products(cur: Cursor[TupleRow], key: Tuple[str, int], cache: PageCache):
    form = key_to_form(key)
    response.content_type = 'application/xhtml+xml; charset=utf-8'
    return get_products(cur, form)


@route('/grocery/tags', method=['GET', 'POST'])
@cache(query_cache=QUERY_CACHE, page_cache=PAGE_CACHE)
@cursor(connection=conn)
def tags(cur: Cursor[TupleRow], key: Tuple[str, int], cache: PageCache):
    form = key_to_form(key)
    response.content_type = 'application/xhtml+xml; charset=utf-8'
    return get_tags(cur, form)
