// Package observability provides logging and metrics instrumentation.
package observability

import (
	"encoding/json"
	"log"
	"time"
)

type LogLevel string

const (
	LogLevelInfo  LogLevel = "info"
	LogLevelWarn  LogLevel = "warn"
	LogLevelError LogLevel = "error"
)

type LogEvent struct {
	TS        string         `json:"ts"`
	Level     LogLevel       `json:"level"`
	Event     string         `json:"event"`
	Message   string         `json:"message,omitempty"`
	Fields    map[string]any `json:"fields,omitempty"`
	Error     string         `json:"error,omitempty"`
	Component string         `json:"component,omitempty"`
}

func LogJSON(level LogLevel, event string, component string, message string, fields map[string]any, err error) {
	errorValue := ""
	if err != nil {
		errorValue = err.Error()
	}

	entry := LogEvent{
		TS:        time.Now().UTC().Format(time.RFC3339Nano),
		Level:     level,
		Event:     event,
		Message:   message,
		Fields:    fields,
		Error:     errorValue,
		Component: component,
	}

	// Best-effort. If encoding fails, fall back to a minimal line.
	bytes, marshalErr := json.Marshal(entry)
	if marshalErr != nil {
		// Keep output JSON-only even on failures by constructing a minimal entry.
		// Marshal individual strings to ensure proper escaping.
		tsBytes, _ := json.Marshal(time.Now().UTC().Format(time.RFC3339Nano))
		levelBytes, _ := json.Marshal(level)
		eventBytes, _ := json.Marshal("log_marshal_failed")
		componentBytes, _ := json.Marshal(component)
		errBytes, _ := json.Marshal(marshalErr.Error())
		log.Printf(`{"ts":%s,"level":%s,"event":%s,"component":%s,"error":%s}`, tsBytes, levelBytes, eventBytes, componentBytes, errBytes)
		return
	}

	log.Print(string(bytes))
}
