#!/usr/bin/env python3

"""API endpoints supporting the 'ui'."""

from hashlib import sha3_256
import os
from typing import Optional

from flask import Blueprint, current_app, request
from tentacles.globals import ctx


BLUEPRINT = Blueprint("api", __name__, url_prefix="/api")


def requires_admin(f):
    def _helper(*args, **kwargs):
        if not ctx.is_admin:
            return {"error": "unauthorized"}, 401
        else:
            return f(*args, **kwargs)

    _helper.__name__ = f.__name__
    return _helper


def requires_auth(f):
    def _helper(*args, **kwargs):
        if not ctx.uid:
            return {"error": "unauthorized"}, 401
        else:
            return f(*args, **kwargs)

    _helper.__name__ = f.__name__
    return _helper


####################################################################################################
# Printers
#
# The trick here is handling multipart uploads.
@BLUEPRINT.route("/printers", methods=["POST"])
@requires_admin
def create_printer():
    pass


@BLUEPRINT.route("/printers", methods=["GET"])
@BLUEPRINT.route("/printers/", methods=["GET"])
@requires_auth
def list_printers():
    return {
        "printers": [
            {"id": p.id, "name": p.name, "url": p.url} for p in ctx.db.list_printers()
        ]
    }, 200


@BLUEPRINT.route("/printers", methods=["DELETE"])
@requires_admin
def delete_printer():
    pass


####################################################################################################
# Files
#
# The trick here is handling multipart uploads.
@BLUEPRINT.route("/files", methods=["POST"])
@BLUEPRINT.route("/files/<location>", methods=["POST"])
@requires_auth
def create_file(location: Optional[str] = None):
    # This is the important one, because it's the one that PrusaSlicer et all use to upload jobs.

    if "file" not in request.files:
        return {"status": "error", "error": "No file to upload"}, 400

    file = request.files["file"]
    # If the user does not select a file, the browser submits an
    # empty file without a filename.
    if not file.filename:
        return {"status": "error", "error": "No filename provided"}, 400

    elif not file.filename.endswith(".gcode"):
        return {"status": "error", "error": "Non-gcode file specified"}, 400

    else:
        digest = sha3_256()
        digest.update(
            f"$USER${ctx.uid}$".encode()
        )  # Salt filenames per-user to avoid collisions
        digest.update(file.filename.encode())
        sanitized_filename = digest.hexdigest() + ".gcode"
        sanitized_path = os.path.join(
            current_app.config["UPLOAD_FOLDER"], sanitized_filename
        )
        if os.path.exists(sanitized_path):
            return {"error": "file exists already"}, 409

        file.save(sanitized_path)
        row = ctx.db.create_file(
            uid=ctx.uid, filename=file.filename, path=sanitized_path
        )

        if request.form.get("print", "").lower() == "true":
            ctx.db.create_job(uid=ctx.uid, fid=row.id)

        return {"status": "ok"}, 202


@BLUEPRINT.route("/files", methods=["GET"])
@BLUEPRINT.route("/files/", methods=["GET"])
@requires_auth
def get_files():
    return {
        "files": [
            {
                "id": f.id,
                "filename": f.filename,
                "path": f.path,
                "owner": ctx.uid,
                "upload_date": f.upload_date,
            }
            for f in ctx.db.list_files(uid=ctx.uid)
        ]
    }, 200


@BLUEPRINT.route("/files", methods=["DELETE"])
@requires_auth
def delete_file():
    pass


####################################################################################################
# Jobs
@BLUEPRINT.route("/jobs", methods=["POST"])
@requires_auth
def create_job():
    pass


@BLUEPRINT.route("/jobs", methods=["GET"])
@requires_auth
def get_jobs():
    return {
        "jobs": [
            {
                "id": j.id,
                "file_id": j.file_id,
                "started_at": j.started_at,
                "finished_at": j.finished_at,
                "printer_id": j.printer_id,
            }
            for j in ctx.db.list_jobs(uid=ctx.uid)
        ]
    }, 200


@BLUEPRINT.route("/jobs", methods=["DELETE"])
@requires_auth
def delete_job():
    pass


####################################################################################################
# API tokens
@BLUEPRINT.route("/tokens", methods=["GET"])
@requires_auth
def get_tokens():
    pass


@BLUEPRINT.route("/version", methods=["GET"])
@requires_auth
def get_version():
    """OctoPrint compatability endpoint."""

    return {
        "api": "0.1",
        "server": "1.3.10",
        "text": "OctoPrint 1.3.10 (Tentacles)",
    }, 200
