package log

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

func NewFileWriter(tag, path string) io.Writer {
	return &file{
		Tag:  tag,
		Path: path,
	}
}

func NewLogger(w io.Writer, dept int) Logger {
	return New(w, "", dept)
}

func New(w io.Writer, prefix string, dept int) *Log {
	if prefix != "" {
		prefix = buildPrefix(prefix) + " "
	}
	b := &Log{
		Depth: dept,
		log:   log.New(w, prefix, PrintFlags),
	}
	return b
}

const (
	LevelError uint8 = iota
	LevelWarn
	LevelInfo
	LevelDebug
)

const (
	LevelsError = "[E]"
	LevelsWarn  = "[W]"
	LevelsInfo  = "[I]"
	LevelsDebug = "[D]"
)

const (
	PrintFlags = log.LstdFlags | log.Llongfile
)

func buildPrefix(s string) string {
	return "[" + strings.ToUpper(s) + "]"
}

func spitPrefix(s string) string {
	idx := strings.Index(s, " ")
	if idx == -1 {
		return s
	}
	s = strings.ToLower(s[:idx])
	s = strings.TrimPrefix(s, "[")
	s = strings.TrimSuffix(s, "]")
	return s
}

type file struct {
	Tag  string // svc
	Path string // /var/log
	date string // 2006_01_02
	fi   *os.File
	mu   sync.Mutex
}

func (f *file) Write(b []byte) (n int, err error) {
	f.mu.Lock()
	defer f.mu.Unlock()
	fi, err := f.open()
	if err != nil {
		return 0, err
	}
	return fi.Write(b)
}

func (f *file) statDir() error {
	if _, err := os.Stat(f.Path); err != nil {
		if os.IsNotExist(err) {
			if err = os.MkdirAll(f.Path, os.ModePerm); err != nil {
				return err
			}
		}
		return err
	}
	return nil
}

func (f *file) openFile(name string) (*os.File, error) {
	return os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModePerm) // 创建文件
}

func (f *file) name(date string) string {
	// /var/log/svc_2006_01_02.log
	return filepath.Join(f.Path, fmt.Sprintf("%s_%s%s", f.Tag, date, ".log"))
}

func (f *file) open() (*os.File, error) {
	if err := f.statDir(); err != nil {
		return nil, err
	}

	date := time.Now().Format("2006_01_02")
	name := f.name(date)

	var (
		fi  *os.File
		err error
	)
	if _, err = os.Stat(name); err == nil { // 文件存在时
		if date != f.date { // 如果保存的日期与当前日期不一致时
			_ = f.fi.Close()
			fi, err = f.openFile(name)
			if err != nil {
				return nil, err
			}
			f.fi = fi     // 更新文件句柄
			f.date = date // 更新时间
		} else {
			fi = f.fi // 日期一致时
		}
		return fi, nil
	}

	if !os.IsNotExist(err) {
		return nil, err
	}

	fi, err = f.openFile(name) // 创建文件
	if err != nil {
		return nil, err
	}
	f.fi = fi // 更新文件句柄

	return fi, nil
}

type Log struct {
	Depth int // 2
	log   *log.Logger
	mu    sync.Mutex
}

func (l *Log) Write(b []byte) (int, error) {
	l.mu.Lock()
	n, err := bytes.NewReader(b).WriteTo(l.log.Writer())
	l.mu.Unlock()
	return int(n), err
}

func (l *Log) WriteString(s string) (int, error) {
	l.mu.Lock()
	defer l.mu.Unlock()
	err := l.log.Output(l.Depth, s)
	if err != nil {
		return 0, err
	}
	return len(s), nil
}

func (l *Log) Prefix(prefix string, f string, v ...any) {
	l.mu.Lock()
	old := l.log.Prefix()
	l.setPrefixFmt(prefix)
	_ = l.log.Output(l.Depth, fmt.Sprintf(f, v...))
	l.setPrefixFmt(old)
	l.mu.Unlock()
}

func (l *Log) Println(f string, v ...any) {
	l.mu.Lock()
	old := l.log.Prefix()
	l.log.SetPrefix("")
	_ = l.log.Output(l.Depth, fmt.Sprintf(f, v...))
	l.log.SetPrefix(old)
	l.mu.Unlock()
}

// Logger start
func (l *Log) Error(f string, v ...any) {
	l.mu.Lock()
	l.setPrefixFmt(LevelsError)
	_ = l.log.Output(l.Depth, fmt.Sprintf(f, v...))
	l.mu.Unlock()
}

func (l *Log) Warn(f string, v ...any) {
	l.mu.Lock()
	l.setPrefixFmt(LevelsWarn)
	_ = l.log.Output(l.Depth, fmt.Sprintf(f, v...))
	l.mu.Unlock()
}

func (l *Log) Info(f string, v ...any) {
	l.mu.Lock()
	l.setPrefixFmt(LevelsInfo)
	_ = l.log.Output(l.Depth, fmt.Sprintf(f, v...))
	l.mu.Unlock()
}

func (l *Log) Debug(f string, v ...any) {
	l.mu.Lock()
	l.setPrefixFmt(LevelsDebug)
	_ = l.log.Output(l.Depth, fmt.Sprintf(f, v...))
	l.mu.Unlock()
}

// Logger end

func (l *Log) setPrefixFmt(s string) {
	l.log.SetPrefix(s + " ")
}
