package log

import (
	"fmt"
	"os"
	"sync"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/buffer"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

// func InitLogger() {
// 	writeSyncer := getLogWriter()
// 	encoder := getEncoder()
// 	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)

// 	logger := zap.New(core) //, zap.AddCaller(), zap.AddCallerSkip(1)
// 	sugarLogger = logger.Sugar()
// }

// var l = new(zapcore.Level)
// err = l.UnmarshalText([]byte(cfg.Level))
// if err != nil {
// 	return
// }

func InitLogger(appName string, isDisplayConsole bool) (*zap.SugaredLogger, *asyncWriter) {

	encoder := getEncoder()

	// test.log记录全量日志
	filePath1 := fmt.Sprintf("%s.log", appName)
	writeSyncer1, lumberJackLogger1 := getLogWriter(filePath1, 1024*1024*1024, 7, 7)
	filePath2 := fmt.Sprintf("%s.err.log", appName)
	writeSyncer2, lumberJackLogger2 := getLogWriter(filePath2, 1024*1024*1024, 7, 7)
	// c1 := zapcore.NewCore(encoder, writeSyncer1, zapcore.DebugLevel)

	// 异步写入器
	asyncWriter := NewAsyncWriter(writeSyncer1)
	asyncWriter.StartRunningAndRotate(func() {
		lumberJackLogger1.Rotate()
		lumberJackLogger2.Rotate()
	})
	asyncCore := zapcore.NewCore(encoder, zapcore.AddSync(asyncWriter), zapcore.InfoLevel)

	// test.err.log记录ERROR级别的日志
	// filePath2 := fmt.Sprintf("%s.err.log", appName)
	// writeSyncer2, lumberJackLogger2 := getLogWriter(filePath2, 1024*1024*1024, 7, 7)
	c2 := zapcore.NewCore(encoder, writeSyncer2, zap.ErrorLevel)

	// 使用NewTee将c1和c2合并到core
	var core zapcore.Core
	if isDisplayConsole {
		consoleWriter := zapcore.AddSync(os.Stdout)
		c3 := zapcore.NewCore(encoder, consoleWriter, zap.DebugLevel)
		core = zapcore.NewTee(asyncCore, c2, c3)
	} else {
		core = zapcore.NewTee(asyncCore, c2)
	}

	logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(2)) //, zap.AddCaller(), zap.AddCallerSkip(1)
	sugarLogger := logger.Sugar()
	return sugarLogger, asyncWriter
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.DateTime) //ISO8601TimeEncoder //
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	return zapcore.NewJSONEncoder(encoderConfig)
	// NewJSONEncoder
	// NewConsoleEncoder
}

// func getLogWriter() zapcore.WriteSyncer {
// 	file, _ := os.Create("./test.log")
// 	// 利用io.MultiWriter支持文件和终端两个输出目标
// 	ws := io.MultiWriter(file, os.Stdout)
// 	return zapcore.AddSync(ws)
// }

func getLogWriter(filename string, maxSize, maxBackup, maxAge int) (zapcore.WriteSyncer, *lumberjack.Logger) {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   filename,
		MaxSize:    maxSize,
		MaxBackups: maxBackup,
		MaxAge:     maxAge,
		LocalTime:  true,
	}
	return zapcore.AddSync(lumberJackLogger), lumberJackLogger
}

// 异步写入器
type asyncWriter struct {
	entryChan chan *buffer.Buffer
	wg        sync.WaitGroup
	writer    zapcore.WriteSyncer
	pool      buffer.Pool
}

func NewAsyncWriter(writeSyncer1 zapcore.WriteSyncer) *asyncWriter {
	entryChan := make(chan *buffer.Buffer, 1000) // 缓冲区大小可以根据实际需求调整
	asyncWriter := new(asyncWriter)
	asyncWriter.entryChan = entryChan
	asyncWriter.writer = writeSyncer1
	asyncWriter.pool = buffer.NewPool()
	return asyncWriter
}

func (w *asyncWriter) Write(p []byte) (n int, err error) {
	buffer := w.pool.Get()
	buffer.Write(p)
	w.entryChan <- buffer
	return len(p), nil
}

func (w *asyncWriter) Sync() error {
	buffer := w.pool.Get()
	w.entryChan <- buffer
	return nil

	// return w.writer.Sync()
}

func (w *asyncWriter) StartRunningAndRotate(rotateFunc func()) {
	w.wg.Add(1)
	go func() {
		defer w.wg.Done()

		safeWrite := func(entry *buffer.Buffer) {
			defer func() {
				if err := recover(); err != nil {
					fmt.Fprintln(os.Stderr, "panic writing to log file:", err)
				}
			}()

			var err error
			if entry.Len() == 0 {
				err = w.writer.Sync()
			} else {
				_, err = w.writer.Write(entry.Bytes())
			}
			if err != nil {
				fmt.Fprintln(os.Stderr, "Error writing to log file:", err)
			}
		}

		safeRote := func() {
			defer func() {
				if err := recover(); err != nil {
					fmt.Fprintln(os.Stderr, "panic writing to log file:", err)
				}
			}()
			rotateFunc()
		}

		lastRoteTime := time.Now()
		ticker := time.NewTicker(time.Minute)

		for {
			select {
			case ct := <-ticker.C:
				if ct.YearDay() != lastRoteTime.YearDay() {
					lastRoteTime = ct
					safeRote()
				}
			case entry, ok := <-w.entryChan:
				if !ok {
					return
				}
				safeWrite(entry)
				entry.Free()
			}
		}
	}()
}

func testzaplog() {
	log, w := InitLogger("test", false)
	defer func() {
		log.Sync()
		close(w.entryChan)
		w.wg.Wait()
	}()

	log.Info("info")
	log.Error("error")
	log.Debug("debug")
	log.Warn("warn")
	// log.Panic("panic")
	log.Fatal("fatal")

	// time.Sleep(time.Second * 1000)

	// defer sugarLogger.Sync()
}
