// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package dashboard

import (
	"fmt"
	"regexp"
	"time"

	"github.com/hashicorp/go-azure-helpers/lang/response"
	"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
	"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
	"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
	"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
	"github.com/hashicorp/go-azure-sdk/resource-manager/dashboard/2023-09-01/grafanaresource"
	"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
	"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
	"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
	"github.com/hashicorp/terraform-provider-azurerm/internal/timeouts"
)

func dataSourceDashboardGrafana() *pluginsdk.Resource {
	return &pluginsdk.Resource{
		Read: dataSourceDashboardGrafanaRead,

		Timeouts: &pluginsdk.ResourceTimeout{
			Read: pluginsdk.DefaultTimeout(5 * time.Minute),
		},

		Schema: map[string]*pluginsdk.Schema{
			"name": {
				Type:     pluginsdk.TypeString,
				Required: true,
				ValidateFunc: validation.StringMatch(
					regexp.MustCompile(`^[a-zA-Z][-a-zA-Z\d]{0,21}[a-zA-Z\d]$`),
					`The name length must be from 2 to 23 characters. The name can only contain letters, numbers and dashes, and it must begin with a letter and end with a letter or digit.`,
				),
			},

			"resource_group_name": commonschema.ResourceGroupNameForDataSource(),

			"location": commonschema.LocationComputed(),

			"api_key_enabled": {
				Type:     pluginsdk.TypeBool,
				Computed: true,
			},

			"auto_generated_domain_name_label_scope": {
				Type:     pluginsdk.TypeString,
				Computed: true,
			},

			"deterministic_outbound_ip_enabled": {
				Type:     pluginsdk.TypeBool,
				Computed: true,
			},

			"azure_monitor_workspace_integrations": {
				Type:     pluginsdk.TypeList,
				Computed: true,
				Elem: &pluginsdk.Resource{
					Schema: map[string]*pluginsdk.Schema{
						"resource_id": {
							Type:     pluginsdk.TypeString,
							Computed: true,
						},
					},
				},
			},

			"identity": commonschema.SystemOrUserAssignedIdentityOptionalForceNew(),

			"public_network_access_enabled": {
				Type:     pluginsdk.TypeBool,
				Computed: true,
			},

			"grafana_major_version": {
				Type:     pluginsdk.TypeString,
				Computed: true,
			},

			"grafana_version": {
				Type:     pluginsdk.TypeString,
				Computed: true,
			},

			"outbound_ips": {
				Type:     pluginsdk.TypeList,
				Computed: true,
				Elem: &pluginsdk.Schema{
					Type: pluginsdk.TypeString,
				},
			},

			"endpoint": {
				Type:     pluginsdk.TypeString,
				Computed: true,
			},

			"sku": {
				Type:     pluginsdk.TypeString,
				Computed: true,
			},

			"zone_redundancy_enabled": {
				Type:     pluginsdk.TypeBool,
				Computed: true,
			},

			"tags": commonschema.TagsDataSource(),
		},
	}
}

func dataSourceDashboardGrafanaRead(d *pluginsdk.ResourceData, meta interface{}) error {
	client := meta.(*clients.Client).Dashboard.GrafanaResourceClient
	subscriptionId := meta.(*clients.Client).Account.SubscriptionId
	ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
	defer cancel()

	id := grafanaresource.NewGrafanaID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string))
	resp, err := client.GrafanaGet(ctx, id)
	if err != nil {
		if response.WasNotFound(resp.HttpResponse) {
			return fmt.Errorf("%s was not found", id)
		}
		return fmt.Errorf("retrieving %s: %+v", id, err)
	}

	d.SetId(id.ID())

	d.Set("name", id.GrafanaName)
	d.Set("resource_group_name", id.ResourceGroupName)

	if model := resp.Model; model != nil {
		d.Set("location", location.Normalize(*model.Location))
		if properties := model.Properties; properties != nil {
			if properties.ApiKey != nil {
				if *properties.ApiKey == grafanaresource.ApiKeyEnabled {
					d.Set("api_key_enabled", true)
				} else {
					d.Set("api_key_enabled", false)
				}
			}

			if properties.AutoGeneratedDomainNameLabelScope != nil {
				d.Set("auto_generated_domain_name_label_scope", string(*properties.AutoGeneratedDomainNameLabelScope))
			}

			if properties.DeterministicOutboundIP != nil {
				if *properties.DeterministicOutboundIP == grafanaresource.DeterministicOutboundIPEnabled {
					d.Set("deterministic_outbound_ip_enabled", true)
				} else {
					d.Set("deterministic_outbound_ip_enabled", false)
				}
			}

			if properties.Endpoint != nil {
				d.Set("endpoint", properties.Endpoint)
			}

			if properties.GrafanaIntegrations != nil && properties.GrafanaIntegrations.AzureMonitorWorkspaceIntegrations != nil {
				d.Set("azure_monitor_workspace_integrations", flattenAzureMonitorWorkspaceIntegrations(*properties.GrafanaIntegrations.AzureMonitorWorkspaceIntegrations))
			}

			if properties.GrafanaVersion != nil {
				d.Set("grafana_version", properties.GrafanaVersion)
			}

			if properties.GrafanaMajorVersion != nil {
				d.Set("grafana_major_version", properties.GrafanaMajorVersion)
			}

			if properties.OutboundIPs != nil {
				d.Set("outbound_ips", properties.OutboundIPs)
			}

			if properties.PublicNetworkAccess != nil {
				if *properties.PublicNetworkAccess == grafanaresource.PublicNetworkAccessEnabled {
					d.Set("public_network_access_enabled", true)
				} else {
					d.Set("public_network_access_enabled", false)
				}
			}

			if properties.ZoneRedundancy != nil {
				if *properties.ZoneRedundancy == grafanaresource.ZoneRedundancyEnabled {
					d.Set("zone_redundancy_enabled", true)
				} else {
					d.Set("zone_redundancy_enabled", false)
				}
			}
		}

		if model.Sku != nil {
			d.Set("sku", model.Sku.Name)
		}

		flattenedIdentity, err := identity.FlattenSystemAndUserAssignedMap((*identity.SystemAndUserAssignedMap)(model.Identity))
		if err != nil {
			return fmt.Errorf("flattening `identity`: %+v", err)
		}

		if err := d.Set("identity", flattenedIdentity); err != nil {
			return fmt.Errorf("setting `identity`: %+v", err)
		}

		if err := tags.FlattenAndSet(d, model.Tags); err != nil {
			return err
		}
	}

	return nil
}

// AzureMonitorWorkspaceIntegration represents a struct for Azure Monitor Workspace Integration
type AzureMonitorWorkspaceIntegration struct {
	AzureMonitorWorkspaceResourceId *string `json:"azureMonitorWorkspaceResourceId,omitempty"`
}

func flattenAzureMonitorWorkspaceIntegrations(integrations []grafanaresource.AzureMonitorWorkspaceIntegration) []interface{} {
	result := make([]interface{}, len(integrations))
	for i, integration := range integrations {
		result[i] = map[string]interface{}{
			"resource_id": integration.AzureMonitorWorkspaceResourceId,
		}
	}
	return result
}
