package boot

import (
	"context"
	"fmt"
	redisLib "github.com/go-redis/redis/v8"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/os/gcron"
	"go.uber.org/dig"
	"gorm.io/gorm"
	agent2 "gxt-file-server/app/agent"
	"gxt-file-server/app/bll"
	"gxt-file-server/app/bll/impl"
	"gxt-file-server/app/model"
	"gxt-file-server/pkg/gplus"
	"gxt-file-server/pkg/logger"
	"gxt-file-server/pkg/middleware"
	"gxt-file-server/pkg/redis"
	"gxt-file-server/pkg/store"
	"gxt-file-server/pkg/utils"
	"gxt-file-server/router"
	"log"
	"os"
)

// VERSION 定义应用版本号
const VERSION = "1.0.0"

func init() {
	// 初始化logger
	logger.Init(g.Cfg().GetString("common.run_mode"))
	logger.SetVersion(VERSION)
	logger.SetTraceIdFunc(utils.NewTraceID)
	ctx := logger.NewTraceIDContext(context.Background(), utils.NewTraceID())
	Init(ctx)
}

// Init 初始化App,
func Init(ctx context.Context) func() {
	logger.Printf(ctx, "服务启动，运行模式：%s，版本号：%s，进程号：%d", g.Cfg().Get("common.run_mode"), VERSION, os.Getpid())
	// 初始化依赖注入容器
	container, call := buildContainer(ctx)
	// 初始化路由注册
	s := g.Server()
	// 每个请求生成新的追踪Id,如果上下文件中没有trace-id
	s.Use(middleware.TraceIdMiddleware())
	// file middleware
	filePrefix := fmt.Sprintf("/%s/", g.Cfg().GetString("agent.DefaultFilePathPrefix"))
	s.Use(middleware.FileMiddleWare(middleware.AllowPathPrefixNoSkipper(filePrefix)))
	// 统一处理内部错误
	s.Use(func(r *ghttp.Request) {
		r.Middleware.Next()
		if err := r.GetError(); err != nil {
			gplus.ResError(r, err)
		}
	})
	initCron(ctx, container)
	router.InitRouters(s, container)
	return func() {
		if call != nil {
			call()
		}
	}

}

// 初始化redis
func initRedis(ctx context.Context, container *dig.Container) func() {
	addr := g.Cfg().GetString("redis.addr")
	password := g.Cfg().GetString("redis.password")
	db := g.Cfg().GetInt("redis.db")
	redisCli := redis.Init(ctx, addr, password, db)
	redisCli.ConfigSet(ctx, "set notify-keyspace-events", "Ex")
	logger.Printf(ctx, "REDIS初始化成功，当前服务器[%s, %d]", addr, db)
	// 注入redis client
	_ = container.Provide(func() *redisLib.Client {
		return redisCli
	})
	return func() {
		_ = redisCli.Close()
	}
}

func initAgent(ctx context.Context, container *dig.Container) func() {
	_ = container.Invoke(func(rCli *redisLib.Client, fhm model.IFileHistory) {
		addr := g.Cfg().GetString("minio.addr")
		aKey := g.Cfg().GetString("minio.assess_key")
		sKey := g.Cfg().GetString("minio.secret_key")
		m := store.MiniStoreInit(addr, aKey, sKey)
		agent2.DefaultAgent().SetBackend(m)
		agent2.DefaultAgent().SetConfig(&agent2.Config{
			DefaultExpireTime: g.Cfg().GetInt("agent.DefaultExpireTime"),
		})
		agent2.DefaultAgent().SetRedisClient(rCli)
		agent2.DefaultAgent().SetFileHistoryModel(fhm)
		_ = agent2.DefaultAgent().Start(ctx)
	})
	return func() {

	}
}

// 初始化存储，目前只初始化gorm
func initStore(ctx context.Context, container *dig.Container) (func(), error) {
	var storeCall func()
	db, err := initGorm()
	if err != nil {
		return storeCall, err
	}
	// 如果自动映射数据表
	if g.Cfg().GetBool("gorm.enable_auto_migrate") {
		err = autoMigrate(db)
		if err != nil {
			return storeCall, err
		}
	}
	// 注入DB
	_ = container.Provide(func() *gorm.DB { return db })
	// 注入model接口
	_ = InjectModel(container)
	storeCall = func() {
		sqlDb, _ := db.DB()
		_ = sqlDb.Close()
	}
	logger.Printf(ctx, "MYSQL初始化成功, 服务器[%s], 数据库[%s]",
		g.Cfg().GetString("mysql.host"),
		g.Cfg().GetString("mysql.db_name"))
	return storeCall, nil
}

// 构建依赖注入容器
func buildContainer(ctx context.Context) (*dig.Container, func()) {
	container := dig.New()
	// 初始化存储模块
	storeCall, err := initStore(ctx, container)
	if err != nil {
		panic(err)
	}
	// 初始化redis模块
	var redisCall func()
	if g.Cfg().GetBool("redis.enable") {
		redisCall = initRedis(ctx, container)
	}
	agentCall := initAgent(ctx, container)
	// 注入bll
	impl.Inject(container)
	return container, func() {
		if storeCall != nil {
			storeCall()
		}
		if redisCall != nil {
			redisCall()
		}
		if agentCall != nil {
			agentCall()
		}
	}
}

func initCron(ctx context.Context, container *dig.Container) {
	//秒 分 时  日 月 周
	runMode := g.Cfg().GetString("common.run_mode")
	_ = container.Invoke(func(
		chunk bll.IFileChunk,
	) {
		switch runMode {
		case "debug", "test":
			initCronDebug(ctx, chunk) //定时清理过期文件块
		case "release":
			initCronRelease(ctx, chunk) //定时清理过期文件块
		}
		logger.Printf(ctx, "gCorn初始化成功\n")
	})
}

func initCronDebug(ctx context.Context, chunk bll.IFileChunk) {
	_, err := gcron.Add("0 5 * * * *", func() {
		err := chunk.ClearChunks(ctx)
		if err != nil {
			log.Fatalln(err)
			return
		}
	})
	if err != nil {
		logger.Errorf(ctx, err.Error())
	}
}

func initCronRelease(ctx context.Context, chunk bll.IFileChunk) {
	clearChunk := g.Cfg().GetString("corn.clear_chunk")
	_, err := gcron.Add(clearChunk, func() {
		err := chunk.ClearChunks(ctx)
		if err != nil {
			log.Fatalln(err)
			return
		}
	})
	if err != nil {
		logger.Errorf(ctx, err.Error())
	}
}
