"""
Ported to Python 3.
"""
from __future__ import annotations

from urllib.parse import quote as urlquote

from twisted.web import http
from twisted.internet import defer
from twisted.python.filepath import FilePath
from twisted.web.resource import Resource
from twisted.web.template import (
    XMLFile,
    renderer,
    renderElement,
    tags,
)
from allmydata.immutable.upload import FileHandle
from allmydata.mutable.publish import MutableFileHandle
from allmydata.web.common import (
    get_keypair,
    get_arg,
    boolean_of_arg,
    convert_children_json,
    WebError,
    get_format,
    get_mutable_type,
    render_exception,
    url_for_string,
)
from allmydata.web import status

def PUTUnlinkedCHK(req, client):
    # "PUT /uri", to create an unlinked file.
    uploadable = FileHandle(req.content, client.convergence)
    d = client.upload(uploadable)
    d.addCallback(lambda results: results.get_uri())
    # that fires with the URI of the new file
    return d

def PUTUnlinkedSSK(req, client, version):
    # SDMF: files are small, and we can only upload data
    req.content.seek(0)
    data = MutableFileHandle(req.content)
    keypair = get_keypair(req)
    d = client.create_mutable_file(data, version=version, unique_keypair=keypair)
    d.addCallback(lambda n: n.get_uri())
    return d

def PUTUnlinkedCreateDirectory(req, client):
    # "PUT /uri?t=mkdir", to create an unlinked directory.
    file_format = get_format(req, None)
    if file_format == "CHK":
        raise WebError("format=CHK not accepted for PUT /uri?t=mkdir",
                       http.BAD_REQUEST)
    mt = None
    if file_format:
        mt = get_mutable_type(file_format)
    d = client.create_dirnode(version=mt)
    d.addCallback(lambda dirnode: dirnode.get_uri())
    # XXX add redirect_to_result
    return d


def POSTUnlinkedCHK(req, client):
    fileobj = req.fields["file"].file
    uploadable = FileHandle(fileobj, client.convergence)
    d = client.upload(uploadable)
    when_done = get_arg(req, "when_done", None)
    if when_done:
        # if when_done= is provided, return a redirect instead of our
        # usual upload-results page
        def _done(upload_results, redir_to):
            if b"%(uri)s" in redir_to:
                redir_to = redir_to.replace(b"%(uri)s", urlquote(upload_results.get_uri()).encode("utf-8"))
            return url_for_string(req, redir_to)
        d.addCallback(_done, when_done)
    else:
        # return the Upload Results page, which includes the URI
        d.addCallback(UploadResultsPage)
    return d


class UploadResultsPage(Resource, object):
    """'POST /uri', to create an unlinked file."""

    def __init__(self, upload_results):
        """
        :param IUploadResults upload_results: stats provider.
        """
        super(UploadResultsPage, self).__init__()
        self._upload_results = upload_results

    @render_exception
    def render_POST(self, req):
        elem = UploadResultsElement(self._upload_results)
        return renderElement(req, elem)


class UploadResultsElement(status.UploadResultsRendererMixin):

    loader = XMLFile(FilePath(__file__).sibling("upload-results.xhtml"))

    def __init__(self, upload_results):
        super(UploadResultsElement, self).__init__()
        self._upload_results = upload_results

    def upload_results(self):
        return defer.succeed(self._upload_results)

    @renderer
    def done(self, req, tag):
        d = self.upload_results()
        d.addCallback(lambda res: "done!")
        return d

    @renderer
    def uri(self, req, tag):
        d = self.upload_results()
        d.addCallback(lambda res: res.get_uri())
        return d

    @renderer
    def download_link(self, req, tag):
        d = self.upload_results()
        d.addCallback(lambda res:
                      tags.a("/uri/" + str(res.get_uri(), "utf-8"),
                             href="/uri/" + urlquote(str(res.get_uri(), "utf-8"))))
        return d


def POSTUnlinkedSSK(req, client, version):
    # "POST /uri", to create an unlinked file.
    # SDMF: files are small, and we can only upload data
    contents = req.fields["file"].file
    data = MutableFileHandle(contents)
    d = client.create_mutable_file(data, version=version)
    d.addCallback(lambda n: n.get_uri())
    return d

def POSTUnlinkedCreateDirectory(req, client):
    # "POST /uri?t=mkdir", to create an unlinked directory.
    ct = req.getHeader("content-type") or ""
    if not ct.startswith("multipart/form-data"):
        # guard against accidental attempts to call t=mkdir as if it were
        # t=mkdir-with-children, but make sure we tolerate the usual HTML
        # create-directory form (in which the t=mkdir and redirect_to_result=
        # and other arguments can be passed encoded as multipath/form-data,
        # in the request body).
        req.content.seek(0)
        kids_json = req.content.read()
        if kids_json:
            raise WebError("t=mkdir does not accept children=, "
                           "try t=mkdir-with-children instead",
                           http.BAD_REQUEST)
    file_format = get_format(req, None)
    if file_format == "CHK":
        raise WebError("format=CHK not currently accepted for POST /uri?t=mkdir",
                       http.BAD_REQUEST)
    mt = None
    if file_format:
        mt = get_mutable_type(file_format)
    d = client.create_dirnode(version=mt, unique_keypair=get_keypair(req))
    redirect = get_arg(req, "redirect_to_result", "false")
    if boolean_of_arg(redirect):
        def _then_redir(res):
            new_url = "uri/" + urlquote(res.get_uri())
            req.setResponseCode(http.SEE_OTHER) # 303
            req.setHeader('location', new_url)
            return ''
        d.addCallback(_then_redir)
    else:
        d.addCallback(lambda dirnode: dirnode.get_uri())
    return d

def POSTUnlinkedCreateDirectoryWithChildren(req, client):
    # "POST /uri?t=mkdir", to create an unlinked directory.
    req.content.seek(0)
    kids_json = req.content.read()
    kids = convert_children_json(client.nodemaker, kids_json)
    d = client.create_dirnode(initial_children=kids, unique_keypair=get_keypair(req))
    redirect = get_arg(req, "redirect_to_result", "false")
    if boolean_of_arg(redirect):
        def _then_redir(res):
            new_url = "uri/" + urlquote(res.get_uri())
            req.setResponseCode(http.SEE_OTHER) # 303
            req.setHeader('location', new_url)
            return ''
        d.addCallback(_then_redir)
    else:
        d.addCallback(lambda dirnode: dirnode.get_uri())
    return d

def POSTUnlinkedCreateImmutableDirectory(req, client):
    # "POST /uri?t=mkdir", to create an unlinked directory.
    req.content.seek(0)
    kids_json = req.content.read()
    kids = convert_children_json(client.nodemaker, kids_json)
    d = client.create_immutable_dirnode(kids)
    redirect = get_arg(req, "redirect_to_result", "false")
    if boolean_of_arg(redirect):
        def _then_redir(res):
            new_url = "uri/" + urlquote(res.get_uri())
            req.setResponseCode(http.SEE_OTHER) # 303
            req.setHeader('location', new_url)
            return ''
        d.addCallback(_then_redir)
    else:
        d.addCallback(lambda dirnode: dirnode.get_uri())
    return d
