package ruleio

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/prism/proxy/internal/store"
)

const ExportVersion = 1

type ExportDoc struct {
	Version int          `json:"version"`
	Rules   []ExportRule `json:"rules"`
}

type ExportRule struct {
	Priority int    `json:"priority"`
	RuleType string `json:"rule_type"`
	Payload  string `json:"payload"`
	Target   string `json:"target"`
	Enabled  *bool  `json:"enabled,omitempty"`
}

type ImportResult struct {
	Imported int      `json:"imported"`
	Skipped  int      `json:"skipped"`
	Errors   []string `json:"errors,omitempty"`
}

func ruleEnabled(r ExportRule) bool {
	if r.Enabled == nil {
		return true
	}
	return *r.Enabled
}

func ToExportRules(rules []store.Rule) []ExportRule {
	out := make([]ExportRule, 0, len(rules))
	for _, r := range rules {
		enabled := r.Enabled
		out = append(out, ExportRule{
			Priority: r.Priority,
			RuleType: r.RuleType,
			Payload:  r.Payload,
			Target:   r.Target,
			Enabled:  &enabled,
		})
	}
	return out
}

func ExportJSON(rules []store.Rule) ([]byte, error) {
	doc := ExportDoc{Version: ExportVersion, Rules: ToExportRules(rules)}
	return json.MarshalIndent(doc, "", "  ")
}

func ExportText(rules []store.Rule) string {
	var b strings.Builder
	for _, r := range rules {
		if !r.Enabled {
			continue
		}
		b.WriteString(formatRuleLine(r))
		b.WriteByte('\n')
	}
	return b.String()
}

func formatRuleLine(r store.Rule) string {
	if r.Payload == "" {
		return fmt.Sprintf("%s,%s", r.RuleType, r.Target)
	}
	return fmt.Sprintf("%s,%s,%s", r.RuleType, r.Payload, r.Target)
}

func ParseJSON(data []byte) ([]ExportRule, error) {
	var doc ExportDoc
	if err := json.Unmarshal(data, &doc); err == nil && (len(doc.Rules) > 0 || doc.Version > 0) {
		return doc.Rules, nil
	}
	var rules []ExportRule
	if err := json.Unmarshal(data, &rules); err != nil {
		return nil, fmt.Errorf("invalid JSON: %w", err)
	}
	return rules, nil
}

func ParseText(text string) ([]ExportRule, []string) {
	lines := strings.Split(text, "\n")
	var out []ExportRule
	var errs []string
	for i, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		r, err := ParseLine(line)
		if err != nil {
			errs = append(errs, fmt.Sprintf("第 %d 行: %v", i+1, err))
			continue
		}
		out = append(out, r)
	}
	return out, errs
}

func ParseLine(line string) (ExportRule, error) {
	parts := strings.Split(line, ",")
	if len(parts) < 2 {
		return ExportRule{}, fmt.Errorf("格式无效 %q", line)
	}
	ruleType := strings.TrimSpace(strings.ToUpper(parts[0]))
	if ruleType == "MATCH" {
		return ExportRule{}, fmt.Errorf("跳过 MATCH 规则")
	}
	target := strings.TrimSpace(parts[len(parts)-1])
	if target == "" {
		return ExportRule{}, fmt.Errorf("缺少出站目标")
	}
	payload := ""
	if len(parts) > 2 {
		payload = strings.Join(parts[1:len(parts)-1], ",")
	}
	if needsPayload(ruleType) && payload == "" {
		return ExportRule{}, fmt.Errorf("%s 需要匹配值", ruleType)
	}
	enabled := true
	return ExportRule{
		RuleType: ruleType,
		Payload:  payload,
		Target:   target,
		Enabled:  &enabled,
	}, nil
}

func needsPayload(ruleType string) bool {
	switch ruleType {
	case "DIRECT", "REJECT", "REJECT-DROP", "MATCH":
		return false
	default:
		return true
	}
}

func ToStoreRules(rules []ExportRule) ([]store.Rule, []string) {
	out := make([]store.Rule, 0, len(rules))
	var errs []string
	for i, r := range rules {
		ruleType := strings.TrimSpace(strings.ToUpper(r.RuleType))
		target := strings.TrimSpace(r.Target)
		if ruleType == "" || ruleType == "MATCH" {
			errs = append(errs, fmt.Sprintf("规则 %d: 类型无效", i+1))
			continue
		}
		if target == "" {
			errs = append(errs, fmt.Sprintf("规则 %d: 缺少出站目标", i+1))
			continue
		}
		payload := strings.TrimSpace(r.Payload)
		if needsPayload(ruleType) && payload == "" {
			errs = append(errs, fmt.Sprintf("规则 %d: %s 需要匹配值", i+1, ruleType))
			continue
		}
		out = append(out, store.Rule{
			Priority: r.Priority,
			RuleType: ruleType,
			Payload:  payload,
			Target:   target,
			Source:   "manual",
			Enabled:  ruleEnabled(r),
		})
	}
	return out, errs
}

func AssignPriorities(rules []store.Rule, start int) {
	if start <= 0 {
		start = 100
	}
	step := 10
	for i := range rules {
		if rules[i].Priority <= 0 {
			rules[i].Priority = start
			start += step
		}
	}
}
