// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package troubleshoot

import (
	"fmt"
	"net"

	envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/troubleshoot/validate"
)

type Troubleshoot struct {
	client         *api.Client
	envoyAddr      net.IPAddr
	envoyAdminPort string

	TroubleshootInfo
}

type TroubleshootInfo struct {
	envoyClusters   *envoy_admin_v3.Clusters
	envoyConfigDump *envoy_admin_v3.ConfigDump
	envoyCerts      *envoy_admin_v3.Certificates
	envoyStats      []*envoy_admin_v3.SimpleMetric
}

func NewTroubleshoot(envoyIP *net.IPAddr, envoyPort string) (*Troubleshoot, error) {
	cfg := api.DefaultConfig()
	c, err := api.NewClient(cfg)
	if err != nil {
		return nil, err
	}

	if envoyIP == nil {
		return nil, fmt.Errorf("envoy address is empty")
	}

	return &Troubleshoot{
		client:         c,
		envoyAddr:      *envoyIP,
		envoyAdminPort: envoyPort,
	}, nil
}

func (t *Troubleshoot) RunAllTests(upstreamEnvoyID, upstreamIP string) (validate.Messages, error) {
	var allTestMessages validate.Messages

	// Get all info from proxy to set up validations.
	err := t.GetEnvoyConfigDump()
	if err != nil {
		return nil, fmt.Errorf("unable to get Envoy config dump: cannot connect to Envoy: %w", err)
	}
	err = t.getEnvoyClusters()
	if err != nil {
		return nil, fmt.Errorf("unable to get Envoy clusters: cannot connect to Envoy: %w", err)
	}
	certs, err := t.getEnvoyCerts()
	if err != nil {
		return nil, fmt.Errorf("unable to get Envoy certificates: cannot connect to Envoy: %w", err)
	}
	indexedResources, err := ProxyConfigDumpToIndexedResources(t.envoyConfigDump)
	if err != nil {
		return nil, fmt.Errorf("unable to index Envoy resources: %w", err)
	}

	// Validate certs.
	messages := t.validateCerts(certs)
	allTestMessages = append(allTestMessages, messages...)
	if errors := messages.Errors(); len(errors) == 0 {
		msg := validate.Message{
			Success: true,
			Message: "Certificates are valid",
		}
		allTestMessages = append(allTestMessages, msg)
	}

	// getStats usage example
	messages, err = t.troubleshootStats()
	if err != nil {
		return nil, fmt.Errorf("unable to get stats: %w", err)
	}
	allTestMessages = append(allTestMessages, messages...)

	// Validate listeners, routes, clusters, endpoints.
	messages = Validate(indexedResources, upstreamEnvoyID, upstreamIP, true, t.envoyClusters)
	allTestMessages = append(allTestMessages, messages...)
	if errors := messages.Errors(); len(errors) == 0 {
		msg := validate.Message{
			Success: true,
			Message: "Upstream resources are valid",
		}
		allTestMessages = append(allTestMessages, msg)
	}

	return allTestMessages, nil
}
