# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import ipaddress


from azure.cli.core.azclierror import (InvalidArgumentValueError, ArgumentUsageError, RequiredArgumentMissingError,
                                       ResourceNotFoundError, ValidationError, MutuallyExclusiveArgumentError)
from azure.cli.core.commands.client_factory import get_mgmt_service_client, get_subscription_id
from azure.cli.core.commands.validators import validate_tags
from azure.mgmt.core.tools import is_valid_resource_id, parse_resource_id

from knack.log import get_logger

from ._appservice_utils import _generic_site_operation
from ._client_factory import web_client_factory
from .utils import (_normalize_sku, get_sku_tier, get_resource_name_and_group,
                    get_resource_if_exists, is_functionapp, is_logicapp, is_webapp, is_centauri_functionapp,
                    _normalize_location, get_site_server_farm_id)

from .aaz.latest.network import ListServiceTags
from .aaz.latest.network.vnet import List as VNetList, Show as VNetShow
from ._constants import ACR_IMAGE_SUFFIX

logger = get_logger(__name__)


def validate_and_convert_to_int(flag, val):
    try:
        return int(val)
    except ValueError:
        raise ArgumentUsageError("{} is expected to have an int value.".format(flag))


def validate_range_of_int_flag(flag_name, value, min_val, max_val):
    value = validate_and_convert_to_int(flag_name, value)
    if min_val > value or value > max_val:
        raise ArgumentUsageError("Usage error: {} is expected to be between {} and {} (inclusive)".format(flag_name,
                                                                                                          min_val,
                                                                                                          max_val))
    return value


def validate_timeout_value(namespace):
    """Validates that zip deployment timeout is set to a reasonable min value"""
    if isinstance(namespace.timeout, int):
        if namespace.timeout <= 29:
            raise ArgumentUsageError('--timeout value should be a positive value in seconds and should be at least 30')


def validate_site_create(cmd, namespace):
    """Validate the SiteName that is being used to create is available
    This API requires that the RG is already created"""
    client = web_client_factory(cmd.cli_ctx)
    if isinstance(namespace.name, str) and isinstance(namespace.resource_group_name, str) \
            and isinstance(namespace.plan, str):
        resource_group_name = namespace.resource_group_name
        plan = namespace.plan
        if is_valid_resource_id(plan):
            parsed_result = parse_resource_id(plan)
            plan_info = client.app_service_plans.get(parsed_result['resource_group'], parsed_result['name'])
        else:
            plan_info = client.app_service_plans.get(resource_group_name, plan)
        if not plan_info:
            raise ResourceNotFoundError("The plan '{}' doesn't exist in the resource group '{}'".format(
                plan, resource_group_name))
        # verify that the name is available for create
        validation_payload = {
            "name": namespace.name,
            "type": "Microsoft.Web/sites",
            "location": plan_info.location,
            "properties": {
                "serverfarmId": plan_info.id
            }
        }
        validation = client.validate(resource_group_name, validation_payload)
        if validation.status.lower() == "failure" and validation.error.code != 'SiteAlreadyExists':
            raise ValidationError(validation.error.message)


def validate_ase_create(cmd, namespace):
    # Validate the ASE Name availability
    from azure.mgmt.web.models import ResourceNameAvailabilityRequest
    client = web_client_factory(cmd.cli_ctx)
    resource_type = 'Microsoft.Web/hostingEnvironments'
    if isinstance(namespace.name, str):
        request = ResourceNameAvailabilityRequest(name=namespace.name, type=resource_type)
        name_validation = client.check_name_availability(request)
        if not name_validation.name_available:
            raise ValidationError(name_validation.message)


def _validate_asp_sku(sku, app_service_environment, zone_redundant):
    supported_skus = ['PREMIUMV2', 'PREMIUMV3', 'PREMIUMMV3', 'PREMIUM0V3', 'PREMIUMV4', 'PREMIUMMV4', 'ISOLATEDV2', 'ISOLATEDMV2', 'ELASTICPREMIUM']  # pylint: disable=line-too-long
    if zone_redundant and get_sku_tier(sku).upper() not in supported_skus:
        raise ValidationError("Zone redundancy cannot be enabled for sku {}".format(sku))
    # Isolated SKU is supported only for ASE
    if sku.upper() in ['I1V2', 'I2V2', 'I3V2', 'I4V2', 'I5V2', 'I6V2', 'I1MV2', 'I2MV2', 'I3MV2', 'I4MV2', 'I5MV2']:
        if not app_service_environment:
            raise ValidationError("The pricing tier 'Isolated' is not allowed for this app service plan. "
                                  "Use this link to learn more: "
                                  "https://learn.microsoft.com/azure/app-service/overview-hosting-plans")
    else:
        if app_service_environment:
            raise ValidationError("Only pricing tier 'IsolatedV2' and 'IsolatedMV2' is allowed in this "
                                  "app service plan. Use this link to "
                                  "learn more: https://learn.microsoft.com/azure/app-service/overview-hosting-plans")


def validate_asp_create(namespace):
    validate_tags(namespace)
    # is_linux is None when not explicitly provided by the user (default).
    # Resolve the default: Linux unless --hyper-v is specified.
    if namespace.is_linux is None:
        namespace.is_linux = not namespace.hyper_v
    elif namespace.is_linux and namespace.hyper_v:
        raise MutuallyExclusiveArgumentError('Usage error: --is-linux true and --hyper-v cannot be used together.')
    if namespace.sku is None:
        if namespace.is_linux:
            namespace.sku = 'P0V3'
            logger.warning("No --sku specified. Using default: P0V3 (Premium V3 Extra Small). "
                           "See all options: az appservice plan create --help. "
                           "For current pricing, visit: "
                           "https://azure.microsoft.com/pricing/details/app-service/")
        else:
            namespace.sku = 'B1'
    sku = _normalize_sku(namespace.sku)
    _validate_asp_sku(sku, namespace.app_service_environment, namespace.zone_redundant)


def validate_functionapp_asp_create(namespace):
    validate_tags(namespace)
    sku = _normalize_sku(namespace.sku)
    tier = get_sku_tier(sku)
    _validate_asp_sku(sku=sku, app_service_environment=None, zone_redundant=namespace.zone_redundant)
    if namespace.max_burst is not None:
        if tier.lower() != "elasticpremium":
            raise ArgumentUsageError("--max-burst is only supported for Elastic Premium (EP) plans")


def validate_functionapp_on_containerapp_site_config_set(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    if is_centauri_functionapp(cmd, resource_group_name, name):
        raise ValidationError(
            "Invalid command. This is not supported for Azure Functions on Azure Container app environments.",
            "Please use the following command instead: az functionapp config container set")
    warn_linux_consumption_eol(cmd, namespace)


def validate_functionapp_on_containerapp_container_settings_delete(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    if is_centauri_functionapp(cmd, resource_group_name, name):
        raise ValidationError(
            "Invalid command. This is currently not supported for Azure Functions on Azure Container app environments.",
            "Please use the following command instead: az functionapp config appsettings set")
    validate_functionapp_on_flex_plan(cmd, namespace)


def validate_functionapp_on_containerapp_update(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    if is_centauri_functionapp(cmd, resource_group_name, name):
        raise ValidationError(
            "Invalid command. This is currently not supported for Azure Functions on Azure Container app environments.",
            "Please use either 'az functionapp config appsettings set' or 'az functionapp config container set'")
    warn_linux_consumption_eol(cmd, namespace)


def validate_functionapp_on_containerapp_site_config_show(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    if is_centauri_functionapp(cmd, resource_group_name, name):
        raise ValidationError(
            "Invalid command. This is not supported for Azure Functions on Azure Container app environments.",
            "Please use the following command instead: az functionapp config container show")
    warn_linux_consumption_eol(cmd, namespace)


def validate_functionapp_on_flex_plan(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = _get_app_name(namespace)
    functionapp = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get')
    server_farm_id = get_site_server_farm_id(functionapp)
    if server_farm_id is None:
        return
    parsed_plan_id = parse_resource_id(server_farm_id)
    client = web_client_factory(cmd.cli_ctx)
    plan_info = client.app_service_plans.get(parsed_plan_id['resource_group'], parsed_plan_id['name'])
    if plan_info is None:
        raise ResourceNotFoundError('Could not determine the current plan of the functionapp')
    if plan_info.sku.tier == 'FlexConsumption':
        raise ValidationError('Invalid command. This is not currently supported for Azure Functions '
                              'on the Flex Consumption plan.')


def validate_is_flex_functionapp(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    functionapp = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get')
    server_farm_id = get_site_server_farm_id(functionapp)
    if server_farm_id is None:
        raise ValidationError('This command is only valid for Azure Functions on the FlexConsumption plan.')
    parsed_plan_id = parse_resource_id(server_farm_id)
    client = web_client_factory(cmd.cli_ctx)
    plan_info = client.app_service_plans.get(parsed_plan_id['resource_group'], parsed_plan_id['name'])
    if plan_info is None:
        raise ResourceNotFoundError('Could not determine the current plan of the functionapp')
    if plan_info.sku.tier.lower() != 'flexconsumption':
        raise ValidationError('This command is only valid for Azure Functions on the FlexConsumption plan.')


def warn_linux_consumption_eol(cmd, namespace):
    """Shows a warning if the function app is on Linux Consumption plan.

    This function is designed to never fail or raise exceptions. Any errors are silently ignored.
    """
    # pylint: disable=broad-except
    try:
        resource_group_name = (getattr(namespace, 'resource_group_name', None) or
                               getattr(namespace, 'resource_group', None))
        name = _get_app_name(namespace)
        slot = getattr(namespace, 'slot', None)
        if not name or not resource_group_name:
            return

        functionapp = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get', slot)
        if functionapp is None:
            return

        # Check if Linux (reserved=True)
        is_linux = getattr(functionapp, 'reserved', False)
        if not is_linux:
            return

        # Get the plan and check if it's Consumption (Dynamic tier)
        server_farm_id = get_site_server_farm_id(functionapp)
        if server_farm_id is None:
            return

        parsed_plan_id = parse_resource_id(server_farm_id)
        client = web_client_factory(cmd.cli_ctx)
        plan_info = client.app_service_plans.get(parsed_plan_id['resource_group'], parsed_plan_id['name'])
        if plan_info is None or not hasattr(plan_info, 'sku') or plan_info.sku is None:
            return

        if plan_info.sku.tier == 'Dynamic':
            logger.warning(
                "Migrate your app to Flex Consumption as Linux Consumption will reach EOL on "
                "September 30, 2028 and will no longer be supported. Flex Consumption is now the "
                "recommended serverless hosting plan for Azure Functions. It offers faster scaling, "
                "reduced cold starts, private networking, and more control over performance and cost. Help link: "
                "https://learn.microsoft.com/en-us/azure/azure-functions/migration/migrate-plan-consumption-to-flex"
            )
    except Exception:
        # This warning function should never fail or block the main command execution.
        pass


def validate_app_exists(cmd, namespace):
    app = namespace.name
    resource_group_name = namespace.resource_group_name
    slot = namespace.slot
    app = _generic_site_operation(cmd.cli_ctx, resource_group_name, app, 'get', slot)
    if not app:
        raise ResourceNotFoundError("'{}' app not found in ResourceGroup '{}'".format(app, resource_group_name))


def validate_functionapp_on_containerapp_vnet_add(cmd, namespace):
    validate_functionapp_on_containerapp_vnet(cmd, namespace)
    validate_add_vnet(cmd, namespace)


def validate_functionapp_on_containerapp_vnet(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    if is_centauri_functionapp(cmd, resource_group_name, name):
        raise ValidationError(
            'Unsupported operation on function app.',
            'Please set virtual network configuration for the function app at Container app environment level.')
    warn_linux_consumption_eol(cmd, namespace)


def validate_add_vnet(cmd, namespace):
    from azure.core.exceptions import ResourceNotFoundError as ResNotFoundError

    resource_group_name = namespace.resource_group_name
    vnet_identifier = namespace.vnet
    name = namespace.name
    slot = namespace.slot

    if is_valid_resource_id(vnet_identifier):
        current_sub_id = get_subscription_id(cmd.cli_ctx)
        parsed_vnet = parse_resource_id(vnet_identifier)

        vnet_sub_id = parsed_vnet['subscription']
        vnet_group = parsed_vnet['resource_group']
        vnet_name = parsed_vnet['name']

        cmd.cli_ctx.data['subscription_id'] = vnet_sub_id
        vnet_loc = VNetShow(cli_ctx=cmd.cli_ctx)(command_args={
            "name": vnet_name,
            "resource_group": vnet_group
        })["location"]
        cmd.cli_ctx.data['subscription_id'] = current_sub_id
    else:
        try:
            vnet_loc = VNetShow(cli_ctx=cmd.cli_ctx)(command_args={
                "name": vnet_identifier,
                "resource_group": namespace.resource_group_name
            })["location"]
        except ResNotFoundError:
            vnets = VNetList(cli_ctx=cmd.cli_ctx)(command_args={})
            vnet_loc = ''
            for v in vnets:
                if vnet_identifier == v["name"]:
                    vnet_loc = v["location"]
                    break

    if not vnet_loc:
        # Fall back to back end validation
        logger.warning("Failed to fetch vnet. Skipping location validation.")
        return

    webapp = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get', slot)

    webapp_loc = _normalize_location(cmd, webapp.location)
    vnet_loc = _normalize_location(cmd, vnet_loc)

    if vnet_loc != webapp_loc:
        raise ValidationError("The app and the vnet resources are in different locations. "
                              "Cannot integrate a regional VNET to an app in a different region"
                              "Web app location: {}. Vnet location: {}".format(webapp_loc, vnet_loc))


def validate_ip_address(cmd, namespace):
    if namespace.ip_address is not None:
        _validate_ip_address_format(namespace)
        # For prevention of adding the duplicate IPs.
        if 'add' in cmd.name:
            _validate_ip_address_existence(cmd, namespace)


def validate_onedeploy_params(namespace):
    if namespace.src_path and namespace.src_url:
        raise MutuallyExclusiveArgumentError('Only one of --src-path and --src-url can be specified')

    if not namespace.src_path and not namespace.src_url:
        raise RequiredArgumentMissingError('Either of --src-path or --src-url must be specified')

    if namespace.src_url and not namespace.artifact_type:
        raise RequiredArgumentMissingError('Deployment type is mandatory when deploying from URLs. Use --type')


def _validate_ip_address_format(namespace):
    if namespace.ip_address is not None:
        input_value = namespace.ip_address
        if ' ' in input_value:
            raise InvalidArgumentValueError("Spaces not allowed: '{}' ".format(input_value))
        input_ips = input_value.split(',')
        if len(input_ips) > 8:
            raise InvalidArgumentValueError('Maximum 8 IP addresses are allowed per rule.')
        validated_ips = ''
        for ip in input_ips:
            # Use ipaddress library to validate ip network format
            ip_obj = ipaddress.ip_network(ip, False)
            validated_ips += str(ip_obj) + ','
        namespace.ip_address = validated_ips[:-1]


def _validate_ip_address_existence(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    slot = namespace.slot
    scm_site = namespace.scm_site
    configs = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get_configuration', slot)
    access_rules = configs.scm_ip_security_restrictions if scm_site else configs.ip_security_restrictions
    new_headers = _normalize_http_headers(getattr(namespace, 'http_headers', None))
    new_ip_set = _normalize_ip_address_list(namespace.ip_address)
    for rule in access_rules or []:
        if _normalize_ip_address_list(rule.ip_address) != new_ip_set:
            continue
        if _normalize_http_headers(rule.headers) == new_headers:
            if not new_headers:
                raise ArgumentUsageError(
                    "An access-restriction rule with IP address '{}' already exists. Cannot add a "
                    "duplicate rule. Use a different IP address, or add a --http-header filter to "
                    "create an additional rule.".format(namespace.ip_address))
            raise ArgumentUsageError(
                "An access-restriction rule with IP address '{}' and the same HTTP header filter "
                "already exists. Cannot add a duplicate rule. Use a different IP address or vary "
                "the --http-header values to create an additional rule.".format(namespace.ip_address))


def _normalize_ip_address_list(ip_address):
    """Return a frozenset of canonical CIDR strings parsed from a comma-separated input.

    The CLI accepts up to 8 comma-separated CIDRs in a single rule's ``--ip-address``. ARM stores
    them in the same comma-separated form on the rule's ``ip_address`` attribute. Two rules
    represent the same source set regardless of the order in which the CIDRs appear, so duplicate
    detection should compare unordered sets. Returns ``frozenset()`` for missing/empty input.
    """
    if not ip_address:
        return frozenset()
    return frozenset(part.strip() for part in ip_address.split(',') if part.strip())


def _normalize_http_headers(headers):
    """Normalize an http-header collection for duplicate-rule comparison.

    Accepts either the CLI input form (list of "name=value" strings, as supplied via --http-header)
    or the SDK/ARM form (dict mapping header name to a list of values). Returns a dict whose keys
    are lowercased header names and whose values are frozensets of value strings, so that order of
    values within a single header (which ARM treats as a set) does not affect equality. None and
    an empty collection are both normalized to an empty dict so that a rule with no headers
    compares equal to itself regardless of representation.
    """
    if not headers:
        return {}
    result = {}
    if isinstance(headers, dict):
        for header_name, values in headers.items():
            if header_name is None:
                continue
            normalized_name = header_name.strip().lower()
            if values is None:
                continue
            if isinstance(values, str):
                values = [values]
            value_set = frozenset(v for v in values if v)
            if value_set:
                result[normalized_name] = value_set
        return result
    # CLI input form: list of "name=value" strings.
    for header_str in headers:
        if header_str is None or '=' not in header_str:
            continue
        header_name, _, header_value = header_str.partition('=')
        normalized_name = header_name.strip().lower()
        normalized_value = header_value.strip()
        if not normalized_name or not normalized_value:
            continue
        existing = set(result.get(normalized_name, frozenset()))
        existing.add(normalized_value)
        result[normalized_name] = frozenset(existing)
    return result


def validate_service_tag(cmd, namespace):
    if namespace.service_tag is not None:
        _validate_service_tag_format(cmd, namespace)
        # For prevention of adding the duplicate IPs.
        if 'add' in cmd.name:
            _validate_service_tag_existence(cmd, namespace)


def _validate_service_tag_format(cmd, namespace):
    if namespace.service_tag is not None:
        input_value = namespace.service_tag
        if ' ' in input_value:
            raise InvalidArgumentValueError("Spaces not allowed: '{}' ".format(input_value))
        input_tags = input_value.split(',')
        if len(input_tags) > 8:
            raise InvalidArgumentValueError('Maximum 8 service tags are allowed per rule.')
        resource_group_name = namespace.resource_group_name
        name = namespace.name
        webapp = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get')
        service_tag_full_list = ListServiceTags(cli_ctx=cmd.cli_ctx)(command_args={
            "location": webapp.location
        })
        if namespace.skip_service_tag_validation is None:
            if service_tag_full_list is None or "values" not in service_tag_full_list:
                logger.warning('Not able to get full Service Tag list. Cannot validate Service Tag.')
                return
            for tag in input_tags:
                valid_tag = False
                for tag_full_list in service_tag_full_list["values"]:
                    if tag.lower() == tag_full_list["name"].lower():
                        valid_tag = True
                        continue
                if not valid_tag:
                    raise InvalidArgumentValueError('Unknown Service Tag: ' + tag)


def _validate_service_tag_existence(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    slot = namespace.slot
    scm_site = namespace.scm_site
    input_tag_value = namespace.service_tag.replace(' ', '')
    configs = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get_configuration', slot)
    access_rules = configs.scm_ip_security_restrictions if scm_site else configs.ip_security_restrictions
    new_headers = _normalize_http_headers(getattr(namespace, 'http_headers', None))
    for rule in access_rules or []:
        if not rule.ip_address or rule.ip_address.lower() != input_tag_value.lower():
            continue
        if _normalize_http_headers(rule.headers) == new_headers:
            if not new_headers:
                raise ArgumentUsageError(
                    "A service-tag access-restriction rule with value '{}' already exists. Cannot "
                    "add a duplicate rule. Use a different service tag, or add a --http-header "
                    "filter to create an additional rule.".format(namespace.service_tag))
            raise ArgumentUsageError(
                "A service-tag access-restriction rule with value '{}' and the same HTTP header "
                "filter already exists. Cannot add a duplicate rule. Use a different service tag "
                "or vary the --http-header values to create an additional rule.".format(
                    namespace.service_tag))


def validate_public_cloud(cmd):
    from azure.cli.core.cloud import AZURE_PUBLIC_CLOUD
    if cmd.cli_ctx.cloud.name != AZURE_PUBLIC_CLOUD.name:
        raise ValidationError('This command is not yet supported on soveriegn clouds.')


def validate_staticsite_sku(cmd, namespace):
    from azure.mgmt.web import WebSiteManagementClient
    client = get_mgmt_service_client(cmd.cli_ctx, WebSiteManagementClient).static_sites
    sku_name = client.get_static_site(namespace.resource_group_name, namespace.name).sku.name
    if sku_name.lower() != "standard":
        raise ValidationError("Invalid SKU: '{}'. Staticwebapp must have 'Standard' SKU".format(sku_name))


def validate_staticsite_link_function(cmd, namespace):
    from azure.mgmt.web import WebSiteManagementClient
    validate_staticsite_sku(cmd, namespace)

    if not is_valid_resource_id(namespace.function_resource_id):
        raise ArgumentUsageError("--function-resource-id must specify a function resource ID. "
                                 "To get resource ID, use the following commmand, inserting the function "
                                 "group/name as needed: \n"
                                 "az functionapp show --resource-group \"[FUNCTION_RESOURCE_GROUP]\" "
                                 "--name \"[FUNCTION_NAME]\" --query id ")

    client = get_mgmt_service_client(cmd.cli_ctx, WebSiteManagementClient, api_version="2020-12-01").static_sites
    functions = client.get_user_provided_function_apps_for_static_site(
        name=namespace.name, resource_group_name=namespace.resource_group_name)
    if list(functions):
        raise ValidationError("Cannot have more than one user provided function app associated with a Static Web App")


def validate_functionapp(cmd, namespace):
    validate_vnet_integration(cmd, namespace)
    validate_registry_server(namespace)
    validate_registry_user(namespace)
    validate_registry_pass(namespace)


# TODO consider combining with validate_add_vnet
def validate_vnet_integration(cmd, namespace):
    validate_tags(namespace)
    if _get_environment(namespace):
        if namespace.vnet:
            raise ArgumentUsageError(
                "Invalid input. '--vnet' is not a supported property for function apps deployed to Azure Container "
                "Apps.",
                "Please try again without '--vnet' and configure vnet from Azure Container Apps environment.")
        if namespace.subnet:
            raise ArgumentUsageError(
                "Invalid input. '--subnet' is not a supported property for function apps deployed to Azure Container "
                "Apps.",
                "Please try again without '--subnet' and configure subnet from Azure Container Apps environment.")
    if namespace.subnet or namespace.vnet:
        if not namespace.subnet:
            raise ArgumentUsageError("Cannot use --vnet without --subnet")
        if not is_valid_resource_id(namespace.subnet) and not namespace.vnet:
            raise ArgumentUsageError("Must either specify subnet by resource ID or include --vnet argument")

        client = web_client_factory(cmd.cli_ctx)
        if is_valid_resource_id(namespace.plan):
            parse_result = parse_resource_id(namespace.plan)
            plan_info = client.app_service_plans.get(parse_result['resource_group'], parse_result['name'])
        elif _get_flexconsumption_location(namespace):
            return
        elif _get_consumption_plan_location(namespace):
            raise ArgumentUsageError("Virtual network integration is not allowed for consumption plans.")
        else:
            plan_info = client.app_service_plans.get(name=namespace.plan,
                                                     resource_group_name=namespace.resource_group_name)

        sku_name = plan_info.sku.name
        disallowed_skus = {'FREE', 'SHARED', 'PremiumContainer', 'Isolated', 'IsolatedV2'}
        if get_sku_tier(sku_name) in disallowed_skus:
            raise ArgumentUsageError("App Service Plan has invalid sku for vnet integration: {}."
                                     "Plan sku cannot be one of: {}. "
                                     "Please run 'az appservice plan create -h' "
                                     "to see all available App Service Plan SKUs ".format(sku_name, disallowed_skus))


def _validate_ase_exists(client, ase_name, ase_rg):
    extant_ase = get_resource_if_exists(client.app_service_environments,
                                        resource_group_name=ase_rg, name=ase_name)
    if extant_ase is None:
        raise ValidationError("App Service Environment {} does not exist.".format(ase_name))


# if the ASP exists, validate that it is in the ASE
def _validate_plan_in_ase(client, plan_name, plan_rg, ase_id):
    if plan_name is not None:
        plan_info = get_resource_if_exists(client.app_service_plans,
                                           resource_group_name=plan_rg, name=plan_name)
        if plan_info is not None:
            plan_hosting_env = plan_info.hosting_environment_profile

            if not plan_hosting_env or plan_hosting_env.id != ase_id:
                raise ValidationError("Plan {} already exists and is not in the "
                                      "app service environment.".format(plan_name))


def _validate_ase_is_v3(ase):
    if ase.kind.upper() != "ASEV3":
        raise ValidationError("Only V3 App Service Environments supported")


def _validate_ase_not_ilb(ase):
    if (ase.internal_load_balancing_mode != 0) and (ase.internal_load_balancing_mode != "None"):
        raise ValidationError("Internal Load Balancer (ILB) App Service Environments not supported")


def validate_webapp_up(cmd, namespace):
    if namespace.runtime and namespace.html:
        raise MutuallyExclusiveArgumentError('Conflicting parameters: cannot have both --runtime and --html specified.')

    client = web_client_factory(cmd.cli_ctx)
    if namespace.app_service_environment:
        ase_name, ase_rg, ase_id = get_resource_name_and_group(cmd, namespace.app_service_environment,
                                                               namespace.resource_group_name,
                                                               namespace="Microsoft.Web",
                                                               type="hostingEnvironments")
        _validate_ase_exists(client, ase_name, ase_rg)
        _validate_plan_in_ase(client, namespace.plan, namespace.resource_group_name, ase_id)

        ase = client.app_service_environments.get(resource_group_name=ase_rg, name=ase_name)
        _validate_ase_is_v3(ase)
        _validate_ase_not_ilb(ase)


def _get_app_name(namespace):
    if hasattr(namespace, "name"):
        return namespace.name
    if hasattr(namespace, "webapp"):
        return namespace.webapp
    return None


def _get_environment(namespace):
    if hasattr(namespace, "environment"):
        return namespace.environment
    return None


def _get_flexconsumption_location(namespace):
    if hasattr(namespace, "flexconsumption_location"):
        return namespace.flexconsumption_location
    return None


def _get_consumption_plan_location(namespace):
    if hasattr(namespace, "consumption_plan_location"):
        return namespace.consumption_plan_location
    return None


def validate_app_is_webapp(cmd, namespace):
    client = web_client_factory(cmd.cli_ctx)
    name = _get_app_name(namespace)
    rg = namespace.resource_group
    app = get_resource_if_exists(client.web_apps, name=name, resource_group_name=rg)
    if is_functionapp(app):
        raise ValidationError(f"App '{name}' in group '{rg}' is a function app.")
    if is_logicapp(app):
        raise ValidationError(f"App '{name}' in group '{rg}' is a logic app.")


def validate_app_is_functionapp(cmd, namespace):
    client = web_client_factory(cmd.cli_ctx)
    name = _get_app_name(namespace)
    rg = namespace.resource_group
    app = get_resource_if_exists(client.web_apps, name=name, resource_group_name=rg)
    if is_logicapp(app):
        raise ValidationError(f"App '{name}' in group '{rg}' is a logic app.")
    if is_webapp(app):
        raise ValidationError(f"App '{name}' in group '{rg}' is a web app.")
    warn_linux_consumption_eol(cmd, namespace)


def validate_centauri_delete_function(cmd, namespace):
    resource_group_name = namespace.resource_group_name
    name = namespace.name
    if is_centauri_functionapp(cmd, resource_group_name, name):
        raise ValidationError(
            "Invalid Operation. This function is currently present in your image",
            "Please modify your image to remove the function and provide an updated image.")
    warn_linux_consumption_eol(cmd, namespace)


def validate_registry_server(namespace):
    if namespace.environment and namespace.registry_server:
        if not namespace.registry_username or not namespace.registry_password:
            if ACR_IMAGE_SUFFIX not in namespace.registry_server:
                raise RequiredArgumentMissingError("Usage error: --registry-server, --registry-password and"
                                                   " --registry-username are required together if not using Azure Container Registry")  # pylint: disable=line-too-long


def validate_registry_user(namespace):
    if namespace.environment and namespace.registry_username:
        if not namespace.registry_server or (not namespace.registry_password and ACR_IMAGE_SUFFIX not in namespace.registry_server):  # pylint: disable=line-too-long
            raise RequiredArgumentMissingError("Usage error: --registry-server, --registry-password and"
                                               " --registry-username are required together if not using Azure Container Registry")  # pylint: disable=line-too-long


def validate_registry_pass(namespace):
    if namespace.environment and namespace.registry_password:
        if not namespace.registry_server or (not namespace.registry_username and ACR_IMAGE_SUFFIX not in namespace.registry_server):  # pylint: disable=line-too-long
            raise RequiredArgumentMissingError("Usage error: --registry-server, --registry-password and"
                                               " --registry-username are required together if not using Azure Container Registry")  # pylint: disable=line-too-long
