from unittest.mock import Mock

import pytest

from servala.core.models import (
    ComputePlan,
    ComputePlanAssignment,
    InvoiceGroupingChoice,
    ServiceInstance,
)


@pytest.mark.django_db
def test_create_compute_plan():
    plan = ComputePlan.objects.create(
        name="Small",
        description="Small resource plan",
        memory_requests="512Mi",
        memory_limits="1Gi",
        cpu_requests="100m",
        cpu_limits="500m",
        is_active=True,
    )

    assert plan.name == "Small"
    assert plan.memory_requests == "512Mi"
    assert plan.memory_limits == "1Gi"
    assert plan.cpu_requests == "100m"
    assert plan.cpu_limits == "500m"
    assert plan.is_active is True


@pytest.mark.django_db
def test_compute_plan_str():
    plan = ComputePlan.objects.create(
        name="Medium",
        memory_requests="1Gi",
        memory_limits="2Gi",
        cpu_requests="500m",
        cpu_limits="1000m",
    )
    assert str(plan) == "Medium"


@pytest.mark.django_db
def test_get_resource_summary():
    plan = ComputePlan.objects.create(
        name="Large",
        memory_requests="2Gi",
        memory_limits="4Gi",
        cpu_requests="1000m",
        cpu_limits="2000m",
    )
    summary = plan.get_resource_summary()
    assert summary == "2000m vCPU, 4Gi RAM"


def test_apply_compute_plan_to_spec():
    compute_plan = Mock()
    compute_plan.memory_requests = "512Mi"
    compute_plan.memory_limits = "1Gi"
    compute_plan.cpu_requests = "100m"
    compute_plan.cpu_limits = "500m"

    compute_plan_assignment = Mock()
    compute_plan_assignment.compute_plan = compute_plan
    compute_plan_assignment.sla = "besteffort"

    spec_data = {"parameters": {}}

    result = ServiceInstance._apply_compute_plan_to_spec(
        spec_data, compute_plan_assignment
    )

    assert result["parameters"]["size"]["memory"] == "1Gi"
    assert result["parameters"]["size"]["cpu"] == "500m"
    assert result["parameters"]["size"]["requests"]["memory"] == "512Mi"
    assert result["parameters"]["size"]["requests"]["cpu"] == "100m"

    assert result["parameters"]["service"]["serviceLevel"] == "besteffort"


def test_apply_compute_plan_preserves_existing_spec():
    compute_plan = Mock()
    compute_plan.memory_requests = "512Mi"
    compute_plan.memory_limits = "1Gi"
    compute_plan.cpu_requests = "100m"
    compute_plan.cpu_limits = "500m"

    compute_plan_assignment = Mock()
    compute_plan_assignment.compute_plan = compute_plan
    compute_plan_assignment.sla = "guaranteed"

    spec_data = {
        "parameters": {
            "custom_field": "custom_value",
            "service": {"existingField": "value"},
        }
    }

    result = ServiceInstance._apply_compute_plan_to_spec(
        spec_data, compute_plan_assignment
    )

    assert result["parameters"]["custom_field"] == "custom_value"
    assert result["parameters"]["service"]["existingField"] == "value"

    assert result["parameters"]["size"]["memory"] == "1Gi"
    assert result["parameters"]["service"]["serviceLevel"] == "guaranteed"


def test_apply_compute_plan_with_none():
    spec_data = {"parameters": {}}
    result = ServiceInstance._apply_compute_plan_to_spec(spec_data, None)

    assert result == spec_data


def test_build_billing_annotations_complete():
    compute_plan_assignment = Mock()
    compute_plan_assignment.odoo_product_id = "test-product-123"
    compute_plan_assignment.odoo_unit_id = "test-unit-hour"

    control_plane = Mock()
    control_plane.storage_plan_odoo_product_id = "storage-product-id"
    control_plane.storage_plan_odoo_unit_id = "storage-unit-id"

    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=compute_plan_assignment,
        control_plane=control_plane,
    )

    assert annotations["servala.com/erp_product_id_resource"] == "test-product-123"
    assert annotations["servala.com/erp_unit_id_resource"] == "test-unit-hour"

    assert annotations["servala.com/erp_product_id_storage"] == "storage-product-id"
    assert annotations["servala.com/erp_unit_id_storage"] == "storage-unit-id"


def test_build_billing_annotations_no_compute_plan():
    control_plane = Mock()
    control_plane.storage_plan_odoo_product_id = "storage-product-id"
    control_plane.storage_plan_odoo_unit_id = "storage-unit-id"

    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=None,
        control_plane=control_plane,
    )

    assert "servala.com/erp_product_id_resource" not in annotations
    assert "servala.com/erp_unit_id_resource" not in annotations
    assert annotations["servala.com/erp_product_id_storage"] == "storage-product-id"
    assert annotations["servala.com/erp_unit_id_storage"] == "storage-unit-id"


def test_build_billing_annotations_no_storage_plan():
    compute_plan_assignment = Mock()
    compute_plan_assignment.odoo_product_id = "product-id"
    compute_plan_assignment.odoo_unit_id = "unit-id"

    control_plane = Mock()
    control_plane.storage_plan_odoo_product_id = None
    control_plane.storage_plan_odoo_unit_id = None

    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=compute_plan_assignment,
        control_plane=control_plane,
    )

    assert annotations["servala.com/erp_product_id_resource"] == "product-id"
    assert annotations["servala.com/erp_unit_id_resource"] == "unit-id"
    assert "servala.com/erp_product_id_storage" not in annotations
    assert "servala.com/erp_unit_id_storage" not in annotations


def test_build_billing_annotations_empty():
    control_plane = Mock()
    control_plane.storage_plan_odoo_product_id = None
    control_plane.storage_plan_odoo_unit_id = None

    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=None,
        control_plane=control_plane,
    )

    assert annotations == {}


@pytest.mark.django_db
@pytest.mark.parametrize(
    "unit_key,expected_display",
    [
        ("hour", "Hour"),
        ("day", "Day"),
        ("year", "Year"),
    ],
)
def test_billing_unit_choices(unit_key, expected_display):
    choices = dict(ComputePlanAssignment.BILLING_UNIT_CHOICES)
    assert unit_key in choices
    assert str(choices[unit_key]) == expected_display


@pytest.mark.django_db
def test_billing_unit_month():
    """Month has a special display format with additional info."""
    choices = dict(ComputePlanAssignment.BILLING_UNIT_CHOICES)
    assert "month" in choices
    assert "Month" in str(choices["month"])


@pytest.mark.parametrize(
    "invoice_grouping,org_name,osb_guid,expected_group_desc,expected_item_desc",
    [
        pytest.param(
            InvoiceGroupingChoice.BY_SERVICE,
            "ACME Corp",
            "some-guid",
            "Servala Service: Redis",
            "MyProdRedis on Exoscale Geneva (CH-GVA-2)",
            id="by_service",
        ),
        pytest.param(
            InvoiceGroupingChoice.BY_ORGANIZATION,
            "ACME",
            "01998651-dc86-7d43-9e49-cdb790fcc4f0",
            "Organization: ACME (01998651-dc86-7d43-9e49-cdb790fcc4f0)",
            "MyProdRedis on Geneva (CH-GVA-2) [Org: 01998651-dc86-7d43-9e49-cdb790fcc4f0]",
            id="by_organization_with_guid",
        ),
        pytest.param(
            InvoiceGroupingChoice.BY_ORGANIZATION,
            "ACME Corp",
            None,
            "Organization: ACME Corp",
            "MyProdRedis on Geneva (CH-GVA-2)",
            id="by_organization_without_guid",
        ),
        pytest.param(
            InvoiceGroupingChoice.BY_ORGANIZATION,
            "ACME Corp",
            "",
            "Organization: ACME Corp",
            "MyProdRedis on Geneva (CH-GVA-2)",
            id="by_organization_with_empty_guid",
        ),
    ],
)
def test_build_billing_annotations_item_description(
    invoice_grouping, org_name, osb_guid, expected_group_desc, expected_item_desc
):
    control_plane = Mock()
    control_plane.storage_plan_odoo_product_id = None
    control_plane.storage_plan_odoo_unit_id = None
    control_plane.cloud_provider = Mock()
    control_plane.cloud_provider.name = "Exoscale"
    control_plane.name = "Geneva (CH-GVA-2)"

    organization = Mock()
    organization.name = org_name
    organization.osb_guid = osb_guid
    organization.origin = Mock()
    organization.origin.invoice_grouping = invoice_grouping

    service = Mock()
    service.name = "Redis"

    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=None,
        control_plane=control_plane,
        instance_name="MyProdRedis",
        organization=organization,
        service=service,
    )

    assert annotations["servala.com/erp_item_group_description"] == expected_group_desc
    assert annotations["servala.com/erp_item_description"] == expected_item_desc


def test_build_billing_annotations_no_item_description_without_organization():
    control_plane = Mock()
    control_plane.storage_plan_odoo_product_id = "storage-product"
    control_plane.storage_plan_odoo_unit_id = "storage-unit"

    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=None,
        control_plane=control_plane,
        instance_name="MyProdRedis",
        organization=None,
        service=None,
    )

    assert "servala.com/erp_item_group_description" not in annotations
    assert "servala.com/erp_item_description" not in annotations
    assert annotations["servala.com/erp_product_id_storage"] == "storage-product"


@pytest.mark.django_db
def test_compute_plan_assignment_str(
    compute_plan_assignment,
):
    result = str(compute_plan_assignment)
    # Format: "{plan_name} ({sla_display}) → {control_plane_crd}"
    assert compute_plan_assignment.compute_plan.name in result
    assert "→" in result


@pytest.mark.django_db
def test_compute_plan_assignment_get_odoo_reporting_product_id(
    compute_plan_assignment,
):
    compute_plan_assignment.odoo_product_id = "test-product-xyz"
    compute_plan_assignment.save()

    result = compute_plan_assignment.get_odoo_reporting_product_id()

    assert result == "test-product-xyz"


def test_build_billing_annotations_combined():
    compute_plan_assignment = Mock()
    compute_plan_assignment.odoo_product_id = "compute-product"
    compute_plan_assignment.odoo_unit_id = "compute-unit"

    control_plane = Mock()
    control_plane.storage_plan_odoo_product_id = "storage-product"
    control_plane.storage_plan_odoo_unit_id = "storage-unit"
    control_plane.cloud_provider = Mock()
    control_plane.cloud_provider.name = "Exoscale"
    control_plane.name = "Geneva (CH-GVA-2)"

    organization = Mock()
    organization.name = "ACME"
    organization.osb_guid = "test-guid"
    organization.origin = Mock()
    organization.origin.invoice_grouping = InvoiceGroupingChoice.BY_SERVICE

    service = Mock()
    service.name = "PostgreSQL"

    annotations = ServiceInstance._build_billing_annotations(
        compute_plan_assignment=compute_plan_assignment,
        control_plane=control_plane,
        instance_name="ProdDB",
        organization=organization,
        service=service,
    )

    assert annotations["servala.com/erp_product_id_resource"] == "compute-product"
    assert annotations["servala.com/erp_unit_id_resource"] == "compute-unit"
    assert annotations["servala.com/erp_product_id_storage"] == "storage-product"
    assert annotations["servala.com/erp_unit_id_storage"] == "storage-unit"
    assert (
        annotations["servala.com/erp_item_group_description"]
        == "Servala Service: PostgreSQL"
    )
    assert (
        annotations["servala.com/erp_item_description"]
        == "ProdDB on Exoscale Geneva (CH-GVA-2)"
    )
