package handlers

import (
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/gin-gonic/gin"
)

// DocsHandler serves documentation files.
type DocsHandler struct {
	docsDir string
}

// NewDocsHandler creates a new DocsHandler.
func NewDocsHandler(docsDir string) *DocsHandler {
	return &DocsHandler{docsDir: docsDir}
}

// docEntry describes a documentation file.
type docEntry struct {
	Slug        string `json:"slug"`
	Title       string `json:"title"`
	Description string `json:"description,omitempty"`
}

// docsOrder defines the display order and metadata for documentation files.
var docsOrder = []docEntry{
	{Slug: "GETTING-STARTED", Title: "Getting Started", Description: "Quick start guide"},
	{Slug: "ARCHITECTURE", Title: "Architecture", Description: "Kernel design and components"},
	{Slug: "EXTENSION-GUIDE", Title: "Extension Guide", Description: "How to author extensions"},
	{Slug: "PACKAGE-FORMAT", Title: "Package Format", Description: "The .pkg archive format"},
	{Slug: "API-REFERENCE", Title: "API Reference", Description: "REST API overview"},
	{Slug: "DEPLOYMENT", Title: "Deployment", Description: "Production deployment guide"},
	{Slug: "DISTRIBUTION", Title: "Distribution", Description: "Docker image and bundled packages"},
}

// ListDocs returns the list of available documentation files.
// GET /api/v1/docs
func (h *DocsHandler) ListDocs(c *gin.Context) {
	if h.docsDir == "" {
		c.JSON(http.StatusOK, gin.H{"data": []docEntry{}})
		return
	}

	var available []docEntry
	for _, d := range docsOrder {
		path := filepath.Join(h.docsDir, d.Slug+".md")
		if _, err := os.Stat(path); err == nil {
			available = append(available, d)
		}
	}

	// Also add any .md files not in the ordered list
	entries, err := os.ReadDir(h.docsDir)
	if err == nil {
		known := make(map[string]bool)
		for _, d := range docsOrder {
			known[d.Slug] = true
		}
		for _, e := range entries {
			if e.IsDir() || !strings.HasSuffix(e.Name(), ".md") {
				continue
			}
			slug := strings.TrimSuffix(e.Name(), ".md")
			if known[slug] {
				continue
			}
			// Skip design docs and other non-user-facing files
			if strings.HasPrefix(slug, "DESIGN-") || strings.HasPrefix(slug, "DEMO-") {
				continue
			}
			available = append(available, docEntry{
				Slug:  slug,
				Title: slugToTitle(slug),
			})
		}
	}

	if available == nil {
		available = []docEntry{}
	}
	c.JSON(http.StatusOK, gin.H{"data": available})
}

// GetDoc returns the raw markdown content of a documentation file.
// GET /api/v1/docs/:name
func (h *DocsHandler) GetDoc(c *gin.Context) {
	name := c.Param("name")
	if !isValidDocName(name) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid document name"})
		return
	}

	if h.docsDir == "" {
		c.JSON(http.StatusNotFound, gin.H{"error": "docs not configured"})
		return
	}

	path := filepath.Join(h.docsDir, name+".md")
	data, err := os.ReadFile(path)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "document not found"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"data": gin.H{
			"slug":    name,
			"title":   slugToTitle(name),
			"content": string(data),
		},
	})
}

var validDocNameRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)

func isValidDocName(name string) bool {
	return validDocNameRe.MatchString(name) && !strings.Contains(name, "..")
}

func slugToTitle(slug string) string {
	// Convert "GETTING-STARTED" → "Getting Started"
	parts := strings.Split(slug, "-")
	for i, p := range parts {
		if len(p) > 0 {
			parts[i] = strings.ToUpper(p[:1]) + strings.ToLower(p[1:])
		}
	}
	return strings.Join(parts, " ")
}
