import gzip
import io
import shutil

import pytest

import docker
from docker import auth, errors
from docker.api.build import process_dockerfile

from ..helpers import make_tree
from .api_test import BaseAPIClientTest, fake_request, url_prefix


class BuildTest(BaseAPIClientTest):
    def test_build_container(self):
        script = io.BytesIO(
            "\n".join(
                [
                    "FROM busybox",
                    "RUN mkdir -p /tmp/test",
                    "EXPOSE 8080",
                    "ADD https://dl.dropboxusercontent.com/u/20637798/silence.tar.gz"
                    " /tmp/silence.tar.gz",
                ]
            ).encode("ascii")
        )

        self.client.build(fileobj=script)

    def test_build_container_pull(self):
        script = io.BytesIO(
            "\n".join(
                [
                    "FROM busybox",
                    "RUN mkdir -p /tmp/test",
                    "EXPOSE 8080",
                    "ADD https://dl.dropboxusercontent.com/u/20637798/silence.tar.gz"
                    " /tmp/silence.tar.gz",
                ]
            ).encode("ascii")
        )

        self.client.build(fileobj=script, pull=True)

    def test_build_container_custom_context(self):
        script = io.BytesIO(
            "\n".join(
                [
                    "FROM busybox",
                    "RUN mkdir -p /tmp/test",
                    "EXPOSE 8080",
                    "ADD https://dl.dropboxusercontent.com/u/20637798/silence.tar.gz"
                    " /tmp/silence.tar.gz",
                ]
            ).encode("ascii")
        )
        context = docker.utils.mkbuildcontext(script)

        self.client.build(fileobj=context, custom_context=True)

    def test_build_container_custom_context_gzip(self):
        script = io.BytesIO(
            "\n".join(
                [
                    "FROM busybox",
                    "RUN mkdir -p /tmp/test",
                    "EXPOSE 8080",
                    "ADD https://dl.dropboxusercontent.com/u/20637798/silence.tar.gz"
                    " /tmp/silence.tar.gz",
                ]
            ).encode("ascii")
        )
        context = docker.utils.mkbuildcontext(script)
        gz_context = gzip.GzipFile(fileobj=context)

        self.client.build(fileobj=gz_context, custom_context=True, encoding="gzip")

    def test_build_remote_with_registry_auth(self):
        self.client._auth_configs = auth.AuthConfig(
            {
                "auths": {
                    "https://example.com": {
                        "user": "example",
                        "password": "example",
                        "email": "example@example.com",
                    }
                }
            }
        )

        expected_params = {
            "t": None,
            "q": False,
            "dockerfile": None,
            "rm": False,
            "nocache": False,
            "pull": False,
            "forcerm": False,
            "remote": "https://github.com/docker-library/mongo",
        }
        expected_headers = {
            "X-Registry-Config": auth.encode_header(self.client._auth_configs.auths)
        }

        self.client.build(path="https://github.com/docker-library/mongo")

        fake_request.assert_called_with(
            "POST",
            f"{url_prefix}build",
            stream=True,
            data=None,
            headers=expected_headers,
            params=expected_params,
            timeout=None,
        )

    def test_build_container_with_named_dockerfile(self):
        self.client.build(".", dockerfile="nameddockerfile")

    def test_build_with_invalid_tag(self):
        with pytest.raises(errors.DockerException):
            self.client.build(".", tag="https://example.com")

    def test_build_container_with_container_limits(self):
        self.client.build(
            ".",
            container_limits={
                "memory": 1024 * 1024,
                "cpusetcpus": 1,
                "cpushares": 1000,
                "memswap": 1024 * 1024 * 8,
            },
        )

    def test_build_container_invalid_container_limits(self):
        with pytest.raises(docker.errors.DockerException):
            self.client.build(".", container_limits={"foo": "bar"})

    def test_set_auth_headers_with_empty_dict_and_auth_configs(self):
        self.client._auth_configs = auth.AuthConfig(
            {
                "auths": {
                    "https://example.com": {
                        "user": "example",
                        "password": "example",
                        "email": "example@example.com",
                    }
                }
            }
        )

        headers = {}
        expected_headers = {
            "X-Registry-Config": auth.encode_header(self.client._auth_configs.auths)
        }

        self.client._set_auth_headers(headers)
        assert headers == expected_headers

    def test_set_auth_headers_with_dict_and_auth_configs(self):
        self.client._auth_configs = auth.AuthConfig(
            {
                "auths": {
                    "https://example.com": {
                        "user": "example",
                        "password": "example",
                        "email": "example@example.com",
                    }
                }
            }
        )

        headers = {"foo": "bar"}
        expected_headers = {
            "X-Registry-Config": auth.encode_header(self.client._auth_configs.auths),
            "foo": "bar",
        }

        self.client._set_auth_headers(headers)
        assert headers == expected_headers

    def test_set_auth_headers_with_dict_and_no_auth_configs(self):
        headers = {"foo": "bar"}
        expected_headers = {"foo": "bar"}

        self.client._set_auth_headers(headers)
        assert headers == expected_headers

    @pytest.mark.skipif(
        not docker.constants.IS_WINDOWS_PLATFORM, reason="Windows-specific syntax"
    )
    def test_process_dockerfile_win_longpath_prefix(self):
        dirs = [
            "foo",
            "foo/bar",
            "baz",
        ]

        files = [
            "Dockerfile",
            "foo/Dockerfile.foo",
            "foo/bar/Dockerfile.bar",
            "baz/Dockerfile.baz",
        ]

        base = make_tree(dirs, files)
        self.addCleanup(shutil.rmtree, base)

        def pre(path):
            return docker.constants.WINDOWS_LONGPATH_PREFIX + path

        assert process_dockerfile(None, pre(base)) == (None, None)
        assert process_dockerfile("Dockerfile", pre(base)) == ("Dockerfile", None)
        assert process_dockerfile("foo/Dockerfile.foo", pre(base)) == (
            "foo/Dockerfile.foo",
            None,
        )
        assert process_dockerfile("../Dockerfile", pre(f"{base}\\foo"))[1] is not None
        assert process_dockerfile("../baz/Dockerfile.baz", pre(f"{base}/baz")) == (
            "../baz/Dockerfile.baz",
            None,
        )

    def test_process_dockerfile(self):
        dirs = [
            "foo",
            "foo/bar",
            "baz",
        ]

        files = [
            "Dockerfile",
            "foo/Dockerfile.foo",
            "foo/bar/Dockerfile.bar",
            "baz/Dockerfile.baz",
        ]

        base = make_tree(dirs, files)
        self.addCleanup(shutil.rmtree, base)

        assert process_dockerfile(None, base) == (None, None)
        assert process_dockerfile("Dockerfile", base) == ("Dockerfile", None)
        assert process_dockerfile("foo/Dockerfile.foo", base) == (
            "foo/Dockerfile.foo",
            None,
        )
        assert process_dockerfile("../Dockerfile", f"{base}/foo")[1] is not None
        assert process_dockerfile("../baz/Dockerfile.baz", f"{base}/baz") == (
            "../baz/Dockerfile.baz",
            None,
        )
