package log

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"github.com/sirupsen/logrus"

	"github.com/vapor/config"
)

const (
	rotationTime int64 = 86400
	maxAge       int64 = 604800
)

var defaultFormatter = &logrus.TextFormatter{DisableColors: true}

func InitLogFile(config *config.Config) error {
	logPath := config.LogDir()
	if err := clearLockFiles(logPath); err != nil {
		return err
	}

	hook := newBtmHook(logPath)
	logrus.AddHook(hook)
	logrus.SetOutput(ioutil.Discard) //控制台不输出
	fmt.Printf("all logs are output in the %s directory\n", logPath)
	return nil
}

type BtmHook struct {
	logPath string
	lock    *sync.Mutex
}

func newBtmHook(logPath string) *BtmHook {
	hook := &BtmHook{lock: new(sync.Mutex)}
	hook.logPath = logPath
	return hook
}

// Write a log line to an io.Writer.
func (hook *BtmHook) ioWrite(entry *logrus.Entry) error {
	module := "general"
	if data, ok := entry.Data["module"]; ok {
		module = data.(string)
	}

	logPath := filepath.Join(hook.logPath, module)
	writer, err := rotatelogs.New(
		logPath+".%Y%m%d",
		rotatelogs.WithMaxAge(time.Duration(maxAge)*time.Second),
		rotatelogs.WithRotationTime(time.Duration(rotationTime)*time.Second),
	)
	if err != nil {
		return err
	}

	msg, err := defaultFormatter.Format(entry)
	if err != nil {
		return err
	}

	if _, err = writer.Write(msg); err != nil {
		return err
	}

	return writer.Close()
}

func clearLockFiles(logPath string) error {
	files, err := ioutil.ReadDir(logPath)
	if os.IsNotExist(err) {
		return nil
	} else if err != nil {
		return err
	}

	for _, file := range files {
		if ok := strings.HasSuffix(file.Name(), "_lock"); ok {
			if err := os.Remove(filepath.Join(logPath, file.Name())); err != nil {
				return err
			}
		}
	}
	return nil
}

func (hook *BtmHook) Fire(entry *logrus.Entry) error {
	hook.lock.Lock()
	defer hook.lock.Unlock()
	return hook.ioWrite(entry)
}

// Levels returns configured log levels.
func (hook *BtmHook) Levels() []logrus.Level {
	return logrus.AllLevels
}
