package logtr

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"time"
)

type Level int

const (
	VERBOSE = Level(7)
	DEBUG   = Level(8)
	INFO    = Level(9)
	ERROR   = Level(10)
	SOS     = Level(15)
)

type SOSer interface {
	Send(string) error
}

func (l Level) MarshalJSON() ([]byte, error) {
	return json.Marshal(l.String())
}

func (l *Level) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	s = strings.TrimSpace(s)
	s = strings.ToUpper(s)
	if len(s) > 3 {
		s = s[:3]
	}
	for i := 0; i < int(SOS)+5; i++ {
		l2 := Level(i)
		if l2.String() == s {
			*l = l2
			return nil
		}
	}
	return errors.New("unknown log level: " + s)
}

var logger io.Writer = os.Stderr
var loggerPath string = ""
var lock = &sync.Mutex{}
var level Level = INFO
var ansoser SOSer = nil

func SetLogpath(p string) {
	lock.Lock()
	defer lock.Unlock()
	if p == loggerPath {
		return
	}
	f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		panic(err)
	}
	logger = f
	loggerPath = p
}

func SetSOSer(another SOSer) {
	lock.Lock()
	defer lock.Unlock()
	ansoser = another
}

func SetLevel(l Level) {
	lock.Lock()
	defer lock.Unlock()
	level = l
}

func logf(l Level, format string, args []interface{}) {
	format = fmt.Sprintf("%v: %v: %s\n", time.Now().Format("01-02T15:04:05"), l.String(), strings.TrimSpace(format))
	logContent := strings.ReplaceAll(fmt.Sprintf(format, args...), "\n", "") + "\n"
	cLevel := level
	cAnsoser := ansoser
	if l >= cLevel {
		fmt.Fprint(os.Stderr, logContent)
	}
	fmt.Fprint(logger, logContent)
	if l == SOS && cAnsoser != nil {
		if err := cAnsoser.Send(logContent); err != nil {
			Errorf("failed to SOS: %v", err)
		}
	}
}

func SOSf(format string, args ...interface{}) {
	logf(SOS, format, args)
}

func Infof(format string, args ...interface{}) {
	logf(INFO, format, args)
}

func Printf(format string, args ...interface{}) {
	Infof(format, args...)
}

func Debugf(format string, args ...interface{}) {
	logf(DEBUG, format, args)
}

func Verbosef(format string, args ...interface{}) {
	logf(VERBOSE, format, args)
}

func Errorf(format string, args ...interface{}) {
	logf(ERROR, format, args)
}

func (l Level) String() string {
	switch l {
	case ERROR:
		return "ERR"
	case INFO:
		return "INF"
	case DEBUG:
		return "DEB"
	case VERBOSE:
		return "VER"
	case SOS:
		return "SOS"
	default:
		return "?"
	}
}
