package main

import (
	"encoding/json"
	"fmt"
	"log/slog"

	// "github.com/gofiber/fiber/v2"
	// "github.com/gofiber/fiber/v2/middleware/cors"
	// "github.com/gofiber/swagger"

	"github.com/gofiber/fiber/v2"

	//"github.com/gofiber/swagger"

	_ "code.wuban.net.cn/odysseus/twitter_syncer/docs"
	// docs are generated by Swag CLI, you have to import them.
	// replace with your own docs folder, usually "github.com/username/reponame/docs"
	//_ "github.com/gofiber/swagger/example/docs"
)

type Res struct {
	Code int64  `json:"code"`
	Msg  string `json:"msg"`
}

type VerifyRes struct {
	Code int64  `json:"code"`
	Msg  string `json:"msg"`
	Data struct {
		Ok bool `json:"ok"`
	} `json:"data"`
}

type ProjectRes struct {
	Code int64    `json:"code"`
	Msg  string   `json:"msg"`
	Data UserInfo `json:"data"`
}

/*

{
  "user_id": "1570057485914087429",
  "project": "OnlyDD_D",
  "task_type": "follow",
  "start": true
}

*/

type ProjectReq struct {
	Config
	Project string `json:"project"`
}

func Project(c *fiber.Ctx) error {

	slog.Info(c.Route().Path, "body", string(c.Request().Body()))

	req := ProjectReq{}

	if err := json.Unmarshal(c.Request().Body(), &req); err != nil {
		slog.Error("json.Unmarshal(c.Request().Body(), &req)", "err", err.Error())
		return c.JSON(ProjectRes{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	slog.Info("cfg", "project", req.Project, "ApiKey", req.ApiKey, "req.ApiKeySecrect", req.ApiKeySecrect, "req.AccessToken", req.AccessToken, "req.AccessTokenSecret", req.AccessTokenSecret, "req.Token", req.Token)

	data, ok, err := QueryProjectByKeysAndToken(req)

	if err != nil {
		slog.Error("QueryProject", "err", err.Error())
		return c.JSON(ProjectRes{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	if ok {
		return c.JSON(ProjectRes{
			Code: 500,
			Msg:  "already existed",
			Data: data,
		})

	}

	cli := NewClient(req.Config, nil)

	me, err := cli.Me()

	if err != nil {
		slog.Error("me", "err", err.Error())
		return c.JSON(ProjectRes{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	for _, v := range me {

		user := UserInfo{
			UserId:   v.User.ID,
			UserName: v.User.UserName,
			Name:     v.User.Name,
		}

		if err := AddOrUpdateProject(req, user); err != nil {
			slog.Error("insert db ", "err", err.Error())
			return c.JSON(ProjectRes{
				Code: 500,
				Msg:  err.Error(),
			})
		}

		return c.JSON(ProjectRes{
			Code: 200,
			Data: UserInfo{
				UserId:   v.User.ID,
				UserName: v.User.UserName,
				Name:     v.User.Name,
			},
		})
	}

	return c.JSON(ProjectRes{
		Code: 500,
		Msg:  "can not find out the user info with me API",
	})
}

type AddTaskReq struct {
	// AddOrStop bool
	User     string `json:"user_id"`
	TaskType string `json:"task_type"`
	TaskId   string `json:"task_id"`
	//FollowCount int
}

func TaskAdd(c *fiber.Ctx) error {

	//fmt.Println(string(c.Request().Body()))

	slog.Info(c.Route().Path, "body", string(c.Request().Body()))

	req := AddTaskReq{}

	if err := json.Unmarshal(c.Request().Body(), &req); err != nil {
		slog.Error("json.Unmarshal(c.Request().Body(), &req)", "err", err.Error())
		return c.JSON(Res{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	slog.Info(c.Route().Path, "user", req.User, "TaskType", req.TaskType, "TaskId", req.TaskId)

	if req.TaskType == "" || req.TaskId == "" {
		return c.JSON(Res{
			Code: 500,
			Msg:  "must provide TaskId and TaskType",
		})
	}
	//Todo
	// 校验任务 条件是否存在；

	projects, ok, err := QueryProjectByUserId(req.User)

	if err != nil {
		return c.JSON(Res{
			Code: 500,
			Msg:  "QueryProjectByUserId " + req.User,
		})
	}

	if !ok {

		return c.JSON(Res{
			Code: 500,
			Msg:  "can not find the user keys and token info " + req.User,
		})
	}

	ok, err = CheckTaskExist(req.User, req.TaskId, req.TaskType)

	if err != nil {
		return c.JSON(Res{
			Code: 500,
			Msg:  "QCheckTask " + req.User,
		})
	}

	if ok {

		return c.JSON(Res{
			Code: 500,
			Msg:  "task already existed",
		})
	}
	fc := 0

	if req.TaskType == FollowType { // do some check.

		// follower task count < available account count.
		ok, err := CheckFollowerTaskAndAccountCount()

		if err != nil {
			return c.JSON(Res{
				Code: 500,
				Msg:  "CheckFollowerTaskAndAccountCount " + req.User,
			})
		}

		if !ok {

			return c.JSON(Res{
				Code: 500,
				Msg:  "CheckFollowerTaskAndAccountCount !ok",
			})
		}

		ok, err = QueryProjectByUserIdAndName(req.TaskId, req.User)

		if err != nil {
			return c.JSON(Res{
				Code: 500,
				Msg:  "QueryProjectByUserIdAndName " + req.User,
			})
		}

		if !ok {

			return c.JSON(Res{
				Code: 500,
				Msg:  "QueryProjectByUserIdAndName !ok",
			})
		}

		fc, err = NewFollowerOb().TryProfileFollowerCount(req.TaskId)

		if err != nil {
			return c.JSON(Res{
				Code: 500,
				Msg:  "QCheckTask " + req.User,
			})
		}

	}

	// create a taskJob.
	job := TaskJob{
		Config: projects[0].Config,
		Idx:    make([]UserTask, 0),

		UserId:        req.User,
		TaskId:        req.TaskId,
		TaskType:      req.TaskType,
		FollowerCount: fc,
	}

	slog.Info("new", "job-", job.String())

	err = Worker.AddJob(job)
	if err != nil {

		slog.Error(" Worker.AddJob", "err", err.Error())
		return c.JSON(Res{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	slog.Info("add task into db", "user", req.User, "TaskType", req.TaskType, "TaskId", req.TaskId)
	if err = AddTaskInsert(req, fc); err != nil {
		slog.Error("twitter_syncer insert", "err", err.Error())
		return c.JSON(Res{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	return c.JSON(Res{
		Code: 200,
	})
}

// func CheckErrOk(ok bool, err error, c *fiber.Ctx) error {

// 	if err != nil {
// 		return c.JSON(Res{
// 			Code: 500,
// 			Msg:  "QCheckTask " + req.User,
// 		})
// 	}

// 	if ok {

// 		return c.JSON(Res{
// 			Code: 500,
// 			Msg:  "task already existed",
// 		})
// 	}

// }

func TaskStop(c *fiber.Ctx) error {

	slog.Info(c.Route().Path, "body", string(c.Request().Body()))

	req := AddTaskReq{}

	if err := json.Unmarshal(c.Request().Body(), &req); err != nil {
		slog.Error("json.Unmarshal(c.Request().Body(), &req)", "err", err.Error())
		return c.JSON(Res{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	slog.Info(c.Route().Path, "user", req.User, "TaskType", req.TaskType, "TaskId", req.TaskId)

	if req.TaskType == "" || req.TaskId == "" {
		return c.JSON(Res{
			Code: 500,
			Msg:  "must provide TaskId and TaskType",
		})
	}

	slog.Info("stop job", "user", req.User, "TaskType", req.TaskType, "TaskId", req.TaskId)

	if err := Worker.StopJob(req); err != nil {
		return c.JSON(Res{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	slog.Info("set stop to true in db", "user", req.User, "TaskType", req.TaskType, "TaskId", req.TaskId)

	err := StopTaskUpdate(req)

	if err != nil {

		slog.Error("twitter_syncer stop", "err", err.Error())
		return c.JSON(Res{
			Code: 500,
			Msg:  err.Error(),
		})
	}

	return c.JSON(Res{
		Code: 200,
	})

}

func VerifyRetweeter(c *fiber.Ctx) error {

	tweetId := c.Query("tweet_id")
	retweeterId := c.Query("retweeter_id")

	// beginTime := c.Query("begin_time")
	// endTime := c.Query("end_time")

	if len(tweetId) == 0 || len(retweeterId) == 0 {
		slog.Error("VerifyFollower", "tweetId", tweetId, "retweeterId", retweeterId)
		return c.JSON(Res{
			Code: 500,
			Msg:  fmt.Sprintf("must provide tweetId [%v] and retweeterId [%v]", tweetId, retweeterId),
		})
	}

	slog.Info(c.Route().Path, "tweetId", tweetId, "retweeterId", retweeterId)

	ok, err := VerifyRetweeterInDb(tweetId, retweeterId)

	if err != nil {
		slog.Error("VerifyRetweeter", "tweetId", tweetId, "retweeterId", retweeterId, "err", err.Error())
		return c.JSON(Res{
			Code: 500,
			//Msg:  fmt.Sprint("VerifyFollowerInDb", "tweetId", tweetId, "retweeterId", retweeterId, "err", err.Error()),
			Msg: fmt.Sprintf("VerifyRetweeter  tweetId   %v retweeterId %v  err %v", tweetId, retweeterId, err.Error()),
		})
	}

	return c.JSON(VerifyRes{
		Code: 200,
		Data: struct {
			Ok bool "json:\"ok\""
		}{
			Ok: ok,
		},
	})

}

func VerifyFollower(c *fiber.Ctx) error {

	userId := c.Query("task_id")
	followerId := c.Query("follower_id")

	if len(userId) == 0 || len(followerId) == 0 {
		slog.Error("VerifyFollower", "userId", userId, "followerId", followerId)
		return c.JSON(Res{
			Code: 500,
			Msg:  fmt.Sprintf("must provide userId [%v] and followerId [%v]", userId, followerId),
		})
	}

	followerUserName := c.Query("follower_username")

	slog.Info(c.Route().Path, "user_id", userId, "followerId", followerId, "followerUserName", followerUserName)

	ok, err := VerifyFollowerInDb(userId, followerId)

	if err != nil {
		slog.Error("VerifyFollowerInDb", "userId", userId, "followerId", followerId, "err", err.Error())
		return c.JSON(Res{
			Code: 500,
			Msg:  fmt.Sprintf("VerifyFollowerInDb  userId   %v followerId %v  err %v", userId, followerId, err.Error()),
		})
	}

	return c.JSON(VerifyRes{
		Code: 200,
		Data: struct {
			Ok bool "json:\"ok\""
		}{
			Ok: ok,
		},
	})

}

func VerifyLike(c *fiber.Ctx) error {

	tweetId := c.Query("tweet_id")
	retweeterId := c.Query("user_id")

	if len(tweetId) == 0 || len(retweeterId) == 0 {
		slog.Error("VerifyFollower", "tweetId", tweetId, "user_id", retweeterId)
		return c.JSON(Res{
			Code: 500,
			Msg:  fmt.Sprintf("must provide tweetId [%v] and user_id [%v]", tweetId, retweeterId),
		})
	}

	slog.Info(c.Route().Path, "tweetId", tweetId, "user_id", retweeterId)

	ok, err := VerifyLikeInDb(tweetId, retweeterId)

	if err != nil {
		slog.Error("VerifyRetweeter", "tweetId", tweetId, "user_id", retweeterId, "err", err.Error())
		return c.JSON(Res{
			Code: 500,
			//Msg:  fmt.Sprint("VerifyFollowerInDb", "tweetId", tweetId, "retweeterId", retweeterId, "err", err.Error()),
			Msg: fmt.Sprintf("VerifyRetweeter  tweetId   %v user_id %v  err %v", tweetId, retweeterId, err.Error()),
		})
	}

	return c.JSON(VerifyRes{
		Code: 200,
		Data: struct {
			Ok bool "json:\"ok\""
		}{
			Ok: ok,
		},
	})

}
