from __future__ import annotations

import pytest
import requests
import responses

from pyartifactory import ArtifactoryRepository
from pyartifactory.exception import RepositoryAlreadyExistsError, RepositoryNotFoundError
from pyartifactory.models import (
    AuthModel,
    FederatedRepository,
    FederatedRepositoryResponse,
    LocalRepository,
    LocalRepositoryResponse,
    RemoteRepository,
    RemoteRepositoryResponse,
    SimpleRepository,
    VirtualRepository,
    VirtualRepositoryResponse,
)

URL = "http://localhost:8080/artifactory"
AUTH = ("user", "password_or_apiKey")

SIMPLE_REPOSITORY = SimpleRepository(key="test_repository", type="local", url="some-url", packageType="docker")
LOCAL_REPOSITORY = LocalRepository(key="test_local_repository")
LOCAL_REPOSITORY_RESPONSE = LocalRepositoryResponse(key="test_local_repository")
UPDATED_LOCAL_REPOSITORY = LocalRepository(key="test_local_repository", description="updated")
UPDATED_LOCAL_REPOSITORY_RESPONSE = LocalRepositoryResponse(key="test_local_repository", description="updated")
VIRTUAL_REPOSITORY = VirtualRepository(key="test_virtual_repository")
VIRTUAL_REPOSITORY_RESPONSE = VirtualRepositoryResponse(key="test_virtual_repository")
UPDATED_VIRTUAL_REPOSITORY = VirtualRepository(key="test_virtual_repository", description="updated")
UPDATED_VIRTUAL_REPOSITORY_RESPONSE = VirtualRepositoryResponse(key="test_virtual_repository", description="updated")
REMOTE_REPOSITORY = RemoteRepository(key="test_remote_repository", url="http://test-url.com")
REMOTE_REPOSITORY_RESPONSE = RemoteRepositoryResponse(key="test_remote_repository", url="http://test-url.com")
UPDATED_REMOTE_REPOSITORY = RemoteRepository(
    key="test_remote_repository",
    url="http://test-url.com",
    description="updated",
)
UPDATED_REMOTE_REPOSITORY_RESPONSE = RemoteRepositoryResponse(
    key="test_remote_repository",
    url="http://test-url.com",
    description="updated",
)

FEDERATED_REPOSITORY = FederatedRepository(
    key="test_federated_repository",
    url="http://test-url.com",
    members=[{"url": "member1.domain.com", "enabled": "true"}],
)
FEDERATED_REPOSITORY_RESPONSE = FederatedRepositoryResponse(
    key="test_federated_repository",
    url="http://test-url.com",
    members=[{"url": "member1.domain.com", "enabled": "true"}],
)
UPDATED_FEDERATED_REPOSITORY = FederatedRepository(
    key="test_federated_repository",
    url="http://test-url.com",
    description="updated",
    members=[{"url": "member1.domain.com", "enabled": "true"}],
)
UPDATED_FEDERATED_REPOSITORY_RESPONSE = FederatedRepositoryResponse(
    key="test_federated_repository",
    url="http://test-url.com",
    description="updated",
    members=[{"url": "member1.domain.com", "enabled": "true"}],
)


@responses.activate
def test_create_local_repository_fail_if_repository_already_exists(
    mocker,
):
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{LOCAL_REPOSITORY.key}",
        json=LOCAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryAlreadyExistsError):
        artifactory_repo.create_repo(LOCAL_REPOSITORY)

    artifactory_repo.get_repo.assert_called_once_with(LOCAL_REPOSITORY.key)


@responses.activate
def test_create_virtual_repository_fail_if_repository_already_exists(
    mocker,
):
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{VIRTUAL_REPOSITORY.key}",
        json=VIRTUAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryAlreadyExistsError):
        artifactory_repo.create_repo(VIRTUAL_REPOSITORY)

    artifactory_repo.get_repo.assert_called_once_with(VIRTUAL_REPOSITORY.key)


@responses.activate
def test_create_remote_repository_fail_if_repository_already_exists(
    mocker,
):
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{REMOTE_REPOSITORY.key}",
        json=REMOTE_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryAlreadyExistsError):
        artifactory_repo.create_repo(REMOTE_REPOSITORY)

    artifactory_repo.get_repo.assert_called_once_with(REMOTE_REPOSITORY.key)


@responses.activate
def test_create_federated_repository_fail_if_repository_already_exists(
    mocker,
):
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{FEDERATED_REPOSITORY.key}",
        json=FEDERATED_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryAlreadyExistsError):
        artifactory_repo.create_repo(FEDERATED_REPOSITORY)

    artifactory_repo.get_repo.assert_called_once_with(FEDERATED_REPOSITORY.key)


@responses.activate
def test_create_local_repository_success(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{LOCAL_REPOSITORY.key}", status=404)
    responses.add(
        responses.PUT,
        f"{URL}/api/repositories/{LOCAL_REPOSITORY.key}",
        json=LOCAL_REPOSITORY_RESPONSE.model_dump(),
        status=201,
    )
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{LOCAL_REPOSITORY.key}",
        json=LOCAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    local_repo = artifactory_repo.create_repo(LOCAL_REPOSITORY)

    assert isinstance(local_repo, LocalRepositoryResponse)
    assert local_repo == LOCAL_REPOSITORY_RESPONSE


@responses.activate
def test_create_virtual_repository_repo_success(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{VIRTUAL_REPOSITORY.key}", status=404)
    responses.add(
        responses.PUT,
        f"{URL}/api/repositories/{VIRTUAL_REPOSITORY.key}",
        json=VIRTUAL_REPOSITORY_RESPONSE.model_dump(),
        status=201,
    )
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{VIRTUAL_REPOSITORY.key}",
        json=VIRTUAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    virtual_repo = artifactory_repo.create_repo(VIRTUAL_REPOSITORY)

    assert virtual_repo == VIRTUAL_REPOSITORY_RESPONSE


@responses.activate
def test_create_remote_repository_success(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{REMOTE_REPOSITORY.key}", status=404)
    responses.add(
        responses.PUT,
        f"{URL}/api/repositories/{REMOTE_REPOSITORY.key}",
        json=REMOTE_REPOSITORY_RESPONSE.model_dump(),
        status=201,
    )
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{REMOTE_REPOSITORY.key}",
        json=REMOTE_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    remote_repo = artifactory_repo.create_repo(REMOTE_REPOSITORY)

    assert remote_repo == REMOTE_REPOSITORY_RESPONSE


@responses.activate
def test_create_federated_repository_success(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{FEDERATED_REPOSITORY.key}", status=404)
    responses.add(
        responses.PUT,
        f"{URL}/api/repositories/{FEDERATED_REPOSITORY.key}",
        json=FEDERATED_REPOSITORY_RESPONSE.model_dump(),
        status=201,
    )
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{FEDERATED_REPOSITORY.key}",
        json=FEDERATED_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    federated_repo = artifactory_repo.create_repo(FEDERATED_REPOSITORY)

    assert federated_repo == FEDERATED_REPOSITORY_RESPONSE


@responses.activate
def test_get_local_repository_error_not_found(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{LOCAL_REPOSITORY.key}", status=404)

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryNotFoundError):
        artifactory_repo.get_repo(LOCAL_REPOSITORY.key)


@responses.activate
def test_get_local_repository_success():
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{LOCAL_REPOSITORY.key}",
        json=LOCAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    local_repo = artifactory_repo.get_repo(LOCAL_REPOSITORY.key)

    assert local_repo == LOCAL_REPOSITORY_RESPONSE


@responses.activate
def test_get_virtual_repository_success(mocker):
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{VIRTUAL_REPOSITORY.key}",
        json=VIRTUAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    virtual_repo = artifactory_repo.get_repo(VIRTUAL_REPOSITORY.key)

    assert virtual_repo == VIRTUAL_REPOSITORY_RESPONSE


@responses.activate
def test_get_remote_repository_success():
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{REMOTE_REPOSITORY.key}",
        json=REMOTE_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    remote_repo = artifactory_repo.get_repo(REMOTE_REPOSITORY.key)

    assert remote_repo == REMOTE_REPOSITORY_RESPONSE


@responses.activate
def test_get_federated_repository_success():
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{FEDERATED_REPOSITORY.key}",
        json=FEDERATED_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    remote_repo = artifactory_repo.get_repo(FEDERATED_REPOSITORY.key)

    assert remote_repo == FEDERATED_REPOSITORY_RESPONSE


@responses.activate
def test_list_repositories_success(mocker):
    responses.add(
        responses.GET,
        f"{URL}/api/repositories",
        json=[SIMPLE_REPOSITORY.model_dump()],
        status=200,
    )

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "list")
    artifactory_repo.list()

    artifactory_repo.list.assert_called_once()


@responses.activate
def test_update_local_repository_fail_if_repo_not_found(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{LOCAL_REPOSITORY.key}", status=404)

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryNotFoundError):
        artifactory_repo.update_repo(LOCAL_REPOSITORY)
    artifactory_repo.get_repo.assert_called_once_with(LOCAL_REPOSITORY.key)


@responses.activate
def test_update_virtual_repository_fail_if_repo_not_found(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{VIRTUAL_REPOSITORY.key}", status=404)

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryNotFoundError):
        artifactory_repo.update_repo(VIRTUAL_REPOSITORY)

    artifactory_repo.get_repo.assert_called_once_with(VIRTUAL_REPOSITORY.key)


@responses.activate
def test_update_remote_repository_fail_if_repo_not_found(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{REMOTE_REPOSITORY.key}", status=404)

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryNotFoundError):
        artifactory_repo.update_repo(REMOTE_REPOSITORY)

    artifactory_repo.get_repo.assert_called_once_with(REMOTE_REPOSITORY.key)


@responses.activate
def test_update_federated_repository_fail_if_repo_not_found(mocker):
    responses.add(responses.GET, f"{URL}/api/repositories/{FEDERATED_REPOSITORY.key}", status=404)

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    with pytest.raises(RepositoryNotFoundError):
        artifactory_repo.update_repo(FEDERATED_REPOSITORY)

    artifactory_repo.get_repo.assert_called_once_with(FEDERATED_REPOSITORY.key)


@responses.activate
def test_update_local_repository_success(mocker):
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{UPDATED_LOCAL_REPOSITORY.key}",
        json=UPDATED_LOCAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    responses.add(
        responses.POST,
        f"{URL}/api/repositories/{UPDATED_LOCAL_REPOSITORY.key}",
        status=200,
    )
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{UPDATED_LOCAL_REPOSITORY.key}",
        json=UPDATED_LOCAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )
    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    mocker.spy(artifactory_repo, "get_repo")
    updated_repo = artifactory_repo.update_repo(UPDATED_LOCAL_REPOSITORY)

    assert isinstance(updated_repo, LocalRepositoryResponse)
    assert updated_repo == UPDATED_LOCAL_REPOSITORY_RESPONSE


@responses.activate
def test_update_virtual_repository_success():
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{UPDATED_VIRTUAL_REPOSITORY.key}",
        json=VIRTUAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    responses.add(
        responses.POST,
        f"{URL}/api/repositories/{UPDATED_VIRTUAL_REPOSITORY.key}",
        status=200,
    )
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{UPDATED_VIRTUAL_REPOSITORY.key}",
        json=UPDATED_VIRTUAL_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )
    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    updated_repo = artifactory_repo.update_repo(UPDATED_VIRTUAL_REPOSITORY)
    assert updated_repo == UPDATED_VIRTUAL_REPOSITORY_RESPONSE


@responses.activate
def test_update_remote_repository_success():
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{UPDATED_REMOTE_REPOSITORY.key}",
        json=UPDATED_REMOTE_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    responses.add(
        responses.POST,
        f"{URL}/api/repositories/{UPDATED_REMOTE_REPOSITORY.key}",
        status=200,
    )
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{UPDATED_REMOTE_REPOSITORY.key}",
        json=UPDATED_REMOTE_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )
    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    updated_repo = artifactory_repo.update_repo(UPDATED_REMOTE_REPOSITORY)
    assert updated_repo == UPDATED_REMOTE_REPOSITORY_RESPONSE


@responses.activate
def test_update_federated_repository_success():
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{UPDATED_FEDERATED_REPOSITORY.key}",
        json=UPDATED_FEDERATED_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )

    responses.add(
        responses.POST,
        f"{URL}/api/repositories/{UPDATED_FEDERATED_REPOSITORY.key}",
        status=200,
    )
    responses.add(
        responses.GET,
        f"{URL}/api/repositories/{UPDATED_FEDERATED_REPOSITORY.key}",
        json=UPDATED_FEDERATED_REPOSITORY_RESPONSE.model_dump(),
        status=200,
    )
    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    updated_repo = artifactory_repo.update_repo(UPDATED_FEDERATED_REPOSITORY)
    assert updated_repo == UPDATED_FEDERATED_REPOSITORY_RESPONSE


@responses.activate
def test_delete_repo_fail_if_repo_not_found():
    responses.add(responses.DELETE, f"{URL}/api/repositories/{REMOTE_REPOSITORY.key}", status=404)

    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))

    with pytest.raises(requests.exceptions.HTTPError):
        artifactory_repo.delete(REMOTE_REPOSITORY.key)


@responses.activate
def test_delete_repo_success():
    responses.add(responses.DELETE, f"{URL}/api/repositories/{VIRTUAL_REPOSITORY.key}", status=204)
    artifactory_repo = ArtifactoryRepository(AuthModel(url=URL, auth=AUTH))
    artifactory_repo.delete(VIRTUAL_REPOSITORY.key)
