package singbox

import (
	"strconv"

	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/rules"
)

type Rule struct {
	Outbound      string   `json:"outbound,omitempty"`
	ClashMode     string   `json:"clash_mode,omitempty"`
	RuleSet       []string `json:"rule_set,omitempty"`
	Domain        []string `json:"domain,omitempty"`
	DomainSuffix  []string `json:"domain_suffix,omitempty"`
	DomainKeyword []string `json:"domain_keyword,omitempty"`
	DomainRegex   []string `json:"domain_regex,omitempty"`
	GeoIP         []string `json:"geoip,omitempty"`
	IPCIDR        []string `json:"ip_cidr,omitempty"`
	IPIsPrivate   bool     `json:"ip_is_private,omitempty"`
	SourceIPCIDR  []string `json:"source_ip_cidr,omitempty"`
	ProcessName   []string `json:"process_name,omitempty"`
	ProcessPath   []string `json:"process_path,omitempty"`
	SourcePort    []uint16 `json:"source_port,omitempty"`
	Protocol      []string `json:"protocol,omitempty"`
	Port          []uint16 `json:"port,omitempty"`
	Action        string   `json:"action,omitempty"`
	Inbound       []string `json:"inbound,omitempty"`
	Rules         []Rule   `json:"rules,omitempty"`
	Type          string   `json:"type,omitempty"`
	Mode          string   `json:"mode,omitempty"`
}

type RuleSet struct {
	Tag            string `json:"tag,omitempty"`
	Type           string `json:"type,omitempty"`
	Format         string `json:"format,omitempty"`
	URL            string `json:"url,omitempty"`
	DownloadDetour string `json:"download_detour,omitempty"`
}

func adapterToSingboxRule(texts []string) []Rule {
	var rulesList []Rule
	for _, rule := range texts {
		r := rules.NewRule(rule, "")
		if r == nil {
			continue
		}
		rulesList = addRuleToItem(rulesList, r.Target, *r)
	}
	return rulesList
}

func addRuleToItem(group []Rule, outbound string, rule rules.Rule) []Rule {
	for i := range group {
		if group[i].Outbound == outbound {
			switch rules.ParseRuleType(rule.Type) {
			case rules.Domain:
				group[i].Domain = append(group[i].Domain, rule.Payload)
				return group
			case rules.DomainSuffix:
				group[i].DomainSuffix = append(group[i].DomainSuffix, rule.Payload)
				return group
			case rules.DomainKeyword:
				group[i].DomainKeyword = append(group[i].DomainKeyword, rule.Payload)
				return group
			case rules.IPCIDR:
				group[i].IPCIDR = append(group[i].IPCIDR, rule.Payload)
				return group
			case rules.SrcIPCIDR:
				group[i].SourceIPCIDR = append(group[i].SourceIPCIDR, rule.Payload)
				return group
			case rules.SrcPort:
				port, err := strconv.ParseUint(rule.Payload, 10, 16)
				if err != nil {
					logger.Errorf("[adapterToSingboxRule] failed to parse port %s to uint16", rule.Payload)
					return group
				}
				group[i].SourcePort = append(group[i].SourcePort, uint16(port))
				return group
			case rules.GEOIP:
				group[i].GeoIP = append(group[i].GeoIP, rule.Payload)
				return group
			case rules.Process:
				group[i].ProcessName = append(group[i].ProcessName, rule.Payload)
				return group
			case rules.ProcessPath:
				group[i].ProcessPath = append(group[i].ProcessPath, rule.Payload)
				return group
			default:
				logger.Errorf("[adapterToSingboxRule] unknown rule type %s", rule.Type)
				return group
			}
		}
	}
	newRule := Rule{
		Outbound: outbound,
	}

	switch rules.ParseRuleType(rule.Type) {
	case rules.Domain:
		newRule.Domain = []string{rule.Payload}
	case rules.DomainSuffix:
		newRule.DomainSuffix = []string{rule.Payload}
	case rules.DomainKeyword:
		newRule.DomainKeyword = []string{rule.Payload}
	case rules.IPCIDR:
		newRule.IPCIDR = []string{rule.Payload}
	case rules.SrcIPCIDR:
		newRule.SourceIPCIDR = []string{rule.Payload}
	case rules.SrcPort:
		port, err := strconv.ParseUint(rule.Payload, 10, 16)
		if err != nil {
			logger.Errorf("[adapterToSingboxRule] failed to parse port %s to uint16", rule.Payload)
			return group
		}
		newRule.SourcePort = []uint16{uint16(port)}
	case rules.GEOIP:
		newRule.GeoIP = []string{rule.Payload}
	case rules.Process:
		newRule.ProcessName = []string{rule.Payload}
	case rules.ProcessPath:
		newRule.ProcessPath = []string{rule.Payload}
	default:
		logger.Errorf("[adapterToSingboxRule] unknown rule type %s", rule.Type)
		return group
	}
	group = append(group, newRule)
	return group
}
