import uuid

from flask import (
    flash, request, jsonify, url_for, redirect, Response, Blueprint)

from . import client
from .ui import render_ui
from .checks import require_oauth2
from .client import oauth2_get, oauth2_post
from .request_utils import (
    flash_error, flash_success, request_error, process_error)

resources = Blueprint("resource", __name__)

@resources.route("/", methods=["GET"])
@require_oauth2
def user_resources():
    """List the resources the user has access to."""
    def __success__(resources):
        return render_ui("oauth2/resources.html", resources=resources)

    return oauth2_get("auth/user/resources").either(
        request_error, __success__)

@resources.route("/create", methods=["GET", "POST"])
@require_oauth2
def create_resource():
    """Create a new resource."""
    def __render_template__(categories=[], error=None):
        return render_ui(
            "oauth2/create-resource.html",
            resource_categories=categories,
            resource_category_error=error,
            resource_name=request.args.get("resource_name"),
            resource_category=request.args.get("resource_category"))

    if request.method == "GET":
        return oauth2_get("auth/resource/categories").either(
            lambda error: __render_template__(error=process_error(
                error, "Could not retrieve resource categories")),
            lambda cats: __render_template__(categories=cats))

    def __perr__(error):
        err = process_error(error)
        flash(f"{err['error']}: {err['error_description']}", "alert-danger")
        return redirect(url_for(
            "oauth2.resource.create_resource",
            resource_name=request.form.get("resource_name"),
            resource_category=request.form.get("resource_category")))
    def __psuc__(succ):
        flash("Resource created successfully", "alert-success")
        return redirect(url_for("oauth2.resource.user_resources"))
    return oauth2_post(
        "auth/resource/create", data=request.form).either(
            __perr__, __psuc__)

def __compute_page__(submit, current_page):
    if submit == "next":
        return current_page + 1
    return (current_page - 1) or 1

@resources.route("/view/<uuid:resource_id>", methods=["GET"])
@require_oauth2
def view_resource(resource_id: uuid.UUID):
    """View the given resource."""
    page = __compute_page__(request.args.get("submit"),
                            int(request.args.get("page", "1"), base=10))
    count_per_page = int(request.args.get("count_per_page", "100"), base=10)
    def __users_success__(
            resource, unlinked_data, users_n_roles, this_user, group_roles,
            users):
        return render_ui(
            "oauth2/view-resource.html", resource=resource,
            unlinked_data=unlinked_data, users_n_roles=users_n_roles,
            this_user=this_user, group_roles=group_roles, users=users,
            page=page, count_per_page=count_per_page)

    def __group_roles_success__(
            resource, unlinked_data, users_n_roles, this_user, group_roles):
        return oauth2_get("auth/user/list").either(
            lambda err: render_ui(
                "oauth2/view-resource.html", resource=resource,
                unlinked_data=unlinked_data, users_n_roles=users_n_roles,
                this_user=this_user, group_roles=group_roles,
                users_error=process_error(err), count_per_page=count_per_page),
            lambda users: __users_success__(
                resource, unlinked_data, users_n_roles, this_user, group_roles,
                users))

    def __this_user_success__(resource, unlinked_data, users_n_roles, this_user):
        return oauth2_get("auth/group/roles").either(
            lambda err: render_ui(
                "oauth2/view-resources.html", resource=resource,
                unlinked_data=unlinked_data, users_n_roles=users_n_roles,
                this_user=this_user, group_roles_error=process_error(err)),
            lambda groles: __group_roles_success__(
                resource, unlinked_data, users_n_roles, this_user, groles))

    def __users_n_roles_success__(resource, unlinked_data, users_n_roles):
        return oauth2_get("auth/user/").either(
            lambda err: render_ui(
                "oauth2/view-resources.html",
                this_user_error=process_error(err)),
            lambda usr_dets: __this_user_success__(
                resource, unlinked_data, users_n_roles, usr_dets))

    def __unlinked_success__(resource, unlinked_data):
        return oauth2_get(f"auth/resource/{resource_id}/user/list").either(
            lambda err: render_ui(
                "oauth2/view-resource.html",
                resource=resource,
                unlinked_data=unlinked_data,
                users_n_roles_error=process_error(err),
                page=page,
                count_per_page=count_per_page),
            lambda users_n_roles: __users_n_roles_success__(
                resource, unlinked_data, users_n_roles))

    def __resource_success__(resource):
        dataset_type = resource["resource_category"]["resource_category_key"]
        return oauth2_get(f"auth/group/{dataset_type}/unlinked-data").either(
            lambda err: render_ui(
                "oauth2/view-resource.html", resource=resource,
                unlinked_error=process_error(err)),
            lambda unlinked: __unlinked_success__(resource, unlinked))

    def __fetch_resource_data__(resource):
        """Fetch the resource's data."""
        return client.get(
            f"auth/resource/view/{resource['resource_id']}/data?page={page}"
            f"&count_per_page={count_per_page}").either(
                lambda err: {
                    **resource, "resource_data_error": process_error(err)
                },
                lambda resdata: {**resource, "resource_data": resdata})

    return oauth2_get(f"auth/resource/view/{resource_id}").map(
        __fetch_resource_data__).either(
            lambda err: render_ui(
                "oauth2/view-resource.html",
                resource=None, resource_error=process_error(err)),
            __resource_success__)

@resources.route("/data/link", methods=["POST"])
@require_oauth2
def link_data_to_resource():
    """Link group data to a resource"""
    form = request.form
    try:
        assert "resource_id" in form, "Resource ID not provided."
        assert "data_link_id" in form, "Data Link ID not provided."
        assert "dataset_type" in form, "Dataset type not specified"
        assert form["dataset_type"].lower() in (
            "mrna", "genotype", "phenotype"), "Invalid dataset type provided."
        resource_id = form["resource_id"]

        def __error__(error):
            err = process_error(error)
            flash(f"{err['error']}: {err['error_description']}", "alert-danger")
            return redirect(url_for(
                "oauth2.resource.view_resource", resource_id=resource_id))

        def __success__(success):
            flash(f"Data linked to resource successfully", "alert-success")
            return redirect(url_for(
                "oauth2.resource.view_resource", resource_id=resource_id))
        return oauth2_post("auth/resource/data/link", data=dict(form)).either(
            __error__,
            __success__)
    except AssertionError as aserr:
        flash(aserr.args[0], "alert-danger")
        return redirect(url_for(
            "oauth2.resource.view_resource", resource_id=form["resource_id"]))

@resources.route("/data/unlink", methods=["POST"])
@require_oauth2
def unlink_data_from_resource():
    """Unlink group data from a resource"""
    form = request.form
    try:
        assert "resource_id" in form, "Resource ID not provided."
        assert "data_link_id" in form, "Data Link ID not provided."
        resource_id = form["resource_id"]

        def __error__(error):
            err = process_error(error)
            flash(f"{err['error']}: {err['error_description']}", "alert-danger")
            return redirect(url_for(
                "oauth2.resource.view_resource", resource_id=resource_id))

        def __success__(success):
            flash(f"Data unlinked from resource successfully", "alert-success")
            return redirect(url_for(
                "oauth2.resource.view_resource", resource_id=resource_id))
        return oauth2_post(
            "auth/resource/data/unlink", data=dict(form)).either(
            __error__, __success__)
    except AssertionError as aserr:
        flash(aserr.args[0], "alert-danger")
        return redirect(url_for(
            "oauth2.resource.view_resource", resource_id=form["resource_id"]))

@resources.route("<uuid:resource_id>/user/assign", methods=["POST"])
@require_oauth2
def assign_role(resource_id: uuid.UUID) -> Response:
    form = request.form
    group_role_id = form.get("group_role_id", "")
    user_email = form.get("user_email", "")
    try:
        assert bool(group_role_id), "The role must be provided."
        assert bool(user_email), "The user email must be provided."

        def __assign_error__(error):
            err = process_error(error)
            flash(f"{err['error']}: {err['error_description']}", "alert-danger")
            return redirect(url_for(
                "oauth2.resource.view_resource", resource_id=resource_id))

        def __assign_success__(success):
            flash(success["description"], "alert-success")
            return redirect(url_for(
                "oauth2.resource.view_resource", resource_id=resource_id))

        return oauth2_post(
            f"auth/resource/{resource_id}/user/assign",
            data={
                "group_role_id": group_role_id,
                "user_email": user_email
            }).either(__assign_error__, __assign_success__)
    except AssertionError as aserr:
        flash(aserr.args[0], "alert-danger")
        return redirect(url_for("oauth2.resources.view_resource", resource_id=resource_id))

@resources.route("<uuid:resource_id>/user/unassign", methods=["POST"])
@require_oauth2
def unassign_role(resource_id: uuid.UUID) -> Response:
    form = request.form
    group_role_id = form.get("group_role_id", "")
    user_id = form.get("user_id", "")
    try:
        assert bool(group_role_id), "The role must be provided."
        assert bool(user_id), "The user id must be provided."

        def __unassign_error__(error):
            err = process_error(error)
            flash(f"{err['error']}: {err['error_description']}", "alert-danger")
            return redirect(url_for(
                "oauth2.resource.view_resource", resource_id=resource_id))

        def __unassign_success__(success):
            flash(success["description"], "alert-success")
            return redirect(url_for(
                "oauth2.resource.view_resource", resource_id=resource_id))

        return oauth2_post(
            f"auth/resource/{resource_id}/user/unassign",
            data={
                "group_role_id": group_role_id,
                "user_id": user_id
            }).either(__unassign_error__, __unassign_success__)
    except AssertionError as aserr:
        flash(aserr.args[0], "alert-danger")
        return redirect(url_for("oauth2.resources.view_resource", resource_id=resource_id))

@resources.route("/toggle/<uuid:resource_id>", methods=["POST"])
@require_oauth2
def toggle_public(resource_id: uuid.UUID):
    """Toggle the given resource's public status."""
    def __handle_error__(err):
        flash_error(process_error(err))
        return redirect(url_for(
            "oauth2.resource.view_resource", resource_id=resource_id))

    def __handle_success__(success):
        flash_success(success)
        return redirect(url_for(
            "oauth2.resource.view_resource", resource_id=resource_id))

    return oauth2_post(
        f"auth/resource/{resource_id}/toggle-public", data={}).either(
            lambda err: __handle_error__(err),
            lambda suc: __handle_success__(suc))

@resources.route("/edit/<uuid:resource_id>", methods=["GET"])
@require_oauth2
def edit_resource(resource_id: uuid.UUID):
    """Edit the given resource."""
    return "WOULD Edit THE GIVEN RESOURCE'S DETAILS"

@resources.route("/delete/<uuid:resource_id>", methods=["GET"])
@require_oauth2
def delete_resource(resource_id: uuid.UUID):
    """Delete the given resource."""
    return "WOULD DELETE THE GIVEN RESOURCE"