/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>
*/

package config

import (
	"slices"

	"github.com/cgrates/cgrates/utils"
)

type DnsListener struct {
	Address string
	Network string // udp or tcp
}

// DNSAgentCfg the config section that describes the DNS Agent
type DNSAgentCfg struct {
	Enabled           bool
	Listeners         []DnsListener
	SessionSConns     []string
	StatSConns        []string
	ThresholdSConns   []string
	Timezone          string
	RequestProcessors []*RequestProcessor
}

func (da *DNSAgentCfg) loadFromJSONCfg(jsnCfg *DNSAgentJsonCfg, sep string) (err error) {
	if jsnCfg == nil {
		return nil
	}
	if jsnCfg.Enabled != nil {
		da.Enabled = *jsnCfg.Enabled
	}

	if jsnCfg.Listeners != nil {
		da.Listeners = make([]DnsListener, 0, len(*jsnCfg.Listeners))
		for _, listnr := range *jsnCfg.Listeners {
			var ls DnsListener
			if listnr.Address != nil {
				ls.Address = *listnr.Address
			}
			if listnr.Network != nil {
				ls.Network = *listnr.Network
			}
			da.Listeners = append(da.Listeners, ls)
		}
	}
	if jsnCfg.Timezone != nil {
		da.Timezone = *jsnCfg.Timezone
	}
	if jsnCfg.SessionSConns != nil {
		da.SessionSConns = make([]string, len(*jsnCfg.SessionSConns))
		for idx, connID := range *jsnCfg.SessionSConns {
			// if we have the connection internal we change the name so we can have internal rpc for each subsystem
			da.SessionSConns[idx] = connID
			if connID == utils.MetaInternal {
				da.SessionSConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS)
			}
		}
	}
	if jsnCfg.StatSConns != nil {
		da.StatSConns = tagInternalConns(*jsnCfg.StatSConns, utils.MetaStats)
	}
	if jsnCfg.ThresholdSConns != nil {
		da.ThresholdSConns = tagInternalConns(*jsnCfg.ThresholdSConns, utils.MetaThresholds)
	}
	if jsnCfg.RequestProcessors != nil {
		for _, reqProcJsn := range *jsnCfg.RequestProcessors {
			rp := new(RequestProcessor)
			var haveID bool
			for _, rpSet := range da.RequestProcessors {
				if reqProcJsn.ID != nil && rpSet.ID == *reqProcJsn.ID {
					rp = rpSet // Will load data into the one set
					haveID = true
					break
				}
			}
			if err = rp.loadFromJSONCfg(reqProcJsn, sep); err != nil {
				return
			}
			if !haveID {
				da.RequestProcessors = append(da.RequestProcessors, rp)
			}
		}
	}
	return
}

// AsMapInterface returns the config as a map[string]any
func (lstn *DnsListener) AsMapInterface(separator string) map[string]any {
	return map[string]any{
		utils.AddressCfg: lstn.Address,
		utils.NetworkCfg: lstn.Network,
	}

}

// AsMapInterface returns the config as a map[string]any
func (da *DNSAgentCfg) AsMapInterface(sep string) map[string]any {
	listeners := make([]map[string]any, len(da.Listeners))
	for i, item := range da.Listeners {
		listeners[i] = item.AsMapInterface(sep)
	}
	requestProcessors := make([]map[string]any, len(da.RequestProcessors))
	for i, item := range da.RequestProcessors {
		requestProcessors[i] = item.AsMapInterface(sep)
	}
	m := map[string]any{
		utils.EnabledCfg:           da.Enabled,
		utils.ListenersCfg:         listeners,
		utils.TimezoneCfg:          da.Timezone,
		utils.StatSConnsCfg:        stripInternalConns(da.StatSConns),
		utils.ThresholdSConnsCfg:   stripInternalConns(da.ThresholdSConns),
		utils.RequestProcessorsCfg: requestProcessors,
	}
	if da.SessionSConns != nil {
		sessionSConns := make([]string, len(da.SessionSConns))
		for i, item := range da.SessionSConns {
			sessionSConns[i] = item
			if item == utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS) {
				sessionSConns[i] = utils.MetaInternal
			}
		}
		m[utils.SessionSConnsCfg] = sessionSConns
	}
	return m
}

// Clone returns a deep copy of DNSAgentCfg
func (da DNSAgentCfg) Clone() *DNSAgentCfg {
	clone := &DNSAgentCfg{
		Enabled:         da.Enabled,
		Listeners:       slices.Clone(da.Listeners),
		Timezone:        da.Timezone,
		SessionSConns:   slices.Clone(da.SessionSConns),
		StatSConns:      slices.Clone(da.StatSConns),
		ThresholdSConns: slices.Clone(da.ThresholdSConns),
	}
	if da.RequestProcessors != nil {
		clone.RequestProcessors = make([]*RequestProcessor, len(da.RequestProcessors))
		for i, req := range da.RequestProcessors {
			clone.RequestProcessors[i] = req.Clone()
		}
	}
	return clone
}

// RequestProcessor is the request processor configuration
type RequestProcessor struct {
	ID            string
	Tenant        RSRParsers
	Filters       []string
	Flags         utils.FlagsWithParams
	Timezone      string
	RequestFields []*FCTemplate
	ReplyFields   []*FCTemplate
}

func (rp *RequestProcessor) loadFromJSONCfg(jsnCfg *ReqProcessorJsnCfg, sep string) (err error) {
	if jsnCfg == nil {
		return nil
	}
	if jsnCfg.ID != nil {
		rp.ID = *jsnCfg.ID
	}
	if jsnCfg.Filters != nil {
		rp.Filters = make([]string, len(*jsnCfg.Filters))
		copy(rp.Filters, *jsnCfg.Filters)
	}
	if jsnCfg.Flags != nil {
		rp.Flags = utils.FlagsWithParamsFromSlice(*jsnCfg.Flags)
	}
	if jsnCfg.Timezone != nil {
		rp.Timezone = *jsnCfg.Timezone
	}
	if jsnCfg.Tenant != nil {
		if rp.Tenant, err = NewRSRParsers(*jsnCfg.Tenant, sep); err != nil {
			return err
		}
	}
	if jsnCfg.Request_fields != nil {
		if rp.RequestFields, err = FCTemplatesFromFCTemplatesJSONCfg(*jsnCfg.Request_fields, sep); err != nil {
			return
		}
	}
	if jsnCfg.Reply_fields != nil {
		if rp.ReplyFields, err = FCTemplatesFromFCTemplatesJSONCfg(*jsnCfg.Reply_fields, sep); err != nil {
			return
		}
	}
	return nil
}

// AsMapInterface returns the config as a map[string]any
func (rp *RequestProcessor) AsMapInterface(separator string) (initialMP map[string]any) {
	initialMP = map[string]any{
		utils.IDCfg:       rp.ID,
		utils.FiltersCfg:  rp.Filters,
		utils.FlagsCfg:    rp.Flags.SliceFlags(),
		utils.TimezoneCfg: rp.Timezone,
	}
	if rp.Tenant != nil {
		initialMP[utils.TenantCfg] = rp.Tenant.GetRule(separator)
	}
	if rp.RequestFields != nil {
		requestFields := make([]map[string]any, len(rp.RequestFields))
		for i, item := range rp.RequestFields {
			requestFields[i] = item.AsMapInterface(separator)
		}
		initialMP[utils.RequestFieldsCfg] = requestFields
	}
	if rp.ReplyFields != nil {
		replyFields := make([]map[string]any, len(rp.ReplyFields))
		for i, item := range rp.ReplyFields {
			replyFields[i] = item.AsMapInterface(separator)
		}
		initialMP[utils.ReplyFieldsCfg] = replyFields
	}
	return
}

// Clone returns a deep copy of APIBanCfg
func (rp RequestProcessor) Clone() (cln *RequestProcessor) {
	cln = &RequestProcessor{
		ID:       rp.ID,
		Tenant:   rp.Tenant.Clone(),
		Flags:    rp.Flags.Clone(),
		Timezone: rp.Timezone,
	}
	if rp.Filters != nil {
		cln.Filters = make([]string, len(rp.Filters))
		copy(cln.Filters, rp.Filters)

	}
	if rp.RequestFields != nil {
		cln.RequestFields = make([]*FCTemplate, len(rp.RequestFields))
		for i, rf := range rp.RequestFields {
			cln.RequestFields[i] = rf.Clone()
		}
	}
	if rp.ReplyFields != nil {
		cln.ReplyFields = make([]*FCTemplate, len(rp.ReplyFields))
		for i, rf := range rp.ReplyFields {
			cln.ReplyFields[i] = rf.Clone()
		}
	}
	return
}
