package util

import (
	"encoding/json"
	"fmt"
	"maps"
	"os"
	"strings"

	corev1 "k8s.io/api/core/v1"
)

// PareseAWSLBName pares the lb name from host
// ref: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/elb/create-load-balancer.html#examples
// "DNSName": "my-load-balancer-123456789.us-west-2.elb.amazonaws.com"
// example:
// k8s-tidbmigr-dbtidb-cd6732f3cb-d18a37835a48aeb5.elb.us-west-2.amazonaws.com
// name should be: k8s-tidbmigr-dbtidb-cd6732f3cb
func PareseAWSLBName(hostname string) (name string, err error) {
	parts := strings.Split(hostname, ".")
	parts = strings.Split(parts[0], "-")

	if len(parts) < 2 {
		return "", fmt.Errorf("invalid hostname %s", hostname)
	}
	name = strings.Join(parts[:len(parts)-1], "-")

	return name, nil
}

func GetAWSLBNameFromSVC(svc *corev1.Service) (name string, err error) {
	if len(svc.Status.LoadBalancer.Ingress) == 0 {
		return "", fmt.Errorf("no ingress for svc %s/%s", svc.Namespace, svc.Name)
	}

	host := svc.Status.LoadBalancer.Ingress[0].Hostname
	parts := strings.Split(host, ".")
	parts = strings.Split(parts[0], "-")
	name = strings.Join(parts[:len(parts)-1], "-")

	return name, nil
}

// PolicyDocument defines a policy document as a Go struct that can be serialized
// to JSON.
// Note: PolicyDocument is a subset of [AWS IAM policy document](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html).
// Please ensure all fields in your policy document are supported by this struct.
type PolicyDocument struct {
	Version   string             `json:"Version"`
	Statement []*PolicyStatement `json:"Statement"`
}

func (p *PolicyDocument) ToString() (string, error) {
	if p == nil {
		return "", fmt.Errorf("policy document is nil")
	}

	b, err := json.Marshal(p)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func (p *PolicyDocument) FindStatementBySid(sid string) (*PolicyStatement, bool) {
	for _, s := range p.Statement {
		if s.Sid == sid {
			return s, true
		}
	}
	return nil, false
}

// PolicyStatement defines a statement in a policy document.
type PolicyStatement struct {
	Sid       string              `json:"Sid,omitempty"`
	Effect    string              `json:"Effect"`
	Action    []string            `json:"Action"`
	Principal *PolicyPrincipal    `json:"Principal,omitempty"`
	Resource  []string            `json:"Resource,omitempty"`
	Condition *StatementCondition `json:"Condition,omitempty"`
}

func (s *PolicyStatement) AppendResource(resource string) {
	for _, r := range s.Resource {
		if r == resource {
			return
		}
	}
	s.Resource = append(s.Resource, resource)
}

type StatementCondition struct {
	StringLike   map[string]string `json:"StringLike,omitempty"`
	StringEquals map[string]string `json:"StringEquals,omitempty"`
}

type PolicyPrincipal struct {
	Federated string   `json:"Federated,omitempty"`
	Service   string   `json:"Service,omitempty"`
	AWS       []string `json:"AWS,omitempty"`
}

func GetAWSPolicyFromFile(path string) (string, error) {
	if path == "" {
		return "", fmt.Errorf("policy config file unspecified")
	}
	//nolint:gosec // path is from configuration file, not user input
	data, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	// var doc PolicyDocument
	// if err := json.Unmarshal(data, &doc); err != nil {
	// 	return "", err
	// }
	return string(data), nil
}

// BuildAWSTagWithName adds a "Name" tag to the tags map.
// AWS uses name tag as the resource name in the console.
// This function returns a new map without modifying the input.
func BuildAWSTagWithName(tags map[string]string, name string) map[string]string {
	result := maps.Clone(tags)
	result["Name"] = name
	return result
}
