package domain

import (
	"regexp"
	"strings"

	http_util "github.com/zitadel/zitadel/internal/api/http"
	"github.com/zitadel/zitadel/internal/crypto"
	"github.com/zitadel/zitadel/internal/eventstore/v1/models"
	"github.com/zitadel/zitadel/internal/zerrors"
)

type OrgDomain struct {
	models.ObjectRoot

	Domain         string
	Primary        bool
	Verified       bool
	ValidationType OrgDomainValidationType
	ValidationCode *crypto.CryptoValue
}

func (domain *OrgDomain) IsValid() bool {
	return domain.Domain != ""
}

func (domain *OrgDomain) GenerateVerificationCode(codeGenerator crypto.Generator) (string, error) {
	validationCodeCrypto, validationCode, err := crypto.NewCode(codeGenerator)
	if err != nil {
		return "", err
	}
	domain.ValidationCode = validationCodeCrypto
	return validationCode, nil
}

func NewIAMDomainName(orgName, iamDomain string) (string, error) {
	// Reference: label domain requirements https://www.nic.ad.jp/timeline/en/20th/appendix1.html

	// Replaces spaces in org name with hyphens
	label := strings.ReplaceAll(orgName, " ", "-")

	// The label must only contains alphanumeric characters and hyphens
	// Invalid characters are replaced with and empty space but as #6471,
	// as these domains are not used to host ZITADEL, but only for user names,
	// the characters shouldn't matter that much so we'll accept unicode
	// characters, accented characters (\p{L}\p{M}), numbers and hyphens.
	label = string(regexp.MustCompile(`[^\p{L}\p{M}0-9-]`).ReplaceAll([]byte(label), []byte("")))

	// The label cannot exceed 63 characters
	if len(label) > 63 {
		label = label[:63]
	}

	// The total length of the resulting domain can't exceed 253 characters
	domain := label + "." + iamDomain
	if len(domain) > 253 {
		truncateNChars := len(domain) - 253
		label = label[:len(label)-truncateNChars]
	}

	// Label (maybe truncated) can't start with a hyphen
	if len(label) > 0 && label[0:1] == "-" {
		label = label[1:]
	}

	// Label (maybe truncated) can't end with a hyphen
	if len(label) > 0 && label[len(label)-1:] == "-" {
		label = label[:len(label)-1]
	}

	// Empty string should be invalid
	if len(label) > 0 {
		return strings.ToLower(label + "." + iamDomain), nil
	}

	return "", zerrors.ThrowInvalidArgument(nil, "ORG-RrfXY", "Errors.Org.Domain.EmptyString")
}

type OrgDomainValidationType int32

const (
	OrgDomainValidationTypeUnspecified OrgDomainValidationType = iota
	OrgDomainValidationTypeHTTP
	OrgDomainValidationTypeDNS
)

func (t OrgDomainValidationType) CheckType() (http_util.CheckType, bool) {
	switch t {
	case OrgDomainValidationTypeHTTP:
		return http_util.CheckTypeHTTP, true
	case OrgDomainValidationTypeDNS:
		return http_util.CheckTypeDNS, true
	default:
		return -1, false
	}
}

type OrgDomainState int32

const (
	OrgDomainStateUnspecified OrgDomainState = iota
	OrgDomainStateActive
	OrgDomainStateRemoved

	orgDomainStateCount
)

func (f OrgDomainState) Valid() bool {
	return f >= 0 && f < orgDomainStateCount
}