"Script to generate the general views of Haldis"
import os
from datetime import datetime, timedelta

import yaml
from typing import Optional

from flask import Flask, render_template, make_response
from flask import request, jsonify
from flask import Blueprint, abort
from flask import current_app as app
from flask import send_from_directory, url_for
from flask_login import login_required

from utils import first
from hlds.definitions import location_definitions
from hlds.models import Location
from models import Order

# import views
from views.order import get_orders

import json
from flask import jsonify

general_bp = Blueprint("general_bp", __name__)


with open(os.path.join(os.path.dirname(__file__), "themes.yml"), "r") as _stream:
    _theme_data = yaml.safe_load(_stream)
    THEME_OPTIONS = _theme_data["options"]
    THEMES = _theme_data["themes"]


@general_bp.route("/")
def home() -> str:
    "Generate the home view"
    prev_day = datetime.now() - timedelta(days=1)
    recently_closed = get_orders(
        ((Order.stoptime > prev_day) & (Order.stoptime < datetime.now()))
    )
    return render_template(
        "home.html", orders=get_orders(), recently_closed=recently_closed
    )


def is_theme_active(theme, now):
    theme_type = theme["type"]

    if theme_type == "static":
        return True

    if theme_type == "seasonal":
        start_day, start_month = map(int, theme["start"].split("/"))
        start_datetime = datetime(year=now.year, day=start_day, month=start_month)

        end_day, end_month = map(int, theme["end"].split("/"))
        end_year = now.year + (1 if start_month > end_month else 0)
        end_datetime = datetime(year=end_year, day=end_day, month=end_month)

        return start_datetime <= now <= end_datetime

    raise Exception("Unknown theme type {}".format(theme_type))


def get_theme_css(theme, options):
    # Build filename
    # Each option's chosen value is appended, to get something like mytheme_darkmode_heavy.css

    filename = theme["file"]

    for option in theme.get("options", []):
        theme_name = theme["name"]
        assert option in THEME_OPTIONS, f"Theme `{theme_name}` uses undefined option `{option}`"

        chosen_value = options[option]
        possible_values = list(THEME_OPTIONS[option].keys())

        value = chosen_value if chosen_value in possible_values \
            else THEME_OPTIONS[option]["_default"]

        filename += "_" + value

    filename += ".css"

    theme_css_dir = "static/css/themes/"
    return os.path.join(app.root_path, theme_css_dir, filename)


def get_active_themes():
    now = datetime.now()
    return [theme for theme in THEMES if is_theme_active(theme, now)]


@general_bp.route("/theme.css")
def theme_css():
    "Send appropriate CSS for current theme"
    themes = get_active_themes()

    theme_name = request.cookies.get("theme", None)
    theme = first((t for t in themes if t["file"] == theme_name), default=themes[-1])

    options = {
        name: request.cookies.get("theme_" + name, None)
        for name in ["atmosphere", "performance"]
    }

    path = get_theme_css(theme, options)

    with open(path) as f:
        response = make_response(f.read())
    response.headers["Content-Type"] = "text/css"

    return response


@general_bp.route("/current_theme.js")
def current_theme_js():
    themes = get_active_themes()

    selected_theme_name = request.cookies.get("theme", None)
    matching_theme = first((t for t in themes if t["file"] == selected_theme_name))
    cur_theme = matching_theme or themes[-1]

    response = make_response(rf'''
var currentTheme        = {json.dumps(cur_theme['file'])};
var currentThemeOptions = {json.dumps(cur_theme.get('options', []))};
''')
    response.headers["Content-Type"] = "text/javascript"

    # Theme name that is not valid at this moment: delete cookie
    if matching_theme is None:
        response.delete_cookie("theme", path="/")

    return response


@general_bp.route("/map")
def map_view() -> str:
    "Generate the map view"
    return render_template("maps.html", locations=location_definitions)


@general_bp.route("/location")
def locations() -> str:
    "Generate the location view"
    return render_template("locations.html", locations=location_definitions)


@general_bp.route("/location/<location_id>")
def location(location_id) -> str:
    "Generate the location view given an id"
    loc = first(filter(lambda l: l.id == location_id, location_definitions))
    if loc is None:
        abort(404)
    return render_template("location.html", location=loc, title=loc.name)


@general_bp.route("/location/<location_id>/<dish_id>")
def location_dish(location_id, dish_id) -> str:
    loc: Optional[Location] = first(
        filter(lambda l: l.id == location_id, location_definitions)
    )
    if loc is None:
        abort(404)
    dish = loc.dish_by_id(dish_id)
    if dish is None:
        abort(404)
    return jsonify([
        {
            "type": c[0],
            "id": c[1].id,
            "name": c[1].name,
            "description": c[1].description,
            "options": [
                {
                    "id": o.id,
                    "name": o.name,
                    "description": o.description,
                    "price": o.price,
                    "tags": o.tags,
                }
                for o in c[1].options
            ],
        }
        for c in dish.choices
    ])


@general_bp.route("/about/")
def about() -> str:
    "Generate the about view"
    return render_template("about.html")


@general_bp.route("/profile/")
@login_required
def profile() -> str:
    "Generate the profile view"
    return render_template("profile.html", themes_list=get_active_themes())


@general_bp.route("/favicon.ico")
def favicon() -> str:
    "Generate the favicon"
    # pylint: disable=R1705
    if not get_orders((Order.stoptime > datetime.now())):
        return send_from_directory(
            os.path.join(app.root_path, "static"),
            "favicon.ico",
            mimetype="image/x-icon",
        )
    else:
        return send_from_directory(
            os.path.join(app.root_path, "static"),
            "favicon_orange.ico",
            mimetype="image/x-icon",
        )
