// Package logger provides helper functions for logging
package logger

import (
	"encoding/json"
	"fmt"

	lz "github.com/karincake/apem/logger-zerolog"
	d "github.com/karincake/dodol"
	l "github.com/karincake/lepet"
)

type Event struct {
	// Context about the operation
	Feature string // Feature area, e.g. "Create"
	Action  string // Action being performed, e.g. "DBCreate"
	Source  string // Source of event, usually present in each use case
	Status  string // e.g. "started", "failed", "success"

	// Error context
	ErrInfo ErrorInfo
}

type ErrorInfo struct {
	Code   string // used in lang json
	Detail string
	Raw    error
}

// SetLogInfo updates the event and logs it.
// The first argument is the event, the second is the data.
// Variadic arguments:
//   - first (optional): status
//   - second (optional): action
func SetLogInfo(e *Event, data any, args ...string) {
	dataString, _ := json.Marshal(data)
	if len(args) > 0 {
		e.Status = args[0]
	}
	if len(args) > 1 {
		e.Action = args[1]
	}
	lz.O.Info().
		String("source", e.Source).
		String("feature", e.Feature).
		String("action", e.Action).
		String("status", e.Status).
		String("input", string(dataString)).
		Send()
}

func SetLogError(e *Event, data any) error {
	dataString, _ := json.Marshal(data)
	msg := l.I.Msg(e.ErrInfo.Code)

	lz.O.Error().
		String("message", msg).
		String("source", e.Source).
		String("feature", e.Feature).
		String("action", e.Action).
		String("status", e.Status).
		String("error_code", e.ErrInfo.Code).
		String("error_detail", e.ErrInfo.Detail).
		String("data", string(dataString)).
		Send()

	if err, ok := e.ErrInfo.Raw.(d.FieldError); ok {
		return err
	}
	if err, ok := e.ErrInfo.Raw.(d.FieldErrors); ok {
		return err
	}

	if e.ErrInfo.Detail != "" {
		return d.FieldError{Code: e.ErrInfo.Code, Message: e.ErrInfo.Detail}
	}
	return d.FieldError{Code: e.ErrInfo.Code, Message: msg}
}

func GenMessage(errCode string, errDetail ...string) string {
	errMsg := ""
	if len(errDetail) == 0 || errDetail[0] == "" {
		errMsg = l.I.Msg(errCode)
	} else if len(errDetail) == 1 && errDetail[0] != "" { // manual
		errMsg = fmt.Sprintf(l.I.Msg(errCode), errDetail[0])
	} else if len(errDetail) == 2 && errDetail[0] != "" && errDetail[1] != "" { // manual
		errMsg = fmt.Sprintf(l.I.Msg(errCode), errDetail[0], errDetail[1])
	}
	return errMsg
}
