// Package logger provides structured JSONL logging for agents with daily
// file rotation, size-based splitting, automatic cleanup, and query helpers.
package logger

import (
	"context"
	"log/slog"
	"os"
	"time"
)

// Standard field names for structured logging across all agents.
const (
	FieldAgentID    = "agent_id"
	FieldTraceID    = "trace_id"
	FieldAction     = "action"
	FieldReason     = "reason"
	FieldDurationMS = "duration_ms"
	FieldTokensUsed = "tokens_used"
	FieldResult     = "result"
	FieldErrorType  = "error_type"
	FieldComponent  = "component"
)

// traceKey is the context key for trace IDs.
type traceKey struct{}

// WithTraceID returns a new context carrying the given trace ID.
func WithTraceID(ctx context.Context, id string) context.Context {
	return context.WithValue(ctx, traceKey{}, id)
}

// TraceIDFromCtx extracts the trace ID from ctx, or "" if absent.
func TraceIDFromCtx(ctx context.Context) string {
	if v, ok := ctx.Value(traceKey{}).(string); ok {
		return v
	}
	return ""
}

// LoggerConfig configures a per-agent logger.
type LoggerConfig struct {
	BaseDir         string        // root log directory (default: "logs"); empty → stdout only
	AgentID         string        // agent identifier (required)
	MaxSizeMB       int64         // max file size before rotation (default: 50)
	MaxAgeDays      int           // retention in days (default: 7)
	Compress        bool          // gzip rotated files (default: true)
	CleanupInterval time.Duration // cleanup ticker interval (default: 24h)
	Level           slog.Level    // minimum log level (default: INFO)
}

func (c *LoggerConfig) defaults() {
	if c.BaseDir == "" {
		c.BaseDir = "logs"
	}
	if c.MaxSizeMB <= 0 {
		c.MaxSizeMB = 50
	}
	if c.MaxAgeDays <= 0 {
		c.MaxAgeDays = 7
	}
	if c.CleanupInterval <= 0 {
		c.CleanupInterval = 24 * time.Hour
	}
}

// NewAgentLogger creates a structured JSON logger that writes to daily-rotated
// JSONL files under BaseDir/<AgentID>/. It returns:
//   - a *slog.Logger pre-enriched with agent_id
//   - a cleanup func to call on shutdown (closes files, stops cleanup goroutine)
//   - an error if the log directory cannot be created
//
// If BaseDir is literally "stdout", the logger writes to os.Stdout with no
// file rotation or cleanup.
func NewAgentLogger(cfg LoggerConfig) (*slog.Logger, func(), error) {
	if cfg.BaseDir == "stdout" {
		h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: cfg.Level})
		l := slog.New(h).With(FieldAgentID, cfg.AgentID)
		return l, func() {}, nil
	}

	cfg.defaults()

	w, err := NewDailyRotatingWriter(cfg.BaseDir, cfg.AgentID, cfg.MaxSizeMB, cfg.Compress)
	if err != nil {
		return nil, nil, err
	}

	h := slog.NewJSONHandler(w, &slog.HandlerOptions{Level: cfg.Level})
	l := slog.New(h).With(FieldAgentID, cfg.AgentID)

	ctx, cancel := context.WithCancel(context.Background())
	go runCleanup(ctx, cfg.BaseDir, cfg.AgentID, cfg.MaxAgeDays, cfg.CleanupInterval)

	cleanup := func() {
		cancel()
		w.Close()
	}

	return l, cleanup, nil
}
