package domain

import (
	"context"
	"fmt"
	"net"
	"strings"

	"github.com/porter-dev/porter/internal/encryption"
	"github.com/porter-dev/porter/internal/integrations/powerdns"
	"github.com/porter-dev/porter/internal/models"
	v1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// GetNGINXIngressServiceIP retrieves the external address of the nginx-ingress service
func GetNGINXIngressServiceIP(clientset kubernetes.Interface) (string, bool, error) {
	svcList, err := clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{
		LabelSelector: "app.kubernetes.io/managed-by=Helm",
	})
	if err != nil {
		return "", false, err
	}

	var nginxSvc *v1.Service
	exists := false

	for _, svc := range svcList.Items {
		// check that helm chart annotation is correct exists
		if chartAnn, found := svc.ObjectMeta.Labels["helm.sh/chart"]; found {
			if (strings.Contains(chartAnn, "ingress-nginx") || strings.Contains(chartAnn, "nginx-ingress")) && svc.Spec.Type == v1.ServiceTypeLoadBalancer {
				nginxSvc = &svc
				exists = true
				break
			}
		}
	}

	if !exists {
		// look for alternate services/names (just Azure for now)
		svcList, err = clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{
			LabelSelector: "app=addon-http-application-routing-nginx-ingress",
		})

		if err != nil {
			return "", false, err
		}

		for _, svc := range svcList.Items {
			// check that the service is type load balancer
			if svc.Spec.Type == v1.ServiceTypeLoadBalancer {
				nginxSvc = &svc
				exists = true
				break
			}
		}

		if !exists {
			return "", false, nil
		}
	}

	if ipArr := nginxSvc.Status.LoadBalancer.Ingress; len(ipArr) > 0 {
		// first default to ip, then check hostname
		if ipArr[0].IP != "" {
			return ipArr[0].IP, true, nil
		} else if ipArr[0].Hostname != "" {
			return ipArr[0].Hostname, true, nil
		}
	}

	return "", false, nil
}

// DNSRecord wraps the gorm DNSRecord model
type DNSRecord models.DNSRecord

type CreateDNSRecordConfig struct {
	ReleaseName string
	RootDomain  string
	Endpoint    string
}

// NewDNSRecordForEndpoint generates a random subdomain and returns a DNSRecord
// model
func (c *CreateDNSRecordConfig) NewDNSRecordForEndpoint() *models.DNSRecord {
	suffix, _ := encryption.GenerateRandomBytes(8)

	subdomain := fmt.Sprintf("%s-%s", c.ReleaseName, suffix)

	return &models.DNSRecord{
		SubdomainPrefix: subdomain,
		RootDomain:      c.RootDomain,
		Endpoint:        c.Endpoint,
		Hostname:        fmt.Sprintf("%s.%s", subdomain, c.RootDomain),
	}
}

// CreateDomain creates a new record for the vanity domain
func (e *DNSRecord) CreateDomain(powerDNSClient *powerdns.Client) error {
	isIPv4 := net.ParseIP(e.Endpoint) != nil
	domain := fmt.Sprintf("%s.%s", e.SubdomainPrefix, e.RootDomain)

	if isIPv4 {
		return powerDNSClient.CreateARecord(e.Endpoint, domain)
	}

	return powerDNSClient.CreateCNAMERecord(e.Endpoint, domain)
}
