package pricing

import (
	"bytes"
	"fmt"
	"sort"
	"strconv"
)

// MarshalDefaultsYAML renders the built-in default pricing table (catalog
// + supplementals, WITHOUT any operator override) as the YAML schema
// LoadOverrideFile consumes. It backs the generated
// defaults_llm_pricing.example.yaml so operators start from a file that
// matches the compiled-in rates exactly; a golden test keeps the two in
// sync. Output is deterministic (sorted surfaces and models).
func MarshalDefaultsYAML() []byte {
	var b bytes.Buffer
	b.WriteString(exampleHeader)

	table := buildDefaultTable()
	surfaces := make([]string, 0, len(table))
	for s := range table {
		surfaces = append(surfaces, s)
	}
	sort.Strings(surfaces)

	for _, surface := range surfaces {
		fmt.Fprintf(&b, "\n%s:\n", surface)
		models := table[surface]
		ids := make([]string, 0, len(models))
		for id := range models {
			ids = append(ids, id)
		}
		sort.Strings(ids)
		for _, id := range ids {
			e := models[id]
			fmt.Fprintf(&b, "  %s:\n", yamlKey(id))
			fmt.Fprintf(&b, "    input_per_1k: %s\n", rate(e.InputPer1k))
			fmt.Fprintf(&b, "    output_per_1k: %s\n", rate(e.OutputPer1k))
			if e.CachedInputPer1k > 0 {
				fmt.Fprintf(&b, "    cached_input_per_1k: %s\n", rate(e.CachedInputPer1k))
			}
			if e.CacheReadPer1k > 0 {
				fmt.Fprintf(&b, "    cache_read_per_1k: %s\n", rate(e.CacheReadPer1k))
			}
			if e.CacheCreationPer1k > 0 {
				fmt.Fprintf(&b, "    cache_creation_per_1k: %s\n", rate(e.CacheCreationPer1k))
			}
		}
	}
	return b.Bytes()
}

// rate renders a USD-per-1k rate without float noise ("0.00015", not
// "0.000150000000...").
func rate(v float64) string {
	return strconv.FormatFloat(v, 'f', -1, 64)
}

// yamlKey quotes model ids that YAML would otherwise misparse (e.g.
// "kimi-k3[1m]" starts a flow sequence unquoted).
func yamlKey(id string) string {
	for _, r := range id {
		switch r {
		case '[', ']', '{', '}', ':', '#', ',', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`':
			return strconv.Quote(id)
		}
	}
	return id
}

const exampleHeader = `# Default LLM pricing used by NetBird's Agent Network cost metering.
# GENERATED from the management catalog — do not edit this file in the
# repository; regenerate with:
#
#   go generate ./management/internals/modules/agentnetwork/pricing
#
# Operators: copy this file to <datadir>/defaults_llm_pricing.yaml (or
# any path configured via management.json:
#
#   { "AgentNetwork": { "PricingDefaultsFile": "/path/defaults_llm_pricing.yaml" } }
#
# ) and adjust the entries you want to change. Management re-reads the
# file periodically (mtime poll, every minute): the live table feeds the
# proxies' cost metering and the dashboard's model-price prefill, so
# edits apply without a restart. Your file only needs the entries you
# want to change — but each entry REPLACES the built-in entry for that
# surface+model whole, so repeat the cache rates you want to keep.
# Unknown fields and negative or non-finite rates are rejected: at
# startup that fails boot (for an explicitly configured path); at
# runtime the previous table is kept and a warning is logged. Deleting
# the file reverts to the built-in defaults below.
#
# Top-level keys are pricing surfaces — the parser shape requests are
# metered under: "openai" (also Azure, Mistral, and OpenAI-compatible
# gateways), "anthropic" (also Anthropic-on-Vertex), "bedrock"
# (normalized ids, e.g. anthropic.claude-sonnet-4-5). Model keys must be
# the normalized id the proxy meters (version/region suffixes stripped).
#
# Values are USD per 1_000 tokens. Optional cache fields:
#   cached_input_per_1k    OpenAI shape: rate for cached prompt tokens
#                          (a SUBSET of input tokens). Absent -> cached
#                          portion bills at input_per_1k.
#   cache_read_per_1k      Anthropic shape: rate for cache_read tokens
#                          (ADDITIVE to input). Absent -> input rate.
#   cache_creation_per_1k  Anthropic shape: rate for cache_creation
#                          tokens (ADDITIVE to input). Absent -> input
#                          rate.
`
