package hetzner

import (
	"strings"

	"github.com/DNSControl/dnscontrol/v4/models"
	"github.com/DNSControl/dnscontrol/v4/pkg/txtutil"
)

type bulkCreateRecordsRequest struct {
	Records []record `json:"records"`
}

type bulkUpdateRecordsRequest struct {
	Records []record `json:"records"`
}

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

type createZoneResponse struct {
	Zone zone `json:"zone"`
}

type getAllRecordsResponse struct {
	Records []record `json:"records"`
	Meta    struct {
		Pagination struct {
			LastPage     int `json:"last_page"`
			TotalEntries int `json:"total_entries"`
		} `json:"pagination"`
	} `json:"meta"`
}

type getAllZonesResponse struct {
	Zones []zone `json:"zones"`
	Meta  struct {
		Pagination struct {
			LastPage     int `json:"last_page"`
			TotalEntries int `json:"total_entries"`
		} `json:"pagination"`
	} `json:"meta"`
}

type record struct {
	ID     string  `json:"id"`
	Name   string  `json:"name"`
	TTL    *uint32 `json:"ttl"`
	Type   string  `json:"type"`
	Value  string  `json:"value"`
	ZoneID string  `json:"zone_id"`
}

type zone struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	NameServers []string `json:"ns"`
	TTL         uint32   `json:"ttl"`
}

func fromRecordConfig(in *models.RecordConfig, zone zone) record {
	r := record{
		Name:   in.GetLabel(),
		Type:   in.Type,
		Value:  in.GetTargetCombinedFunc(txtutil.EncodeQuoted),
		TTL:    &in.TTL,
		ZoneID: zone.ID,
	}

	if r.Type == "TXT" && (in.GetTargetTXTSegmentCount() == 1) {
		// HACK: HETZNER rejects values that fit into 255 bytes w/o quotes,
		//  but do not fit w/ added quotes (via GetTargetCombined()).
		// Sending the raw, non-quoted value works for the comprehensive
		//  suite of integrations tests.
		// The HETZNER validation does not provide helpful error messages.
		// {"error":{"message":"422 Unprocessable Entity: missing: ; ","code":422}}
		// Last checked: 2023-04-01
		valueNotQuoted := in.GetTargetTXTSegmented()[0]
		if len(valueNotQuoted) == 254 || len(valueNotQuoted) == 255 {
			r.Value = valueNotQuoted
		}
	}

	return r
}

func toRecordConfig(domain string, r *record) (*models.RecordConfig, error) {
	rc := models.RecordConfig{
		Type:     r.Type,
		TTL:      *r.TTL,
		Original: r,
	}
	if strings.HasSuffix(r.Name, "."+domain+".") {
		// Records created through other tools or the browser UI can contain FQDN labels.
		rc.SetLabelFromFQDN(r.Name, domain)
	} else {
		rc.SetLabel(r.Name, domain)
	}

	// HACK: Hetzner is inserting a trailing space after multiple, quoted values.
	// NOTE: The actual DNS answer does not contain the space.
	// NOTE: The txtutil.ParseQuoted parser handles this just fine.
	// Last checked: 2023-04-01
	return &rc, rc.PopulateFromStringFunc(r.Type, r.Value, domain, txtutil.ParseQuoted)
}
