// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Diagnostics.CodeAnalysis;
using Bicep.Core.Diagnostics;
using Bicep.Core.Resources;
using Bicep.Core.TypeSystem;
using Bicep.Core.TypeSystem.Types;
using Bicep.Core.UnitTests;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.UnitTests.Utils;
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Bicep.Core.IntegrationTests
{
    [TestClass]
    public class ScopeTests
    {
        private const string ExpectedTenantSchema = "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#";
        private const string ExpectedMgSchema = "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#";
        private const string ExpectedSubSchema = "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#";
        private const string ExpectedRgSchema = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#";

        [NotNull]
        public TestContext? TestContext { get; set; }

        [DataRow("tenant", "tenant()", "tenant", ExpectedTenantSchema, "[reference(tenantResourceId('Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[tenantResourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("tenant", "managementGroup('abc')", "managementGroup", ExpectedTenantSchema, "[reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', 'abc'), 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', 'abc'), 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("tenant", "subscription('abc')", "subscription", ExpectedTenantSchema, "[reference(subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("tenant", "resourceGroup('abc', 'def')", "resourceGroup", ExpectedTenantSchema, "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("managementGroup", "managementGroup()", "managementGroup", ExpectedMgSchema, "[reference(extensionResourceId(managementGroup().id, 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[extensionResourceId(managementGroup().id, 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("managementGroup", "subscription('abc')", "subscription", ExpectedMgSchema, "[reference(subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("managementGroup", "resourceGroup('abc', 'def')", "resourceGroup", ExpectedMgSchema, "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("subscription", "subscription()", "subscription", ExpectedSubSchema, "[reference(subscriptionResourceId('Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[subscriptionResourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("subscription", "subscription('abc')", "subscription", ExpectedSubSchema, "[reference(subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("subscription", "resourceGroup('abc')", "resourceGroup", ExpectedSubSchema, "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, 'abc'), 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, 'abc'), 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("subscription", "tenant()", "tenant", ExpectedSubSchema, "[reference(tenantResourceId('Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[tenantResourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("resourceGroup", "subscription()", "subscription", ExpectedRgSchema, "[reference(subscriptionResourceId('Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[subscriptionResourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("resourceGroup", "subscription('abc')", "subscription", ExpectedRgSchema, "[reference(subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[subscriptionResourceId('abc', 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("resourceGroup", "resourceGroup()", "resourceGroup", ExpectedRgSchema, "[reference(resourceId('Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[resourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("resourceGroup", "resourceGroup('abc')", "resourceGroup", ExpectedRgSchema, "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, 'abc'), 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, 'abc'), 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("resourceGroup", "resourceGroup('abc', 'def')", "resourceGroup", ExpectedRgSchema, "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', 'abc', 'def'), 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("resourceGroup", "tenant()", "tenant", ExpectedRgSchema, "[reference(tenantResourceId('Microsoft.Resources/deployments', 'myMod'), '2025-04-01').outputs.hello.value]", "[tenantResourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataTestMethod]
        public void Emitter_should_generate_correct_module_output_scope_strings(string targetScope, string moduleScope, string moduleTargetScope, string expectedSchema, string expectedOutput, string expectedResourceDependsOn)
        {
            var (template, diags, _) = CompilationHelper.Compile(
                ("main.bicep", @"
targetScope = '$targetScope'

module myMod './module.bicep' = {
  name: 'myMod'
  scope: $moduleScope
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceB'
  dependsOn: [
    myMod
  ]
}

output hello string = myMod.outputs.hello
".Replace("$targetScope", targetScope).Replace("$moduleScope", moduleScope)),
                ("module.bicep", @"
targetScope = '$moduleTargetScope'

output hello string = 'hello!'
".Replace("$moduleTargetScope", moduleTargetScope)));

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.['$schema']", expectedSchema);
                template.Should().HaveValueAtPath("$.outputs.hello.value", expectedOutput);
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].dependsOn[0]", expectedResourceDependsOn);
            }
        }

        [DataRow("tenant", "[tenantResourceId('My.Rp/myResource', 'resourceA')]", "[tenantResourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("managementGroup", "[extensionResourceId(managementGroup().id, 'My.Rp/myResource', 'resourceA')]", "[extensionResourceId(managementGroup().id, 'Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("subscription", "[subscriptionResourceId('My.Rp/myResource', 'resourceA')]", "[subscriptionResourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataRow("resourceGroup", "[resourceId('My.Rp/myResource', 'resourceA')]", "[resourceId('Microsoft.Resources/deployments', 'myMod')]")]
        [DataTestMethod]
        public void Emitter_should_generate_correct_dependsOn_resourceIds(string targetScope, string expectedModuleDependsOn, string expectedResourceDependsOn)
        {
            var (template, diags, _) = CompilationHelper.Compile(
                ("main.bicep", @"
targetScope = '$targetScope'

resource resourceA 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceA'
}

module myMod './module.bicep' = {
  name: 'myMod'
  params: {
    dependency: resourceA.id
  }
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceB'
  dependsOn: [
    myMod
  ]
}
".Replace("$targetScope", targetScope)),
                ("module.bicep", @"
targetScope = '$targetScope'

param dependency string
".Replace("$targetScope", targetScope))
            );

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].dependsOn[0]", expectedResourceDependsOn);
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'myMod')].dependsOn[0]", expectedModuleDependsOn);
            }
        }

        [TestMethod]
        public void Emitter_should_generate_correct_extension_scope_property_and_correct_dependsOn()
        {
            var (template, _, _) = CompilationHelper.Compile(@"
resource resourceA 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceA'
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  scope: resourceA
  name: 'resourceB'
}

resource resourceC 'My.Rp/myResource@2020-01-01' = {
  scope: resourceB
  name: 'resourceC'
}");

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].scope", "[resourceId('My.Rp/myResource', 'resourceA')]");
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].dependsOn[0]", "[resourceId('My.Rp/myResource', 'resourceA')]");

                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceC')].scope", "[extensionResourceId(resourceId('My.Rp/myResource', 'resourceA'), 'My.Rp/myResource', 'resourceB')]");
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceC')].dependsOn[0]", "[extensionResourceId(resourceId('My.Rp/myResource', 'resourceA'), 'My.Rp/myResource', 'resourceB')]");
            }
        }

        //https://github.com/Azure/bicep/issues/18097
        [TestMethod]
        public void Emitter_should_generate_fullyQualifiedResourceId_in_extension_scope_property_and_correct_dependsOn()
        {
            //tenant scope resource and its extension resource can be deployed from anywhere
            var (template, _, _) = CompilationHelper.Compile(@"
resource resourceA 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceA'
  scope: tenant()
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  scope: resourceA
  name: 'resourceB'
}");

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceA')].scope", "/");
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].scope", "[tenantResourceId('My.Rp/myResource', 'resourceA')]");
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].dependsOn[0]", "[tenantResourceId('My.Rp/myResource', 'resourceA')]");
            }

            // existing tenant resource and its extension resource can be deployed from anywhere
            var (template1, _, _) = CompilationHelper.Compile(@"
resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
  scope: tenant()
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  scope: resourceA
  name: 'resourceB'
}");

            using (new AssertionScope())
            {
                template1.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].scope", "[tenantResourceId('My.Rp/myResource', 'resourceA')]");
            }


        }

        [DataRow("tenant", "[tenantResourceId('My.Rp/myResource', 'resourceA')]", "[reference(tenantResourceId('My.Rp/myResource', 'resourceA'), '2020-01-01').myProp]")]
        [DataRow("managementGroup", "[extensionResourceId(managementGroup().id, 'My.Rp/myResource', 'resourceA')]", "[reference(extensionResourceId(managementGroup().id, 'My.Rp/myResource', 'resourceA'), '2020-01-01').myProp]")]
        [DataRow("subscription", "[subscriptionResourceId('My.Rp/myResource', 'resourceA')]", "[reference(subscriptionResourceId('My.Rp/myResource', 'resourceA'), '2020-01-01').myProp]")]
        [DataRow("resourceGroup", "[resourceId('My.Rp/myResource', 'resourceA')]", "[reference(resourceId('My.Rp/myResource', 'resourceA'), '2020-01-01').myProp]")]
        [DataTestMethod]
        public void Emitter_should_generate_correct_references_for_existing_resources(string targetScope, string expectedScopeExpression, string expectedReferenceExpression)
        {
            var (template, diags, _) = CompilationHelper.Compile(@"
targetScope = '$targetScope'

resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  scope: resourceA
  name: 'resourceB'
}

output resourceARef string = resourceA.properties.myProp
".Replace("$targetScope", targetScope));

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].scope", expectedScopeExpression);
                template.Should().NotHaveValueAtPath("$.resources[?(@.name == 'resourceB')].dependsOn");

                template.Should().HaveValueAtPath("$.outputs['resourceARef'].value", expectedReferenceExpression);
            }
        }

        [TestMethod]
        public void Existing_resources_can_be_referenced_at_other_scopes()
        {
            var typeReference = ResourceTypeReference.Parse("My.Rp/myResource@2020-01-01");
            var typeLoader = TestTypeHelper.CreateResourceTypeLoaderWithTypes(new[] {
                new ResourceTypeComponents(typeReference, ResourceScope.ResourceGroup, ResourceScope.None, ResourceFlags.None, new ObjectType(typeReference.FormatName(), TypeSymbolValidationFlags.Default, new [] {
                    new NamedTypeProperty("name", LanguageConstants.String, TypePropertyFlags.DeployTimeConstant, "name property"),
                    new NamedTypeProperty("kind", LanguageConstants.String, TypePropertyFlags.ReadOnly, "kind property"),
                }, null))
            });

            // explicitly pass a valid scope
            var (template, _, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
  scope: resourceGroup()
}

output resourceARef string = resourceA.kind
"));

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.outputs['resourceARef'].value", "[reference(resourceId('My.Rp/myResource', 'resourceA'), '2020-01-01', 'full').kind]");
            }

            // use a valid targetScope without setting the scope property
            (template, _, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
targetScope = 'resourceGroup'

resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
}

output resourceARef string = resourceA.kind
"));

            using (new AssertionScope())
            {
                template.Should().HaveValueAtPath("$.outputs['resourceARef'].value", "[reference(resourceId('My.Rp/myResource', 'resourceA'), '2020-01-01', 'full').kind]");
            }
        }

        [TestMethod]
        public void Errors_are_raised_for_existing_resources_at_invalid_scopes()
        {
            var typeReference = ResourceTypeReference.Parse("My.Rp/myResource@2020-01-01");
            var typeLoader = TestTypeHelper.CreateResourceTypeLoaderWithTypes(new[] {
                new ResourceTypeComponents(typeReference, ResourceScope.ResourceGroup, ResourceScope.None, ResourceFlags.None, new ObjectType(typeReference.FormatName(), TypeSymbolValidationFlags.Default, new [] {
                    new NamedTypeProperty("name", LanguageConstants.String, TypePropertyFlags.DeployTimeConstant, "name property"),
                }, null))
            });

            // explicitly pass an invalid scope
            var (_, diags, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
  scope: subscription()
}
"));

            diags.Should().HaveDiagnostics(new[] {
                ("no-unused-existing-resources", DiagnosticLevel.Warning, "Existing resource \"resourceA\" is declared but never used."),
                ("BCP135", DiagnosticLevel.Error, "Scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."),
            });

            // use an invalid targetScope without setting the scope property
            (_, diags, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
targetScope = 'subscription'

resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
}
"));

            diags.Should().HaveDiagnostics(new[] {
                ("no-unused-existing-resources", DiagnosticLevel.Warning, "Existing resource \"resourceA\" is declared but never used."),
                ("BCP135", DiagnosticLevel.Error, "Scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."),
            });
        }

        [TestMethod]
        public void Errors_are_raised_for_extensions_of_existing_resources_at_invalid_scopes()
        {
            var typeReference = ResourceTypeReference.Parse("My.Rp/myResource@2020-01-01");
            var typeLoader = TestTypeHelper.CreateResourceTypeLoaderWithTypes(new[] {
                new ResourceTypeComponents(typeReference, ResourceScope.ResourceGroup | ResourceScope.Resource, ResourceScope.None, ResourceFlags.None, new ObjectType(typeReference.FormatName(), TypeSymbolValidationFlags.Default, new [] {
                    new NamedTypeProperty("name", LanguageConstants.String, TypePropertyFlags.DeployTimeConstant, "name property"),
                }, null))
            });

            // extension resource of an existing resource at an invalid scope
            var (_, diags, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
  scope: resourceGroup('different')
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceB'
  scope: resourceA
}
"));

            diags.Should().HaveDiagnostics(new[] {
                ("BCP139", DiagnosticLevel.Error, "A resource's scope must match the scope of the Bicep file for it to be deployable. You must use modules to deploy resources to a different scope."),
            });
        }

        [TestMethod]
        public void Extensions_of_existing_resources_are_permitted()
        {
            var typeReference = ResourceTypeReference.Parse("My.Rp/myResource@2020-01-01");
            var typeLoader = TestTypeHelper.CreateResourceTypeLoaderWithTypes(new[] {
                new ResourceTypeComponents(typeReference, ResourceScope.ResourceGroup | ResourceScope.Resource, ResourceScope.None, ResourceFlags.None, new ObjectType(typeReference.FormatName(), TypeSymbolValidationFlags.Default, new [] {
                    new NamedTypeProperty("name", LanguageConstants.String, TypePropertyFlags.DeployTimeConstant, "name property"),
                }, null))
            });

            // extension resource of an existing resource at an invalid scope
            var (template, diags, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
resource resourceA 'My.Rp/myResource@2020-01-01' existing = {
  name: 'resourceA'
}

resource resourceB 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceB'
  scope: resourceA
}
"));

            using (new AssertionScope())
            {
                diags.Should().BeEmpty();

                template.Should().HaveValueAtPath("$.resources[?(@.name == 'resourceB')].scope", "[resourceId('My.Rp/myResource', 'resourceA')]");
            }
        }

        [DataRow("resourceGroup", true)]
        [DataRow("subscription", true)]
        [DataRow("managementGroup", true)]
        [DataRow("tenant", false)]
        [DataTestMethod]
        public void Tenant_scope_resources_can_be_deployed_from_anywhere(string targetScope, bool tenantScopeExpected)
        {
            var typeReference = ResourceTypeReference.Parse("My.Rp/myResource@2020-01-01");
            var typeLoader = TestTypeHelper.CreateResourceTypeLoaderWithTypes(new[] {
                new ResourceTypeComponents(typeReference, ResourceScope.Tenant, ResourceScope.None, ResourceFlags.None, new ObjectType(typeReference.FormatName(), TypeSymbolValidationFlags.Default, new [] {
                    new NamedTypeProperty("name", LanguageConstants.String, TypePropertyFlags.DeployTimeConstant, "name property"),
                }, null))
            });

            var (template, diags, _) = CompilationHelper.Compile(typeLoader, ("main.bicep", @"
targetScope = 'TARGET_SCOPE'
resource resourceA 'My.Rp/myResource@2020-01-01' = {
  name: 'resourceA'
  scope: tenant()
}
".Replace("TARGET_SCOPE", targetScope)));

            using (new AssertionScope())
            {
                diags.Should().BeEmpty();
                template.Should().NotBeNull();

                const string path = "$.resources[?(@.name == 'resourceA')].scope";
                if (tenantScopeExpected)
                {

                    template.Should().HaveValueAtPath(path, "/");
                }
                else
                {
                    template.Should().NotHaveValueAtPath(path);
                }
            }
        }

        [TestMethod]
        public void Existing_resource_with_symbolic_names_enabled_includes_scope_properties()
        {
            var services = new ServiceBuilder().WithFeatureOverrides(new(this.TestContext, SymbolicNameCodegenEnabled: true));
            var (template, diagnostics, _) = CompilationHelper.Compile(services, @"
targetScope = 'subscription'

var accounts = [
  {
    name: 'majastrzcri'
    rg: 'majastrz-cri'
  }
  {
    name: 'majastrzcri2'
    rg: 'majastrz-cri2'
  }
]

resource storage 'Microsoft.Storage/storageAccounts@2021-08-01' existing = [for account in accounts: {
  name: account.name
  scope: resourceGroup(account.rg)
}]

#disable-next-line outputs-should-not-contain-secrets
output keys array = [for (account, i) in accounts: storage[i].listKeys().keys[0].value]
output tiers array = [for (account, i) in accounts: storage[i].properties.accessTier]

");
            using (new AssertionScope())
            {
                diagnostics.Should().BeEmpty();
                template.Should().NotBeNull();

                template.Should().HaveValueAtPath("$.resources.storage.existing", true);
                template.Should().HaveValueAtPath("$.resources.storage.resourceGroup", "[variables('accounts')[copyIndex()].rg]");
            }
        }

        [TestMethod]
        public void Existing_policy_definitions_can_use_tenant_scope_from_management_group()
        {
            // Regression test for https://github.com/Azure/bicep/issues/18166
            // Microsoft.Authorization/policyDefinitions should allow existing resources to use tenant() scope
            var (template, diagnostics, _) = CompilationHelper.Compile(@"
targetScope = 'managementGroup'

var input array = [
  {policyDefinitionId: '0a914e76-4921-4c19-b460-a2d36003525a'}
]

// Get all builtin policies as defined in the input parameter
resource builtin 'Microsoft.Authorization/policyDefinitions@2023-04-01' existing = [for policy in input: {
  scope: tenant()
  name: policy.policyDefinitionId
}]
");

            using (new AssertionScope())
            {
                diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty();
                template.Should().NotBeNull();
            }
        }

        [TestMethod]
        public void Policy_definitions_can_use_scope_property_for_deployment()
        {
            // Regression test for https://github.com/Azure/bicep/issues/18163
            var (template, diagnostics, _) = CompilationHelper.Compile(@"
targetScope = 'subscription'

resource policyDef 'Microsoft.Authorization/policyDefinitions@2021-06-01' = {
  name: 'test-policy'
  scope: subscription()
  properties: {
    displayName: 'Test Policy'
    description: 'Test policy for regression test'
    mode: 'All'
    policyRule: {
      if: {
        field: 'type'
        equals: 'Microsoft.Storage/storageAccounts'
      }
      then: {
        effect: 'audit'
      }
    }
  }
}
");

            using (new AssertionScope())
            {
                diagnostics.ExcludingLinterDiagnostics().Should().BeEmpty();
                template.Should().NotBeNull();
            }
        }
    }
}
