package api

import (
	"io"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"

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

func (s *Server) exportRules(c *gin.Context) {
	ctx := c.Request.Context()
	items, err := s.app.Store.ListRules(ctx, "manual")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	format := strings.ToLower(c.DefaultQuery("format", "json"))
	filename := "prism-rules"
	switch format {
	case "text", "txt", "clash":
		c.Header("Content-Type", "text/plain; charset=utf-8")
		c.Header("Content-Disposition", `attachment; filename="`+filename+`.txt"`)
		c.String(http.StatusOK, ruleio.ExportText(items))
	default:
		data, err := ruleio.ExportJSON(items)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.Header("Content-Type", "application/json; charset=utf-8")
		c.Header("Content-Disposition", `attachment; filename="`+filename+`.json"`)
		c.Data(http.StatusOK, "application/json; charset=utf-8", data)
	}
}

type importRulesReq struct {
	Mode  string              `json:"mode"`
	Text  string              `json:"text"`
	Rules []ruleio.ExportRule `json:"rules"`
}

func (s *Server) importRules(c *gin.Context) {
	ctx := c.Request.Context()
	mode := strings.ToLower(strings.TrimSpace(c.DefaultQuery("mode", "")))

	var parsed []ruleio.ExportRule
	var parseErrors []string

	contentType := c.GetHeader("Content-Type")
	if strings.Contains(contentType, "application/json") {
		var req importRulesReq
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		if mode == "" {
			mode = strings.ToLower(strings.TrimSpace(req.Mode))
		}
		if len(req.Rules) > 0 {
			parsed = req.Rules
		} else if strings.TrimSpace(req.Text) != "" {
			parsed, parseErrors = parseImportText(req.Text)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": "rules or text required"})
			return
		}
	} else {
		body, err := io.ReadAll(c.Request.Body)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		if mode == "" {
			mode = "append"
		}
		parsed, parseErrors = parseImportText(string(body))
	}

	if mode == "" {
		mode = "append"
	}
	if mode != "append" && mode != "replace" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "mode must be append or replace"})
		return
	}
	if len(parsed) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no valid rules", "errors": parseErrors})
		return
	}

	rules, valErrors := ruleio.ToStoreRules(parsed)
	allErrors := append(parseErrors, valErrors...)
	if len(rules) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no valid rules", "errors": allErrors})
		return
	}

	if mode == "append" {
		start, _ := s.app.Store.NextManualRulePriority(ctx)
		ruleio.AssignPriorities(rules, start)
		if err := s.app.Store.AppendManualRules(ctx, rules); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
	} else {
		ruleio.AssignPriorities(rules, 100)
		if err := s.app.Store.ReplaceManualRules(ctx, rules); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
	}

	_ = s.app.Store.AddAuditLog(ctx, "import", "rules", mode+": "+strconv.Itoa(len(rules)))
	_ = s.app.ReloadEngine(ctx)

	c.JSON(http.StatusOK, gin.H{
		"ok":       true,
		"imported": len(rules),
		"skipped":  len(allErrors),
		"errors":   allErrors,
		"mode":     mode,
	})
}

func parseImportText(text string) ([]ruleio.ExportRule, []string) {
	text = strings.TrimSpace(text)
	if text == "" {
		return nil, nil
	}
	if strings.HasPrefix(text, "{") || strings.HasPrefix(text, "[") {
		rules, err := ruleio.ParseJSON([]byte(text))
		if err == nil && len(rules) > 0 {
			return rules, nil
		}
	}
	return ruleio.ParseText(text)
}
