from unittest.mock import MagicMock, patch

import pytest

from servala.core.models.service import (
    CloudProvider,
    ControlPlane,
    ControlPlaneCRD,
    ServiceInstance,
    ServiceOffering,
)


@pytest.mark.parametrize(
    "url,redirect",
    (
        ("/", "/accounts/login/?next=/"),
        ("/accounts/profile/", "/accounts/login/?next=/accounts/profile/"),
    ),
)
def test_root_view_redirects_valid_urls(client, url, redirect):
    response = client.get(url)
    assert response.status_code == 302
    assert response.url == redirect


def test_root_view_invalid_urls_404(client):
    response = client.get("/foobarbaz/")
    assert response.status_code == 404


@pytest.mark.django_db
def test_owner_can_access_dashboard(client, org_owner, organization):
    client.force_login(org_owner)
    response = client.get(organization.urls.base)
    assert organization.namespace == f"org-{organization.pk}"
    assert response.status_code == 200
    assert organization.name in response.content.decode()


@pytest.mark.django_db
def test_user_cannot_see_other_organization(client, org_owner, other_organization):
    client.force_login(org_owner)
    response = client.get(other_organization.urls.base)
    assert response.status_code == 403
    assert other_organization.name not in response.content.decode()


@pytest.mark.django_db
def test_organization_linked_in_sidebar(
    client, org_owner, organization, other_organization
):
    client.force_login(org_owner)
    response = client.get("/", follow=True)
    assert response.status_code == 200
    assert organization.name in response.content.decode()
    assert other_organization.name not in response.content.decode()


@pytest.mark.django_db
def test_service_detail_redirects_with_single_offering(
    client, org_owner, organization, service, service_offering
):
    client.force_login(org_owner)
    url = f"/org/{organization.slug}/services/{service.slug}/"
    response = client.get(url)

    assert response.status_code == 302
    expected_url = f"/org/{organization.slug}/services/{service.slug}/offering/{service_offering.pk}/"
    assert response.url == expected_url


@pytest.mark.django_db
def test_service_detail_shows_multiple_offerings(
    client, org_owner, organization, service, service_offering
):
    second_provider = CloudProvider.objects.create(
        name="AWS", description="Amazon Web Services"
    )
    second_offering = ServiceOffering.objects.create(
        service=service,
        provider=second_provider,
        description="Redis on AWS",
        osb_plan_id="test-plan-456",
    )

    client.force_login(org_owner)
    url = f"/org/{organization.slug}/services/{service.slug}/"
    response = client.get(url)

    assert response.status_code == 200
    content = response.content.decode()

    assert service_offering.provider.name in content
    assert second_offering.provider.name in content
    assert "Create Instance" in content


@pytest.mark.django_db
def test_service_detail_respects_cloud_provider_restrictions(
    client, org_owner, organization, service, service_offering
):
    second_provider = CloudProvider.objects.create(
        name="AWS", description="Amazon Web Services"
    )
    ServiceOffering.objects.create(
        service=service,
        provider=second_provider,
        description="Redis on AWS",
        osb_plan_id="test-plan-456",
    )
    organization.origin.limit_cloudproviders.add(service_offering.provider)

    client.force_login(org_owner)
    url = f"/org/{organization.slug}/services/{service.slug}/"
    response = client.get(url)

    assert response.status_code == 302
    expected_url = f"/org/{organization.slug}/services/{service.slug}/offering/{service_offering.pk}/"
    assert response.url == expected_url


@pytest.mark.django_db
def test_service_detail_no_redirect_with_restricted_multiple_offerings(
    client, org_owner, organization, service, service_offering
):
    second_provider = CloudProvider.objects.create(
        name="AWS", description="Amazon Web Services"
    )
    second_offering = ServiceOffering.objects.create(
        service=service,
        provider=second_provider,
        description="Redis on AWS",
        osb_plan_id="test-plan-456",
    )
    third_provider = CloudProvider.objects.create(
        name="Azure", description="Microsoft Azure"
    )
    third_offering = ServiceOffering.objects.create(
        service=service,
        provider=third_provider,
        description="Redis on Azure",
        osb_plan_id="test-plan-789",
    )
    organization.origin.limit_cloudproviders.add(
        service_offering.provider, second_provider
    )

    client.force_login(org_owner)
    url = f"/org/{organization.slug}/services/{service.slug}/"
    response = client.get(url)

    assert response.status_code == 200
    content = response.content.decode()
    assert service_offering.provider.name in content
    assert second_offering.provider.name in content
    assert third_offering.provider.name not in content


@pytest.mark.django_db
def test_service_instance_update_spec_pushes_display_name_annotation(
    organization, control_plane_crd, org_owner
):
    instance = ServiceInstance.objects.create(
        name="test-instance",
        display_name="Original Name",
        organization=organization,
        context=control_plane_crd,
    )
    mock_api = MagicMock()
    with (
        patch.object(ControlPlane, "custom_objects_api", mock_api),
        patch.object(ControlPlaneCRD, "kind_plural", "testkinds"),
    ):
        instance.display_name = "Updated Name"
        instance.update_spec(spec_data={}, updated_by=org_owner)
        mock_api.patch_namespaced_custom_object.assert_called_once()
        call_kwargs = mock_api.patch_namespaced_custom_object.call_args[1]
        assert "metadata" in call_kwargs["body"]
        annotations = call_kwargs["body"]["metadata"]["annotations"]
        assert annotations["servala.com/displayName"] == "Updated Name"

    instance.refresh_from_db()
    assert instance.display_name == "Updated Name"


@pytest.mark.django_db
def test_build_billing_annotations_includes_display_name():
    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=None,
        control_plane=MagicMock(
            storage_plan_odoo_product_id=None,
            storage_plan_odoo_unit_id=None,
        ),
        instance_name="test-instance",
        display_name="My Display Name",
        organization=None,
        service=None,
    )
    assert annotations["servala.com/displayName"] == "My Display Name"


@pytest.mark.django_db
def test_build_billing_annotations_omits_display_name_when_none():
    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=None,
        control_plane=MagicMock(
            storage_plan_odoo_product_id=None,
            storage_plan_odoo_unit_id=None,
        ),
        instance_name="test-instance",
        display_name=None,
        organization=None,
        service=None,
    )
    assert "servala.com/displayName" not in annotations
