# SPDX-License-Identifier: GPL-2.0
from __future__ import annotations

import argparse
import copy
import os
import unittest

import yaml
from parameterized import parameterized

from podman_compose import PodmanCompose


class TestCanMergeBuild(unittest.TestCase):
    @parameterized.expand([
        ({}, {}, {}),
        ({}, {"test": "test"}, {"test": "test"}),
        ({"test": "test"}, {}, {"test": "test"}),
        ({"test": "test-1"}, {"test": "test-2"}, {"test": "test-2"}),
        ({}, {"build": "."}, {"build": {"context": "."}}),
        ({"build": "."}, {}, {"build": {"context": "."}}),
        ({"build": "./dir-1"}, {"build": "./dir-2"}, {"build": {"context": "./dir-2"}}),
        ({}, {"build": {"context": "./dir-1"}}, {"build": {"context": "./dir-1"}}),
        ({"build": {"context": "./dir-1"}}, {}, {"build": {"context": "./dir-1"}}),
        (
            {"build": {"context": "./dir-1"}},
            {"build": {"context": "./dir-2"}},
            {"build": {"context": "./dir-2"}},
        ),
        (
            {},
            {"build": {"dockerfile": "dockerfile-1"}},
            {"build": {"dockerfile": "dockerfile-1"}},
        ),
        (
            {"build": {"dockerfile": "dockerfile-1"}},
            {},
            {"build": {"dockerfile": "dockerfile-1"}},
        ),
        (
            {"build": {"dockerfile": "./dockerfile-1"}},
            {"build": {"dockerfile": "./dockerfile-2"}},
            {"build": {"dockerfile": "./dockerfile-2"}},
        ),
        (
            {"build": {"dockerfile": "./dockerfile-1"}},
            {"build": {"context": "./dir-2"}},
            {"build": {"dockerfile": "./dockerfile-1", "context": "./dir-2"}},
        ),
        (
            {"build": {"dockerfile": "./dockerfile-1", "context": "./dir-1"}},
            {"build": {"dockerfile": "./dockerfile-2", "context": "./dir-2"}},
            {"build": {"dockerfile": "./dockerfile-2", "context": "./dir-2"}},
        ),
        (
            {"build": {"dockerfile": "./dockerfile-1"}},
            {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
            {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
        ),
        (
            {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
            {"build": {"dockerfile": "./dockerfile-1"}},
            {"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1"]}},
        ),
        (
            {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
            {"build": {"dockerfile": "./dockerfile-1", "args": ["ENV2=2"]}},
            {"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1", "ENV2=2"]}},
        ),
        (
            {"build": {"dockerfile": "./dockerfile-1", "args": {"ENV1": "1"}}},
            {"build": {"dockerfile": "./dockerfile-2", "args": {"ENV2": "2"}}},
            {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1", "ENV2=2"]}},
        ),
        (
            {"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1"]}},
            {"build": {"dockerfile": "./dockerfile-2", "args": {"ENV2": "2"}}},
            {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1", "ENV2=2"]}},
        ),
    ])
    def test_parse_compose_file_when_multiple_composes(self, input, override, expected):
        compose_test_1 = {"services": {"test-service": input}}
        compose_test_2 = {"services": {"test-service": override}}
        dump_yaml(compose_test_1, "test-compose-1.yaml")
        dump_yaml(compose_test_2, "test-compose-2.yaml")

        podman_compose = PodmanCompose()
        set_args(podman_compose, ["test-compose-1.yaml", "test-compose-2.yaml"])

        podman_compose._parse_compose_file()  # pylint: disable=protected-access

        actual_compose = {}
        if podman_compose.services:
            actual_compose = podman_compose.original_configuration(
                podman_compose.services["test-service"]
            )
        self.assertEqual(actual_compose, expected)

    def test_parse_with_map_merge_into_none(self):
        compose_test_1 = {"volumes": {"vol_a": None}}
        compose_test_2 = {
            "volumes": {
                "vol_a": {
                    "driver_opts": {"type": "none", "device": "/dev/some", "o": "bind"},
                }
            }
        }
        expected = {"driver_opts": {"device": "/dev/some", "o": "bind", "type": "none"}}
        dump_yaml(compose_test_1, "test-compose-1.yaml")
        dump_yaml(compose_test_2, "test-compose-2.yaml")

        podman_compose = PodmanCompose()
        set_args(podman_compose, ["test-compose-1.yaml", "test-compose-2.yaml"])

        podman_compose._parse_compose_file()  # pylint: disable=protected-access

        actual_compose = podman_compose.vols["vol_a"]
        self.assertEqual(actual_compose, expected)

    # $$$ is a placeholder for either command or entrypoint
    @parameterized.expand([
        ({}, {"$$$": []}, {"$$$": []}),
        ({"$$$": []}, {}, {"$$$": []}),
        ({"$$$": []}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
        ({"$$$": "sh-2"}, {"$$$": []}, {"$$$": []}),
        ({}, {"$$$": "sh"}, {"$$$": ["sh"]}),
        ({"$$$": "sh"}, {}, {"$$$": ["sh"]}),
        ({"$$$": "sh-1"}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
        ({"$$$": ["sh-1"]}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
        ({"$$$": "sh-1"}, {"$$$": ["sh-2"]}, {"$$$": ["sh-2"]}),
        ({"$$$": "sh-1"}, {"$$$": ["sh-2", "sh-3"]}, {"$$$": ["sh-2", "sh-3"]}),
        ({"$$$": ["sh-1"]}, {"$$$": ["sh-2", "sh-3"]}, {"$$$": ["sh-2", "sh-3"]}),
        ({"$$$": ["sh-1", "sh-2"]}, {"$$$": ["sh-3", "sh-4"]}, {"$$$": ["sh-3", "sh-4"]}),
        ({}, {"$$$": ["sh-3", "sh      4"]}, {"$$$": ["sh-3", "sh      4"]}),
        ({"$$$": "sleep infinity"}, {"$$$": "sh"}, {"$$$": ["sh"]}),
        ({"$$$": "sh"}, {"$$$": "sleep infinity"}, {"$$$": ["sleep", "infinity"]}),
        (
            {},
            {"$$$": "bash -c 'sleep infinity'"},
            {"$$$": ["bash", "-c", "sleep infinity"]},
        ),
    ])
    def test_parse_compose_file_when_multiple_composes_keys_command_entrypoint(
        self, base_template, override_template, expected_template
    ):
        for key in ['command', 'entrypoint']:
            base, override, expected = template_to_expression(
                base_template, override_template, expected_template, key
            )
            compose_test_1 = {"services": {"test-service": base}}
            compose_test_2 = {"services": {"test-service": override}}
            dump_yaml(compose_test_1, "test-compose-1.yaml")
            dump_yaml(compose_test_2, "test-compose-2.yaml")

            podman_compose = PodmanCompose()
            set_args(podman_compose, ["test-compose-1.yaml", "test-compose-2.yaml"])

            podman_compose._parse_compose_file()  # pylint: disable=protected-access

            actual = {}
            if podman_compose.services:
                actual = podman_compose.original_configuration(
                    podman_compose.services["test-service"]
                )
            self.assertEqual(actual, expected)


def set_args(podman_compose: PodmanCompose, file_names: list[str]) -> None:
    podman_compose.global_args = argparse.Namespace()
    podman_compose.global_args.file = file_names
    podman_compose.global_args.project_name = None
    podman_compose.global_args.env_file = None
    podman_compose.global_args.profile = []
    podman_compose.global_args.in_pod = "1"
    podman_compose.global_args.pod_args = None
    podman_compose.global_args.no_normalize = True


def dump_yaml(compose: dict, name: str) -> None:
    with open(name, "w", encoding="utf-8") as outfile:
        yaml.safe_dump(compose, outfile, default_flow_style=False)


def template_to_expression(base, override, expected, key):
    base_copy = copy.deepcopy(base)
    override_copy = copy.deepcopy(override)
    expected_copy = copy.deepcopy(expected)

    expected_copy[key] = expected_copy.pop("$$$")
    if "$$$" in base:
        base_copy[key] = base_copy.pop("$$$")
    if "$$$" in override:
        override_copy[key] = override_copy.pop("$$$")
    return base_copy, override_copy, expected_copy


def test_clean_test_yamls() -> None:
    test_files = ["test-compose-1.yaml", "test-compose-2.yaml"]
    for file in test_files:
        if os.path.exists(file):
            os.remove(file)
