package dnsmadeeasy

import (
	"strconv"

	"github.com/StackExchange/dnscontrol/v3/models"
)

// DNS Made Easy does not allow the system name servers to be edited, and said records appear to always have a fixed TTL of 86400.
const fixedNameServerRecordTTL = 86400

type singleDomainResponse struct {
	ID                  int                              `json:"id"`
	Name                string                           `json:"name"`
	DelegateNameServers []string                         `json:"delegateNameServers"`
	NameServers         []singleDomainResponseNameServer `json:"nameServers"`
	ProcessMulti        bool                             `json:"processMulti"`
	ActiveThirdParties  []interface{}                    `json:"activeThirdParties"`
	PendingActionID     int                              `json:"pendingActionId"`
	GtdEnabled          bool                             `json:"gtdEnabled"`
	Created             int64                            `json:"created"`
	Updated             int64                            `json:"updated"`
}

type singleDomainResponseNameServer struct {
	Fqdn string `json:"fqdn"`
	Ipv4 string `json:"ipv4"`
	Ipv6 string `json:"ipv6"`
}

type singleDomainRequestData struct {
	Name string `json:"name"`
}

type multiDomainResponse struct {
	TotalRecords int                            `json:"totalRecords"`
	TotalPages   int                            `json:"totalPages"`
	Data         []multiDomainResponseDataEntry `json:"data"`
	Page         int                            `json:"page"`
}

type multiDomainResponseDataEntry struct {
	ID                 int           `json:"id"`
	Name               string        `json:"name"`
	FolderID           int           `json:"folderId"`
	GtdEnabled         bool          `json:"gtdEnabled"`
	ProcessMulti       bool          `json:"processMulti"`
	ActiveThirdParties []interface{} `json:"activeThirdParties"`
	PendingActionID    int           `json:"pendingActionId"`
	VanityID           int           `json:"vanityId,omitempty"`
	Created            int64         `json:"created"`
	Updated            int64         `json:"updated"`
}

type recordResponse struct {
	TotalRecords int                       `json:"totalRecords"`
	TotalPages   int                       `json:"totalPages"`
	Data         []recordResponseDataEntry `json:"data"`
	Page         int                       `json:"page"`
}

type recordResponseDataEntry struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Type  string `json:"type"`
	Value string `json:"value"`
	TTL   int    `json:"ttl"`

	Source   int `json:"source"`
	SourceID int `json:"sourceId"`

	DynamicDNS bool   `json:"dynamicDns"`
	Password   string `json:"password"`

	// A records
	Monitor  bool `json:"monitor"`
	Failover bool `json:"failover"`
	Failed   bool `json:"failed"`

	// Global Traffic Director
	GtdLocation string `json:"gtdLocation"`

	// HTTPRED records
	Description  string `json:"description"`
	Keywords     string `json:"keywords"`
	Title        string `json:"title"`
	RedirectType string `json:"redirectType"`
	HardLink     bool   `json:"hardLink"`

	// MX records
	MxLevel int `json:"mxLevel"`

	// SRV records
	Weight   int `json:"weight"`
	Priority int `json:"Priority"`
	Port     int `json:"port"`

	// CAA records
	CaaType        string `json:"caaType"`
	IssuerCritical int    `json:"issuerCritical"`
}

type recordRequestData struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Type  string `json:"type"`
	Value string `json:"value"`
	TTL   int    `json:"ttl"`

	// Global Traffic Director
	GtdLocation string `json:"gtdLocation"`

	// MX records
	MxLevel int `json:"mxLevel"`

	// SRV records
	Weight   int `json:"weight,omitempty"`
	Priority int `json:"priority,omitempty"`
	Port     int `json:"port,omitempty"`

	// CAA records
	CaaType        string `json:"caaType"`
	IssuerCritical int    `json:"issuerCritical"`
}

func toRecordConfig(domain string, record *recordResponseDataEntry) *models.RecordConfig {
	rc := &models.RecordConfig{
		Type:     record.Type,
		TTL:      uint32(record.TTL),
		Original: record,
	}

	rc.SetLabel(record.Name, domain)

	var err error
	if record.Type == "MX" {
		err = rc.SetTargetMX(uint16(record.MxLevel), record.Value)
	} else if record.Type == "SRV" {
		err = rc.SetTargetSRV(uint16(record.Priority), uint16(record.Weight), uint16(record.Port), record.Value)
	} else if record.Type == "CAA" {
		value, unquoteErr := strconv.Unquote(record.Value)
		if unquoteErr != nil {
			panic(err)
		}
		err = rc.SetTargetCAA(uint8(record.IssuerCritical), record.CaaType, value)
	} else {
		err = rc.PopulateFromString(record.Type, record.Value, domain)
	}

	if err != nil {
		panic(err)
	}

	return rc
}

func fromRecordConfig(rc *models.RecordConfig) *recordRequestData {
	label := rc.GetLabel()
	if label == "@" {
		label = ""
	}

	record := &recordRequestData{
		Type:        rc.Type,
		TTL:         int(rc.TTL),
		GtdLocation: "DEFAULT",
		Name:        label,
		Value:       rc.GetTargetCombined(),
	}

	if record.Type == "MX" {
		record.MxLevel = int(rc.MxPreference)
		record.Value = rc.GetTargetField()
	} else if record.Type == "SRV" {
		target := rc.GetTargetField()
		if target == "." {
			target += "."
		}

		record.Priority = int(rc.SrvPriority)
		record.Weight = int(rc.SrvWeight)
		record.Port = int(rc.SrvPort)
		record.Value = target
	} else if record.Type == "CAA" {
		record.IssuerCritical = int(rc.CaaFlag)
		record.CaaType = rc.CaaTag
		record.Value = rc.GetTargetField()
	}

	return record
}

func systemNameServerToRecordConfig(domain string, nameServer string) *models.RecordConfig {
	target := nameServer + "."
	return toRecordConfig(domain, &recordResponseDataEntry{Type: "NS", Value: target, TTL: fixedNameServerRecordTTL})
}
