// Package policycatalog scans a directory of Rego files for the dashboard catalog API.
package policycatalog

import (
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// Entry describes one governance domain module or the top-level router.
type Entry struct {
	PolicyID  string  `json:"policy_id,omitempty"`
	Domain    string  `json:"domain"`
	Package   string  `json:"package"`
	EntryKind string  `json:"entry_kind"` // "module" | "router"
	Title     string  `json:"title,omitempty"`
	Rules     []Rule  `json:"rules,omitempty"`
	Routes    []Route `json:"routes,omitempty"`
	RuleCount int     `json:"rule_count"`
}

// Scan walks dir for *.rego (excluding *_test.rego), parses packages and rules.
func Scan(dir string) ([]Entry, error) {
	if strings.TrimSpace(dir) == "" {
		return nil, nil
	}
	st, err := os.Stat(dir)
	if err != nil {
		if os.IsNotExist(err) {
			return []Entry{}, nil
		}
		return nil, err
	}
	if !st.IsDir() {
		return nil, fmt.Errorf("policies dir is not a directory: %s", dir)
	}

	var entries []Entry
	err = filepath.WalkDir(dir, func(path string, d os.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if d.IsDir() {
			return nil
		}
		name := d.Name()
		if !strings.HasSuffix(name, ".rego") {
			return nil
		}
		if strings.HasSuffix(name, "_test.rego") || name == "policies_test.rego" {
			return nil
		}
		pkg, err := parsePackageLine(path)
		if err != nil || pkg == "" {
			return nil
		}

		ent := Entry{
			Package: pkg,
			Domain:  domainFromPackage(pkg),
			Title:   titleFromDomain(domainFromPackage(pkg)),
		}

		if pkg == "acp" {
			ent.EntryKind = "router"
			ent.PolicyID = "acp/router"
			routes, _ := ParseRoutes(path)
			ent.Routes = routes
			ent.RuleCount = len(routes)
		} else {
			ent.EntryKind = "module"
			ent.PolicyID = strings.ReplaceAll(pkg, ".", "/")
			rules, _ := ParseRules(path)
			ent.Rules = rules
			ent.RuleCount = len(rules)
		}
		entries = append(entries, ent)
		return nil
	})
	if err != nil {
		return nil, err
	}
	sort.Slice(entries, func(i, j int) bool {
		if entries[i].EntryKind != entries[j].EntryKind {
			return entries[i].EntryKind == "router"
		}
		return strings.ToLower(entries[i].Domain) < strings.ToLower(entries[j].Domain)
	})
	return entries, nil
}

func titleFromDomain(domain string) string {
	if domain == "router" {
		return "Policy router"
	}
	if domain == "pii" {
		return "PII access"
	}
	parts := strings.Split(domain, "_")
	for i, p := range parts {
		if p == "" {
			continue
		}
		parts[i] = strings.ToUpper(p[:1]) + strings.ToLower(p[1:])
	}
	return strings.Join(parts, " ")
}

func parsePackageLine(path string) (string, error) {
	b, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	for _, line := range strings.Split(string(b), "\n") {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") || line == "" {
			continue
		}
		if strings.HasPrefix(line, "package ") {
			return strings.TrimSpace(strings.TrimPrefix(line, "package ")), nil
		}
	}
	return "", nil
}
