"""
    Product related endpoints. Included contexts for principles such as
    categories and image processing.
"""

from flask import (
    render_template, session, flash, request, redirect, Blueprint, url_for
)

from models.products.product import Product
from models.stats import Stats
from controllers.database.product import ProductController
from controllers.database.category import CategoryController
from controllers.database.stats import StatsController

from datetime import datetime
from utils.file_utils import save_image, remove_file
from utils.user_utils import is_role

import os

product_blueprint = Blueprint("products", __name__, url_prefix="/products")
seller_blueprint = Blueprint("seller", __name__, url_prefix="/products")

# List of available filters for the user to select
FILTERS = {
    # ANY INFOMRATION PUT INTO THE VALUES HERE WILL BE INTERPRETED AS SQL!!!
    'Relevance': 'ORDER BY quantityAvailable DESC',
    'Price: High -> Low': 'ORDER BY cost DESC',
    'Price: Low -> High': 'ORDER BY cost'
}


@product_blueprint.context_processor
def filter_list():
    """ Places a list of all the available filters in the
        products context
    """
    return dict(filters=FILTERS)


def get_filter():
    """ Return any filters that are currently active on the page """
    filter = request.args.get('filter')
    if filter is None:
        filter = 'Relevance'

    if filter in FILTERS:
        return FILTERS[filter]
    return FILTERS['Relevance']


def redirect_on_complete():
    """ Redirects to an appropriate location depending on the role """
    if is_role("Admin"):
        return redirect(url_for('main.admin.products'))
    return redirect(url_for('main.seller.display_own'))


@product_blueprint.context_processor
@seller_blueprint.context_processor
def category_list():
    """ Places a list of all categories in the products context """
    database = CategoryController()
    categories = database.read_all()
    return dict(categories=categories)


@seller_blueprint.before_request
def check_role():
    # User must be logged in as seller to view page
    if not is_role("Seller") and not is_role("Admin"):
        flash("You must be logged in as a seller to view this page!", "error")
        return redirect(url_for('main.index'))


@product_blueprint.route('/')
def index():
    """ The front product page """
    # Returning an empty category acts the same
    # as a generic home page
    return category("")


@product_blueprint.route('/<string:category>')
def category(category: str):
    """ Loads a given categories page """
    database = ProductController()

    # Check to see if there is a custome search term
    search_term = request.args.get("search", type=str)
    if search_term is not None:
        products = database.read_all(category, search_term, get_filter())
    else:
        products = database.read_all(category, "", get_filter())

    # No Products visible
    if products is None:
        flash(
            f"No Products available. Try expanding your search criteria.",
            "warning"
        )

    return render_template(
        'index.html',
        content="content.html",
        products=products,
        category=category
    )


@product_blueprint.route('/<int:id>')
def product(id: int):
    """ Loads a given product based on ID """
    db = ProductController()
    product = db.read_id(id)

    # Check that a valid product was returned
    if product is None:
        flash(f"No Product available with id {id}", "warning")
        return redirect(url_for('main.index'))

    # Record a view on the product
    db = StatsController()

    user_id = session.get('user_id')
    db.create(Stats(product.id, user_id))

    return render_template(
        'index.html',
        content='product.html',
        product=product
    )


@seller_blueprint.route('/add')
def display_add():
    """ Launches the page to add a new product to the site """
    return render_template('index.html', content='new_product.html')


@seller_blueprint.post('/add')
def add():
    """ Server site processing to handle a request to add a
        new product to the site
    """
    user_id = session.get('user_id')

    file = request.files.get('image')
    image_filename = save_image(file)

    product = Product(
        request.form.get('name'),
        image_filename if image_filename is not None else "",
        request.form.get('description'),
        request.form.get('cost'),
        request.form.get('category'),
        user_id,
        datetime.now(),
        request.form.get('quantity')
    )

    db = ProductController()
    db.create(product)

    return redirect(url_for('main.seller.display_own'))


@seller_blueprint.post('/update/<int:id>')
def update(id: int):
    """ Processes a request to update a product in place on the site """
    # Ensure that the product belongs to the current user
    user_id = session.get('user_id')

    db = ProductController()
    product = db.read_id(id)

    # Only admins and the owner can change the product
    if product.sellerID != user_id and not is_role("Admin"):
        flash("This product does not belong to you!", "error")
        return redirect(url_for('main.seller.display_own'))

    # Save new image file
    file = request.files.get('image')
    new_image = save_image(file)

    if new_image is not None:
        remove_file(os.path.join(os.environ.get('FILESTORE'), product.image))
        product.image = new_image

    # Update product details
    product.name = request.form.get('name')
    product.description = request.form.get('description')
    product.category = request.form.get('category')
    product.cost = request.form.get('cost')
    product.quantityAvailable = request.form.get('quantity')

    db.update(product)
    flash("Product successfully updated", 'notice')
    return redirect(url_for('main.products.product', id=product.id))


@seller_blueprint.post('/delete/<int:id>')
def delete(id: int):
    """ Processes a request to delete a product in place on the site """
    # Ensure that the product belongs to the current user
    user_id = session.get('user_id')

    db = ProductController()
    product = db.read_id(id)

    if product.sellerID != user_id and not is_role("Admin"):
        flash("This product does not belong to you!", "error")
        return redirect(url_for('main.seller.display_own'))

    db.delete(id)
    flash("Product Removed!", "success")
    return redirect_on_complete()


@seller_blueprint.route('/ownproducts')
def display_own():
    """ Display products owned by the currently logged in seller """
    user_id = session.get('user_id')

    db = ProductController()
    products = db.read_user(user_id)

    if products is None:
        flash("You don't currently have any products for sale.", "info")

    return render_template(
        'index.html',
        content='content.html',
        products=products
    )
