package api

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

	"github.com/gin-gonic/gin"

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

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

	format := strings.ToLower(c.DefaultQuery("format", "json"))
	filename := "prism-proxies"
	switch format {
	case "text", "txt", "links":
		c.Header("Content-Type", "text/plain; charset=utf-8")
		c.Header("Content-Disposition", `attachment; filename="`+filename+`.txt"`)
		c.String(http.StatusOK, proxyio.ExportLinks(items))
	default:
		data, err := proxyio.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 importProxiesReq struct {
	Mode  string              `json:"mode"`
	Text  string              `json:"text"`
	Nodes []proxyio.ExportNode `json:"nodes"`
}

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

	var nodes []store.ProxyNode
	var parseErrors []string

	contentType := c.GetHeader("Content-Type")
	if strings.Contains(contentType, "application/json") {
		var req importProxiesReq
		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.Nodes) > 0 {
			var valErrors []string
			nodes, valErrors = proxyio.ToStoreNodes(req.Nodes)
			parseErrors = append(parseErrors, valErrors...)
		} else if strings.TrimSpace(req.Text) != "" {
			nodes, parseErrors = proxyio.ParseText(req.Text)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": "nodes 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"
		}
		nodes, parseErrors = proxyio.ParseText(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(nodes) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no valid proxies", "errors": parseErrors})
		return
	}

	if mode == "append" {
		existing, _ := s.app.Store.ListProxyNodes(ctx, "manual", nil)
		proxyio.AssignUniqueNames(nodes, proxyio.ReservedNames(existing))
		if err := s.app.Store.AppendManualProxies(ctx, nodes); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
	} else {
		proxyio.AssignUniqueNames(nodes, nil)
		if err := s.app.Store.ReplaceManualProxies(ctx, nodes); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
	}

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

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