package learn

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

	"ctx-wire/internal/ui"
)

// RulesFileRelPath is where WriteRulesFile persists the rules, relative to the
// project root. It matches the convention agents read project rules from.
const RulesFileRelPath = ".claude/rules/cli-corrections.md"

// FormatThemed renders the console report: the detected correction rules with
// their occurrence counts and the token change to make.
func FormatThemed(rep *Report, theme ui.Theme) string {
	var b strings.Builder
	fmt.Fprintf(&b, "%s\n\n", theme.Heading("ctx-wire learn: CLI corrections"))
	fmt.Fprintf(&b, "%s\n", theme.Dim.Render(fmt.Sprintf(
		"%d rule(s) from %d correction(s) across %d session(s)",
		len(rep.Rules), rep.Pairs, rep.Sessions)))
	if len(rep.Rules) == 0 {
		fmt.Fprintf(&b, "\n%s\n", theme.Dim.Render("No repeated CLI corrections detected. Nothing to learn yet."))
		return b.String()
	}
	b.WriteByte('\n')
	for i, r := range rep.Rules {
		fmt.Fprintf(&b, "%s %s  %s\n",
			theme.Number.Render(fmt.Sprintf("%d.", i+1)),
			theme.Command.Render(r.Base),
			theme.Dim.Render(fmt.Sprintf("(%s, x%d)", r.Kind, r.Occurrences)))
		fmt.Fprintf(&b, "   %s %s\n", theme.Dim.Render("change:"), theme.Good.Render(r.Diff))
		fmt.Fprintf(&b, "   %s %s\n", theme.Dim.Render("instead of:"), theme.Warn.Render(r.Wrong))
		fmt.Fprintf(&b, "   %s %s\n", theme.Dim.Render("use:"), theme.Good.Render(r.Right))
		if r.Example != "" {
			fmt.Fprintf(&b, "   %s %s\n", theme.Dim.Render("error:"), theme.Dim.Render(r.Example))
		}
	}
	return b.String()
}

// RulesMarkdown renders the rules file content: a stable header plus one section
// per correction rule. An empty report still produces a valid (empty) file so a
// prior file can be refreshed to "nothing learned".
func RulesMarkdown(rep *Report) string {
	var b strings.Builder
	b.WriteString("# CLI corrections\n\n")
	b.WriteString("Generated by `ctx-wire learn` from local Claude transcripts. ")
	b.WriteString("These are commands that failed and were corrected; prefer the corrected form.\n\n")
	if len(rep.Rules) == 0 {
		b.WriteString("No repeated CLI corrections detected yet.\n")
		return b.String()
	}
	for _, r := range rep.Rules {
		fmt.Fprintf(&b, "## `%s` (%s)\n\n", r.Base, r.Kind)
		fmt.Fprintf(&b, "- Seen %d time(s).\n", r.Occurrences)
		fmt.Fprintf(&b, "- Change: `%s`.\n", r.Diff)
		fmt.Fprintf(&b, "- Instead of: `%s`\n", r.Wrong)
		fmt.Fprintf(&b, "- Use: `%s`\n", r.Right)
		// The raw error output is deliberately omitted from the persisted file:
		// it is free text that secret-scrubbing only catches best-effort, and this
		// file may be committed. The classified kind above is the safe summary.
		b.WriteByte('\n')
	}
	return b.String()
}

// WriteRulesFile writes the rules markdown under projectRoot and returns the
// path written. It creates the .claude/rules directory if needed.
func WriteRulesFile(rep *Report, projectRoot string) (string, error) {
	path := filepath.Join(projectRoot, RulesFileRelPath)
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		return "", err
	}
	if err := os.WriteFile(path, []byte(RulesMarkdown(rep)), 0o644); err != nil {
		return "", err
	}
	return path, nil
}
