package impl_localfile

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"goutil/fileUtil"
	"goutil/timeUtil"
)

const (
	con_SEPERATOR = "\n------------------------------------------------------\n"
)

// Logger
// @description: 日志对象
type Logger struct {
	logPath   string
	loggerMap map[levelType]*fileLog
	bufPool   *sync.Pool

	// 压缩属性
	// 上一次日志压缩的日期 时间戳
	preCompressDate int64
	// 压缩锁对象
	compressLock sync.Mutex
}

// NewLogger
// @description: 构造日志对象
// parameter:
//
//	@*Logger:
func NewLogger() *Logger {
	log := &Logger{
		logPath:   "Log",
		loggerMap: make(map[levelType]*fileLog, 4),
		bufPool: &sync.Pool{
			New: func() interface{} {
				return &strings.Builder{}
			},
		},
	}

	// 初始化基础日志
	for lv := range levels {
		log.loggerMap[levelType(lv)] = newLog(log.logPath, levelType(lv))
	}
	return log
}

// log
// @description: 记录日志
// parameter:
//
//	@receiver l:
//	@logInfo:
//	@lv:
//	@ifIncludeHour:
//
// return:
func (l *Logger) log(logInfo string, lv levelType, ifIncludeHour bool) {
	buf := l.bufPool.Get().(*strings.Builder)
	defer l.bufPool.Put(buf)
	buf.Reset()

	// 组装所有需要写入的内容
	// 获取当前时间
	buf.WriteString(time.Now().Format("2006-01-02 15:04:05"))
	buf.WriteString("---->\n")
	buf.WriteString(logInfo)
	// 加上最后的分隔符
	buf.WriteString(con_SEPERATOR)

	// 写入之前检查
	l.checkCompress()

	// 构造对象并添加到队列中
	l.loggerMap[lv].msgChan <- newLogObject(buf.String(), lv, ifIncludeHour)
}

// SetLogPath
// @description: 设置文件路径
// parameter:
//
//	@receiver l:
//	@_logPath:
//
// return:
func (l *Logger) SetLogPath(_logPath string) {
	l.logPath = _logPath
	for _, lf := range l.loggerMap {
		lf.SetLogPath(_logPath)
	}
}

// checkCompress
// @description: 压缩
// parameter:
//
//	@receiver l:
//
// return:
func (l *Logger) checkCompress() {
	// 不用压缩，直接返回
	if !l.preCompress() {
		return
	}

	// 日志压缩
	go l.doCompress()
}

// closeLogFile
// @description: 关闭日志文件
// parameter:
//
//	@receiver l:
//
// return:
func (l *Logger) closeLogFile() {
	// 需要压缩文件，关闭已经打开的文件
	for _, lf := range l.loggerMap {
		_ = lf.f.Close()
	}
}

// preCompress  确定是否要压缩
func (l *Logger) preCompress() bool {
	// 检查是否需要进行数据压缩
	nowDate := timeUtil.GetDate(time.Now()).Unix()
	if nowDate == l.preCompressDate {
		return false
	}

	l.compressLock.Lock()
	defer l.compressLock.Unlock()

	// 上一次压缩的时间
	if nowDate == l.preCompressDate {
		return false
	}

	l.closeLogFile()

	l.preCompressDate = nowDate

	return true
}

// 日志压缩
func (l *Logger) doCompress() {
	defer func() {
		if r := recover(); r != nil {
			// 将错误输出，而不是记录到文件，是因为可能导致死循环
			fmt.Println(r)
		}
	}()

	// 获取昨天的日期，并获取昨天对应的文件夹
	yesterday := time.Now().AddDate(0, 0, -1)
	dateString := timeUtil.Format(yesterday, "yyyy-MM-dd")
	fileAbsoluteDirectory := filepath.Join(l.logPath, strconv.Itoa(yesterday.Year()), strconv.Itoa(int(yesterday.Month())))

	// 判断是否已经存在压缩文件
	compressFileName := fmt.Sprintf("%s.tar.gz", dateString)
	compressAbsolutePath := filepath.Join(fileAbsoluteDirectory, compressFileName)
	if exists, err := fileUtil.IsFileExists(compressAbsolutePath); err == nil && exists {
		return
	}

	// 获取昨天的文件列表
	fileList, err := fileUtil.GetFileList2(fileAbsoluteDirectory, dateString, con_FILE_SUFFIX)
	if err != nil {
		fmt.Printf("logUtil.compress.fileUtil.GetFileList2 err:%s\n", err)
		return
	}
	if len(fileList) == 0 {
		return
	}

	// 进行tar操作，得到yyyy-MM-dd.tar
	tarFileName := fmt.Sprintf("%s.tar", dateString)
	tarAbsolutePath := filepath.Join(fileAbsoluteDirectory, tarFileName)
	if err := fileUtil.Tar(fileList, tarAbsolutePath); err != nil {
		fmt.Printf("logUtil.compress.fileUtil.Tar err:%s\n", err)
	}

	// 进行gzip操作，得到yyyy-MM-dd.tar.gz
	if err := fileUtil.Gzip(tarAbsolutePath, ""); err != nil {
		fmt.Printf("logUtil.compress.fileUtil.Gzip err:%s\n", err)
	}

	// 删除原始文件
	for _, item := range fileList {
		_ = fileUtil.DeleteFile(item)
	}

	// 删除tar文件
	_ = fileUtil.DeleteFile(tarAbsolutePath)
}

// getLog
// @description: 组装日志
// parameter:
//
//	@format:
//	@args:
//
// return:
//
//	@string:
func getLog(format string, args ...interface{}) string {
	if len(args) == 0 {
		return format
	} else {
		return fmt.Sprintf(format, args...)
	}
}
