#
# 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 Union
from bottle import (
    route, request, response,
    redirect,
    template, static_file,
    FormsDict, HTTPError,
)
from psycopg import Cursor, connect
from psycopg.rows import TupleRow

from .validate import validate, validate_parameter, validate_url
from .hash_util import blake, bytes_to_base32, normalize_base32
from .route_decorators import normalize, poison, cursor
from .query_to_xml import get_categories, get_groups, get_products, get_tags
from .CachedLoadingPage import CachedLoadingPage
from .Cache import Cache
from . import BOOLEAN, PARAMS, 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')


@route('/grocery/trend', method=['GET', 'POST'])
@poison(cache=Cache(10))
@normalize
def trend(key: str, forms: FormsDict, cache: Cache):
    page = cache[key]
    if page:
        return page

    _, _, path, *_ = request.urlparts

    return cache.add(key, CachedLoadingPage(
        template("loading", progress=[]),
        lambda queue: Thread(target=worker.trend, args=(
            queue, conn, path, forms
        )).start()
    ))


@route('/grocery/groups', method=['GET', 'POST'])
@poison(cache=Cache(10))
@normalize
@cursor(connection=conn)
def groups(cur: Cursor[TupleRow], key: Union[int, str], forms: FormsDict, cache: Cache):
    response.content_type = 'application/xhtml+xml; charset=utf-8'
    return get_groups(cur, forms)


@route('/grocery/categories', method=['GET', 'POST'])
@poison(cache=Cache(10))
@normalize
@cursor(connection=conn)
def categories(cur: Cursor[TupleRow], key: Union[int, str], forms: FormsDict, cache: Cache):
    response.content_type = 'application/xhtml+xml; charset=utf-8'
    return get_categories(cur, forms)


@route('/grocery/products', method=['GET', 'POST'])
@poison(cache=Cache(10))
@normalize
@cursor(connection=conn)
def products(cur: Cursor[TupleRow], key: Union[int, str], forms: FormsDict, cache: Cache):
    response.content_type = 'application/xhtml+xml; charset=utf-8'
    return get_products(cur, forms)


@route('/grocery/tags', method=['GET', 'POST'])
@poison(cache=Cache(10))
@normalize
@cursor(connection=conn)
def tags(cur: Cursor[TupleRow], key: Union[int, str], forms: FormsDict, cache: Cache):
    response.content_type = 'application/xhtml+xml; charset=utf-8'
    return get_tags(cur, forms)


SCHEME = "https://"
HOST = ""
DOMAIN = "shandan.one"
PORT = ""
LOCATION = SCHEME + (f"{HOST}." if HOST else "") + DOMAIN + (f":{PORT}" if PORT else "")


def save(content: bytes, root='app/rest/static') -> str:
    _bytes = blake(content, person='clip'.encode('utf-8'))
    _b32 = bytes_to_base32(_bytes)
    directory = f'{root}/{_b32}'
    try:
        os.mkdir(directory, mode=0o700, dir_fd=None)
    except FileExistsError:
        pass
    fd = os.open(f'{directory}/{_b32}.file', os.O_WRONLY | os.O_TRUNC | os.O_CREAT, 0o600)
    with open(fd, "wb") as f:
        f.write(content)
    return _b32


@route('/clip', method=['GET', 'POST'])
def clip():
    if request.method == 'GET':
        _hash = request.params.hash
        if _hash:
            _hash = normalize_base32(_hash)
            content = validate(_hash).decode('utf-8')
        else:
            content = None
        link = f'{LOCATION}/clip/{_hash}' if content else f'{LOCATION}/clip'
        response.content_type = 'text/html; charset=utf-8'
        form = template(
            'clip-form',
            action='/clip',
            method='post',
            content=content,
            disabled=True if content else False
        )
        return template(
            'paste',
            form=form,
            link=link,
            disabled=True if content else False,
            download=f'/clip/{_hash}' if content else None
        )

    if request.method == 'POST':
        content = validate_parameter(request, 'paste')

        if request.params.copy != 'true':
            _b32 = save(content)
            return redirect(f'/clip?hash={_b32}')
        
        response.content_type = 'text/html; charset=utf-8'
        form = template(
            'clip-form',
            action='/clip',
            method='post',
            content=content,
            disabled=False
        )
        return template(
            'paste',
            form=form,
            link=f'{LOCATION}/clip',
            disabled=False,
            download=None
        )


@route('/clip/', method='GET')
def _clip(): return redirect(f'/clip')


@route('/clip/<filename:path>', method='GET')
def get_clip(filename):
    filename = filename and normalize_base32(filename)
    if not request.params.raw.lower() == 'true':
        # TODO: return a form with timeout to a GET instead ?
        return redirect(f'/clip?hash={filename}')

    ret = validate(filename)
    if isinstance(ret, HTTPError):
        return ret

    return static_file('/'.join([filename,]*2) + '.file', root='app/rest/static')


@route('/goto', method=['GET', 'POST'])
def goto():
    if request.method == 'GET':
        _hash = request.params.hash
        if _hash:
            _hash = normalize_base32(_hash)
            content = validate(_hash).decode('utf-8')
        else:
            content = None
        
        if content and request.params.go == 'true':
            target = validate_url(content)
            return redirect(target)
        
        link = f'{LOCATION}/goto/{_hash}' if content else f'{LOCATION}/goto'
        disabled = True if content else False
        response.content_type = 'text/html; charset=utf-8'
        form = template(
            'goto-form',
            action='/goto',
            method='post',
            content=content,
            disabled=disabled
        )
        return template('goto', form=form, link=link, disabled=disabled)

    if request.method == 'POST':
        content = validate_parameter(request, 'url')
        _b32 = save(content)

        # validate but save content unmodified
        _ = validate_url(content.decode('utf-8'))
        return redirect(f'/goto?hash={_b32}')


@route('/goto/', method='GET')
def _goto(): return redirect(f'/goto')


@route('/goto/<filename:path>', method='GET')
def redirect_goto(filename):
    filename = filename and normalize_base32(filename)
    return redirect(f'/goto?hash={filename}&go=true')
