""" Test serving files. """

import os
import re
import urllib.request

import pytest
import werkzeug.exceptions

from vasl_templates.webapp.files import FileServer
from vasl_templates.webapp.tests.utils import init_webapp, find_child, wait_for_clipboard

# ---------------------------------------------------------------------

def test_local_file_server( webapp ):
    """Test serving files from the local file system."""

    # initialize
    base_dir = os.path.normpath( os.path.join( os.path.split(__file__)[0], "fixtures/file-server" ) )
    file_server = FileServer( base_dir )

    # do the tests
    with webapp.test_request_context():

        assert _get_response_data( file_server.serve_file( "1.txt" ) ).strip() == b"file 1"
        with pytest.raises( werkzeug.exceptions.NotFound ):
            _get_response_data( file_server.serve_file( "/1.txt" ) )

        with pytest.raises( werkzeug.exceptions.NotFound ):
            _get_response_data( file_server.serve_file( "unknown.txt" ) )

        assert _get_response_data( file_server.serve_file( "subdir/2.txt" ) ).strip() == b"file 2"
        with pytest.raises( werkzeug.exceptions.NotFound ):
            _get_response_data( file_server.serve_file( "/subdir/2.txt" ) )

        # try to get a file outside the configured directory
        fname = "../new-default-scenario.json"
        assert os.path.isfile( os.path.join( base_dir, fname ) )
        with pytest.raises( werkzeug.exceptions.NotFound ):
            _get_response_data( file_server.serve_file( fname) )

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def test_remote_file_server( webapp ):
    """Test serving files from a remote file system."""

    # initialize
    base_url = "{}/static/images".format( _get_base_url( webapp ) )
    file_server = FileServer( base_url )
    base_dir = os.path.join( os.path.split(__file__)[0], "../static/images" )

    def do_test( fname ):
        """Get the specified user file from the remote server and check the response."""
        buf = _get_response_data( file_server.serve_file( fname ) )
        with open( os.path.join( base_dir, fname ), "rb" ) as fp:
            assert buf == fp.read()

    # do the tests
    with webapp.test_request_context():
        do_test( "hint.gif" )
        do_test( "flags/german.png" )

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def _get_base_url( webapp ):
    """Get the webapp base URL."""
    url = webapp.url_for( "get_user_file", path="unused" )
    mo = re.search( r"^http://.+?:\d+", url )
    return mo.group()

def _get_response_data( resp ):
    """Get the data from a Flask response."""
    resp.direct_passthrough = False
    return resp.get_data()

# ---------------------------------------------------------------------

def test_local_user_files( webapp, webdriver ):
    """Test serving user files from the local file system."""

    def do_test( enable_user_files ): #pylint: disable=missing-docstring

        # initialize
        webapp.control_tests.set_user_files_dir( "user-files/" if enable_user_files else None )
        init_webapp( webapp, webdriver )

        # try getting a user file
        try:
            url = webapp.url_for( "get_user_file", path="hello.txt" )
            with urllib.request.urlopen( url ) as resp:
                assert enable_user_files # nb: we should only get here if user files are enabled
                assert resp.code == 200
                assert resp.read().strip() == b"Yo, wassup!"
                assert resp.headers[ "Content-Type" ].startswith( "text/plain" )
        except urllib.error.HTTPError as ex:
            assert not enable_user_files # nb: we should only get here if user files are disabled
            assert ex.code == 404

        # try getting a non-existent file (nb: should always fail, whether user files are enabled/disabled)
        with pytest.raises( urllib.error.HTTPError ) as exc_info:
            url = webapp.url_for( "get_user_file", path="unknown" )
            with urllib.request.urlopen( url ):
                pass
        assert exc_info.value.code == 404

        # try getting a file in a sub-directory
        try:
            url = webapp.url_for( "get_user_file", path="subdir/placeholder.png" )
            with urllib.request.urlopen( url ) as resp:
                assert enable_user_files # nb: we should only get here if user files are enabled
                assert resp.code == 200
                assert resp.read().startswith( b"\x89PNG\r\n" )
                assert resp.headers[ "Content-Type" ] == "image/png"
        except urllib.error.HTTPError as ex:
            assert not enable_user_files # nb: we should only get here if user files are disabled
            assert ex.code == 404

        # try getting a file outside the configured directory (nb: should always fail)
        fname = os.path.join( os.path.split(__file__)[0], "fixtures/new-default-scenario.json" )
        assert os.path.isfile( fname )
        with pytest.raises( urllib.error.HTTPError ) as exc_info:
            url = webapp.url_for( "get_user_file", path="../new-default-scenario.json" )
            with urllib.request.urlopen( url ):
                pass
        assert exc_info.value.code == 404

        # try getting a file with special characters in its name
        try:
            url = webapp.url_for( "get_user_file", path="amp=& ; plus=+.txt" )
            with urllib.request.urlopen( url ) as resp:
                assert enable_user_files # nb: we should only get here if user files are enabled
                assert resp.code == 200
                assert resp.read().strip() == b"special chars"
                assert resp.headers[ "Content-Type" ].startswith( "text/plain" )
        except urllib.error.HTTPError as ex:
            assert not enable_user_files # nb: we should only get here if user files are disabled
            assert ex.code == 404

    # do the tests with user files enabled/disabled
    do_test( True )
    do_test( False )

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def test_remote_user_files( webapp, webdriver ):
    """Test serving user files from a remote server."""

    # initialize
    init_webapp( webapp, webdriver )
    remote_app_config = webapp.control_tests.get_app_config()

    def do_test( enable_user_files ): #pylint: disable=missing-docstring

        # initialize
        base_url = "{}/static/images".format( _get_base_url( webapp ) )
        if remote_app_config.get( "IS_CONTAINER" ):
            # FUDGE! We test getting a file from a remote server by requesting a file from the webapp (since we know
            # it will be available). However, if it's running in a container, the port it needs to use to talk
            # to itself is not necessarily the same as the port an outside client (e.g. us) uses to talk with it,
            # so we need to adjust the user files base URL to reflect that.
            remote_base_url = "http://localhost:{}".format( remote_app_config["FLASK_PORT_NO"] )
            base_url = re.sub( r"http://.+?:\d+", remote_base_url, base_url )
        webapp.control_tests.set_user_files_dir( base_url if enable_user_files else None )
        init_webapp( webapp, webdriver )

        # try getting a user file
        try:
            url = webapp.url_for( "get_user_file", path="menu.png" )
            with urllib.request.urlopen( url ) as resp:
                assert enable_user_files # nb: we should only get here if user files are enabled
                assert resp.code == 200
                assert resp.read().startswith( b"\x89PNG\r\n" )
                assert resp.headers[ "Content-Type" ] == "image/png"
        except urllib.error.HTTPError as ex:
            assert not enable_user_files # nb: we should only get here if user files are disabled
            assert ex.code == 404

    # do the tests with user files enabled/disabled
    do_test( True )
    do_test( False )

# ---------------------------------------------------------------------

def test_user_file_snippets( webapp, webdriver ):
    """Test user files in snippets."""

    def do_test( enable_user_files ): #pylint: disable=missing-docstring

        # initialize
        webapp.control_tests.set_user_files_dir( "user-files/" if enable_user_files else None )
        init_webapp( webapp, webdriver )

        # set the victory conditions
        elem = find_child( "textarea[name='VICTORY_CONDITIONS']" )
        elem.send_keys( "my image: {{USER_FILES}}/subdir/placeholder.png" )
        btn = find_child( "button.generate[data-id='victory_conditions']" )
        btn.click()
        def get_user_file_url( clipboard ): #pylint: disable=missing-docstring
            # nb: the test template wraps {{VICTORY_CONDITIONS}} in square brackets :-/
            mo = re.search( r"http://.+?/([^]]+)", clipboard )
            return "/" + mo.group(1)
        wait_for_clipboard( 2, "/user/subdir/placeholder.png", transform=get_user_file_url )

    # do the tests with user files enabled/disabled
    # NOTE: The user file URL will be inserted into the snippet even if user files are disabled,
    # but the URL will 404 when somebody tries to resolve it.
    do_test( True )
    do_test( False )
