import re
import json
import os
from unittest.mock import (
    Mock,
    AsyncMock,
    patch,
    create_autospec,
    call,
    mock_open,
)
from pytest import mark, fixture
from aiodocker.containers import DockerContainer
import proximl.connections as specimen
from proximl.jobs import Job
from proximl.datasets import Dataset


pytestmark = [mark.sdk, mark.unit, mark.connections]


@fixture
def connections(mock_proximl):
    yield specimen.Connections(mock_proximl)


@fixture
def dataset_con(mock_proximl):
    DatasetMock = create_autospec(Dataset)
    yield specimen.Connection(
        mock_proximl,
        entity_type="dataset",
        id="data-id-1",
        entity=DatasetMock(
            mock_proximl,
            dataset_uuid="data-id-1",
            project_uuid="proj-id-1",
            name="first one",
            status="new",
        ),
    )


@fixture
def job_con(mock_proximl):
    JobMock = create_autospec(Job)
    yield specimen.Connection(
        mock_proximl,
        entity_type="job",
        id="job-id-1",
        entity=JobMock(
            mock_proximl,
            **{
                "customer_uuid": "cus-id-1",
                "project_uuid": "proj-id-1",
                "job_uuid": "job-id-1",
                "name": "test notebook",
                "type": "interactive",
                "status": "new",
            },
        ),
    )


class ConnectionsTests:
    @mark.asyncio
    async def test_connections_list(
        self,
        connections,
    ):
        with patch("proximl.connections.os") as mock_os:
            mock_os.listdir = Mock(
                return_value=["job_job-id-1", "dataset_data-id-1", "baddir"]
            )
            with patch(
                "proximl.connections.Connection", autospec=True
            ) as mock_connection:
                connection = mock_connection.return_value
                connections = await connections.list()
                connection.check.assert_called
                assert len(connections) == 2

    @mark.asyncio
    async def test_connections_cleanup(
        self,
        connections,
    ):
        with patch("proximl.connections.os") as mock_os:
            dir_list = ["job_job-id-1", "dataset_data-id-1"]
            mock_os.listdir = Mock(return_value=dir_list)

            with patch(
                "proximl.connections._cleanup_containers"
            ) as mock_cleanup:
                await connections.cleanup_connections()
                assert mock_cleanup.mock_calls == [
                    call(
                        "proj-id-1",
                        os.path.expanduser("~/.proximl/connections/proj-id-1"),
                        dir_list,
                        "vpn",
                    ),
                    call(
                        "proj-id-1",
                        os.path.expanduser("~/.proximl/connections/proj-id-1"),
                        dir_list,
                        "storage",
                    ),
                ]

    @mark.asyncio
    async def test_connections_cleanup_containers(
        self,
    ):
        with patch("proximl.connections.open", mock_open(read_data="keep-me")):

            with patch(
                "proximl.connections.aiodocker.Docker", autospec=True
            ) as mock_docker:
                containers = [
                    create_autospec(DockerContainer),
                    create_autospec(DockerContainer),
                ]
                containers[0].id = "keep-me"
                containers[1].id = "delete-me"
                docker = mock_docker.return_value
                docker.containers = AsyncMock()
                docker.containers.list = AsyncMock(return_value=containers)
                await specimen._cleanup_containers(
                    "proj-id-1",
                    os.path.expanduser("~/.proximl/connections/proj-id-1"),
                    ["dir_1"],
                    "job",
                )
                docker.containers.list.assert_called_once_with(
                    all=True,
                    filters=json.dumps(
                        dict(
                            label=[
                                "service=proximl",
                                "type=job",
                                "project=proj-id-1",
                            ]
                        )
                    ),
                )
                containers[0].delete.assert_not_called
                containers[1].delete.assert_called_once_with(force=True)


class ConnectionTests:
    def test_connection_properties(self, dataset_con):
        assert isinstance(dataset_con.id, str)
        assert isinstance(dataset_con.status, str)
        assert isinstance(dataset_con.type, str)
        assert dataset_con.type == "dataset"

    def test_connection_str(self, dataset_con):
        string = str(dataset_con)
        regex = (
            r"^Connection for "
            + dataset_con.type
            + " - "
            + dataset_con.id
            + ": "
            + dataset_con.status
            + r"$"
        )
        assert isinstance(string, str)
        assert re.match(regex, string)

    def test_connection_repr(self, dataset_con):
        string = repr(dataset_con)
        regex = (
            r"^Connection\( proximl , "
            + dataset_con.id
            + ", "
            + dataset_con.type
            + r"\)$"
        )
        assert isinstance(string, str)
        assert re.match(regex, string)
