package log

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"
)

// DateRotator 日期轮转日志
type DateRotator struct {
	// 基本配置
	Filename   string // 日志文件路径模板，如 "./logs/app-%s.log"
	MaxSize    int    // 单个文件最大大小，单位MB
	MaxBackups int    // 最多保留多少个备份
	MaxAge     int    // 文件最多保存多少天
	Compress   bool   // 是否压缩
	LogFormat  string // 日期格式，如 "2006-01-02"

	// 内部状态
	mu          sync.Mutex // 保护以下字段
	file        *os.File   // 当前日志文件
	currentDate string     // 当前日期
	size        int64      // 当前文件大小
}

// NewDateRotator 创建一个基于日期轮转的日志器
func NewDateRotator(filename string, maxSize, maxBackups, maxAge int, compress bool, logFormat string) *DateRotator {
	if logFormat == "" {
		logFormat = "2006-01-02" // 默认按天轮转
	}

	r := &DateRotator{
		Filename:    filename,
		MaxSize:     maxSize,
		MaxBackups:  maxBackups,
		MaxAge:      maxAge,
		Compress:    compress,
		LogFormat:   logFormat,
		currentDate: time.Now().Format(logFormat),
	}

	return r
}

// Write 写入日志，同时检查是否需要轮转
func (r *DateRotator) Write(p []byte) (n int, err error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	// 检查日期是否变化
	currentDate := time.Now().Format(r.LogFormat)
	if r.file == nil || currentDate != r.currentDate {
		if err := r.rotate(currentDate); err != nil {
			return 0, err
		}
	}

	// 检查文件大小是否超限
	if r.size+int64(len(p)) >= int64(r.MaxSize*1024*1024) {
		if err := r.rotate(currentDate); err != nil {
			return 0, err
		}
	}

	// 写入日志
	n, err = r.file.Write(p)
	r.size += int64(n)
	return n, err
}

// Close 关闭当前日志文件
func (r *DateRotator) Close() error {
	r.mu.Lock()
	defer r.mu.Unlock()

	if r.file != nil {
		err := r.file.Close()
		r.file = nil
		return err
	}
	return nil
}

// rotate 轮转日志文件
func (r *DateRotator) rotate(newDate string) error {
	// 关闭旧文件
	if r.file != nil {
		r.file.Close()
		r.file = nil
	}

	// 更新日期
	r.currentDate = newDate

	// 根据文件名格式更新日期部分
	var filename string
	if strings.Contains(r.Filename, "%s") {
		// 如果文件名中有格式化占位符，直接使用新日期替换
		filename = fmt.Sprintf(r.Filename, newDate)
	} else {
		// 如果没有占位符，则检查文件名是否已经包含日期格式
		ext := filepath.Ext(r.Filename)
		base := r.Filename[:len(r.Filename)-len(ext)]

		// 如果文件名中已经有日期格式（如api-2025-05-26.log），则替换日期部分
		datePattern := regexp.MustCompile(`-\d{4}-\d{2}-\d{2}`)
		if datePattern.MatchString(base) {
			base = datePattern.ReplaceAllString(base, "-"+newDate)
			filename = base + ext
		} else {
			// 否则在扩展名前添加日期
			filename = base + "-" + newDate + ext
		}
	}

	// 确保目录存在
	dir := filepath.Dir(filename)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

	// 打开新文件
	f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return err
	}

	// 获取文件大小
	info, err := f.Stat()
	if err != nil {
		f.Close()
		return err
	}

	// 更新状态
	r.file = f
	r.size = info.Size()

	// 清理旧文件（异步）
	go r.cleanup()

	return nil
}

// cleanup 清理旧日志文件
func (r *DateRotator) cleanup() {
	// 实现基于日期和数量的旧文件清理
	if r.MaxAge <= 0 && r.MaxBackups <= 0 {
		return
	}

	pattern := ""
	dir := filepath.Dir(r.Filename)
	base := filepath.Base(r.Filename)
	ext := filepath.Ext(base)
	prefix := base[:len(base)-len(ext)]

	// 构造通配符模式
	pattern = filepath.Join(dir, prefix+"*"+ext)

	// 查找匹配的文件
	matches, err := filepath.Glob(pattern)
	if err != nil {
		return
	}

	// 按修改时间排序
	type fileInfo struct {
		path    string
		modTime time.Time
	}
	var files []fileInfo

	for _, match := range matches {
		info, err := os.Stat(match)
		if err != nil {
			continue
		}
		files = append(files, fileInfo{match, info.ModTime()})
	}

	// 按修改时间从新到旧排序
	sort := func(i, j int) bool {
		return files[i].modTime.After(files[j].modTime)
	}
	for i := 0; i < len(files)-1; i++ {
		for j := i + 1; j < len(files); j++ {
			if !sort(i, j) {
				files[i], files[j] = files[j], files[i]
			}
		}
	}

	// 根据保留数量删除旧文件
	if r.MaxBackups > 0 && len(files) > r.MaxBackups {
		for i := r.MaxBackups; i < len(files); i++ {
			os.Remove(files[i].path)
		}
		files = files[:r.MaxBackups]
	}

	// 根据保留天数删除旧文件
	if r.MaxAge > 0 {
		cutoff := time.Now().Add(-time.Duration(r.MaxAge) * 24 * time.Hour)
		for _, f := range files {
			if f.modTime.Before(cutoff) {
				os.Remove(f.path)
			}
		}
	}
}

// Sync 将缓存数据同步到磁盘
func (r *DateRotator) Sync() error {
	r.mu.Lock()
	defer r.mu.Unlock()

	if r.file != nil {
		return r.file.Sync()
	}
	return nil
}
