package main

import (
	"flag"
	"fmt"
	"github.com/syndtr/goleveldb/leveldb"
	"os"
	"os/signal"
	"simple-kv-store/internal/logprovider"
	"simple-kv-store/internal/nodes"
	"strconv"
	"strings"
	"syscall"

	"go.uber.org/zap"
)

var log, _ = logprovider.CreateDefaultZapLogger(zap.InfoLevel)

func main() {
	defer func() {
		if err := recover(); err != nil {
			log.Info("i get a panic", zap.Any("panic error", err))
		}
	}()

	// 设置一个通道来捕获中断信号
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)

	port := flag.String("port", ":9091", "rpc listen port")
	cluster := flag.String("cluster", "127.0.0.1:9091,127.0.0.1:9092,127.0.0.1:9093", "comma sep")
	id := flag.String("id", "1", "node ID")
	isRestart := flag.Bool("isRestart", false, "new test or restart")

	// 参数解析
	flag.Parse()
	clusters := strings.Split(*cluster, ",")
	idClusterPairs := make(map[string]string)
	idCnt := 1
	selfi, err := strconv.Atoi(*id)
	if err != nil {
		log.Fatal("figure id only")
	}
	for _, addr := range clusters {
		if idCnt == selfi {
			idCnt++ // 命令行cluster按id排序传入，记录时跳过自己的id，先保证所有节点互相记录的id一致
			continue
		}
		idClusterPairs[strconv.Itoa(idCnt)] = addr
		idCnt++
	}

	// storage/文件夹下为node重要数据持久化数据库，节点一旦创建成功就不能被删除
	if !*isRestart {	
		os.RemoveAll("storage/node" + *id)
	}

	// 创建每个结点自己的数据库。这里一开始理解上有些误区，状态机的状态恢复应该靠节点的持久化log，
	// 而用leveldb模拟状态机，造成了状态机本身的持久化，因此通过删去旧db避免这一矛盾
	// 因此leveldb/文件夹下为状态机模拟数据库，每次节点启动都需要删除该数据库
	os.RemoveAll("leveldb/simple-kv-store" + *id)

	db, err := leveldb.OpenFile("leveldb/simple-kv-store" + *id, nil)
	if err != nil {
		log.Fatal("Failed to open database: ", zap.Error(err))
	}
	defer db.Close() // 确保数据库在使用完毕后关闭

	// 打开或创建节点数据持久化文件
	storage := nodes.NewRaftStorage("storage/node" + *id)
	defer storage.Close()

	// 初始化
	node := nodes.InitRPCNode(*id, *port, idClusterPairs, db, storage, *isRestart)

	// 开启 raft
	quitChan := make(chan struct{}, 1)
	nodes.Start(node, quitChan)

	sig := <-sigs
	fmt.Println("node_"+ *id +"接收到信号:", sig)
	close(quitChan)

}
