package config

import (
	"regexp"

	"github.com/filtr/infrastructure/go/openvpn-route-updater/internal/logger"
	"github.com/filtr/infrastructure/go/openvpn-route-updater/internal/utility"
)

// Zone - object which contain info about dns zone name and which record filters should be applied
type Zone struct {
	Name          string   `yaml:"zoneName"`
	RecordFilters []string `yaml:"recordFilters"`
	// Not mandatory, if not define it will be set to default during initialization
	AwsProfile string `yaml:"awsProfile"`
}

// GroupConfig - Configuration of routes for a group
type GroupConfig struct {
	Name               string   `yaml:"groupName"`
	SingleRecords      []string `yaml:"singleRecords"`
	AllowedRecordTypes []string `yaml:"allowedRecordTypes"`
	Zones              []Zone   `yaml:"zones"`
}

// GroupConfigManager - operates on groupconfig object
type GroupConfigManager struct {
	log   logger.Logger
	Group *GroupConfig
}

// SetLogger - set logger -_-
func (c *GroupConfigManager) SetLogger(log logger.Logger) {
	c.log = log
}

// CheckZonePresense check whether zone with given name exists in the group config
func (c GroupConfigManager) CheckZonePresense(zoneName string) bool {
	for _, zone := range c.Group.Zones {
		if zoneName == zone.Name {
			return true
		}
	}
	return false
}

// GetZoneFilters - return list of filters for zone (with escaped zone name added to them)
func (c GroupConfigManager) GetZoneFilters(zoneName string) []regexp.Regexp {
	var filters []regexp.Regexp
	var zoneEscapedName = "\\.?"
	for i := 0; i < len(zoneName); i++ {
		if string(zoneName[i]) == "." {
			zoneEscapedName = zoneEscapedName + string("\\.")
		} else {
			zoneEscapedName += string(zoneName[i])
		}
	}
	c.log.Debug(zoneEscapedName)
	for _, zone := range c.Group.Zones {
		if zone.Name == zoneName {
			for _, filter := range zone.RecordFilters {
				c.log.Debugf("Evaluating regexp %v", zoneEscapedName)
				re := regexp.MustCompile(filter + zoneEscapedName)
				filters = append(filters, *re)
			}
			c.log.Debug(filters)
			return filters
		}
	}
	c.log.Warnf("No filters have been found for the zone %v", zoneName)
	return nil
}

// CheckRecordType - check whether record type is in the list of allowed record types in the config
func (c GroupConfigManager) CheckRecordType(recordType string) bool {
	for _, allowedType := range c.Group.AllowedRecordTypes {
		if recordType == allowedType {
			c.log.Debugf("%v is in the list of allowed types", recordType)
			return true
		}
	}
	return false
}

// CheckRecordName - check whether record name falls under any of filters passed to the function
func (c GroupConfigManager) CheckRecordName(recordName string, zoneName string) bool {
	if zoneName == "" {
		c.log.Errorf("Incorrect record name %v", recordName)
	}
	filters := c.GetZoneFilters(zoneName)
	for _, filter := range filters {
		if filter.MatchString(recordName) {
			c.log.Debugf("%v matched filter %v", recordName, filter.String())
			return true
		}
	}
	c.log.Debugf("No matches for %v", recordName)
	return false
}

// Append settings from another group to current one.
func (c *GroupConfigManager) Append(group *GroupConfig) {
	for _, appenderZone := range group.Zones {
		zoneAlreadyInConfig := false
		for i := range c.Group.Zones {
			if appenderZone.Name == c.Group.Zones[i].Name {
				zoneAlreadyInConfig = true
				c.Group.Zones[i].RecordFilters = append(c.Group.Zones[i].RecordFilters, appenderZone.RecordFilters...)
				c.Group.Zones[i].RecordFilters = utility.Unique(c.Group.Zones[i].RecordFilters)
			}
		}
		if !zoneAlreadyInConfig {
			c.Group.Zones = append(c.Group.Zones, appenderZone)
		}
	}
	c.Group.SingleRecords = append(c.Group.SingleRecords, group.SingleRecords...)
	c.Group.SingleRecords = utility.Unique(c.Group.SingleRecords)
	c.Group.AllowedRecordTypes = append(c.Group.AllowedRecordTypes, group.AllowedRecordTypes...)
	c.Group.AllowedRecordTypes = utility.Unique(c.Group.AllowedRecordTypes)
}
