package parse

import (
	"fmt"
	"github.com/hashicorp/hcl/v2"
	"github.com/turbot/steampipe/pkg/steampipeconfig/modconfig"
)

// validate the resource
func validateResource(resource modconfig.HclResource) hcl.Diagnostics {
	var diags hcl.Diagnostics
	if qp, ok := resource.(modconfig.NodeAndEdgeProvider); ok {
		moreDiags := validateNodeAndEdgeProvider(qp)
		diags = append(diags, moreDiags...)
	} else if qp, ok := resource.(modconfig.QueryProvider); ok {
		moreDiags := validateQueryProvider(qp)
		diags = append(diags, moreDiags...)
	}

	if wp, ok := resource.(modconfig.WithProvider); ok {
		moreDiags := validateRuntimeDependencyProvider(wp)
		diags = append(diags, moreDiags...)
	}
	return diags
}

func validateRuntimeDependencyProvider(wp modconfig.WithProvider) hcl.Diagnostics {
	resource := wp.(modconfig.HclResource)
	var diags hcl.Diagnostics
	if len(wp.GetWiths()) > 0 && !resource.IsTopLevel() {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Only top level resources can have `with` blocks",
			Detail:   fmt.Sprintf("%s contains 'with' blocks but is not a top level resource.", resource.Name()),
			Subject:  resource.GetDeclRange(),
		})
	}
	return diags
}

// validate that the provider does not contains both edges/nodes and a query/sql
// enrich the loaded nodes and edges with the fully parsed resources from the resourceMapProvider
func validateNodeAndEdgeProvider(resource modconfig.NodeAndEdgeProvider) hcl.Diagnostics {
	// TODO [node_reuse] add NodeAndEdgeProviderImpl and move validate there
	// https://github.com/turbot/steampipe/issues/2918

	var diags hcl.Diagnostics
	containsEdgesOrNodes := len(resource.GetEdges())+len(resource.GetNodes()) > 0
	definesQuery := resource.GetSQL() != nil || resource.GetQuery() != nil

	// cannot declare both edges/nodes AND sql/query
	if definesQuery && containsEdgesOrNodes {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  fmt.Sprintf("%s contains edges/nodes AND has a query", resource.Name()),
			Subject:  resource.GetDeclRange(),
		})
	}

	// if resource is NOT top level must have either edges/nodes OR sql/query
	if !resource.IsTopLevel() && !definesQuery && !containsEdgesOrNodes {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  fmt.Sprintf("%s does not define a query or SQL, and has no edges/nodes", resource.Name()),
			Subject:  resource.GetDeclRange(),
		})
	}

	diags = append(diags, validateSqlAndQueryNotBothSet(resource)...)

	diags = append(diags, validateParamAndQueryNotBothSet(resource)...)

	return diags
}

func validateQueryProvider(resource modconfig.QueryProvider) hcl.Diagnostics {
	var diags hcl.Diagnostics

	diags = append(diags, resource.ValidateQuery()...)

	diags = append(diags, validateSqlAndQueryNotBothSet(resource)...)

	diags = append(diags, validateParamAndQueryNotBothSet(resource)...)

	return diags
}

func validateParamAndQueryNotBothSet(resource modconfig.QueryProvider) hcl.Diagnostics {
	var diags hcl.Diagnostics

	// param block cannot be set if a query property is set - it is only valid if inline SQL ids defined
	if len(resource.GetParams()) > 0 {
		if resource.GetQuery() != nil {
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagWarning,
				Summary:  fmt.Sprintf("Deprecated usage: %s has 'query' property set so should not define 'param' blocks", resource.Name()),
				Subject:  resource.GetDeclRange(),
			})
		}
		if !resource.IsTopLevel() && !resource.ParamsInheritedFromBase() {
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagWarning,
				Summary:  "Deprecated usage: Only top level resources can have 'param' blocks",
				Detail:   fmt.Sprintf("%s contains 'param' blocks but is not a top level resource.", resource.Name()),
				Subject:  resource.GetDeclRange(),
			})
		}
	}
	return diags
}

func validateSqlAndQueryNotBothSet(resource modconfig.QueryProvider) hcl.Diagnostics {
	var diags hcl.Diagnostics
	// are both sql and query set?
	if resource.GetSQL() != nil && resource.GetQuery() != nil {
		// either Query or SQL property may be set -  if Query property already set, error
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  fmt.Sprintf("%s has both 'SQL' and 'query' property set - only 1 of these may be set", resource.Name()),
			Subject:  resource.GetDeclRange(),
		})
	}
	return diags
}
