package admin

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/bjdgyc/anylink/base"
	"github.com/bjdgyc/anylink/dbdata"
	"github.com/bjdgyc/anylink/pkg/utils"
	"github.com/bjdgyc/anylink/sessdata"
	"github.com/skip2/go-qrcode"
	mail "github.com/xhit/go-simple-mail/v2"
)

func UserList(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	prefix := r.FormValue("prefix")
	prefix = strings.TrimSpace(prefix)
	pageS := r.FormValue("page")
	page, _ := strconv.Atoi(pageS)
	if page < 1 {
		page = 1
	}

	var (
		pageSize = dbdata.PageSize
		count    int
		datas    []dbdata.User
		err      error
	)

	// 查询前缀匹配
	if len(prefix) > 0 {
		fuzzy := "%" + prefix + "%"
		where := "username LIKE ? OR nickname LIKE ? OR email LIKE ? OR type LIKE ?"

		count = dbdata.FindWhereCount(&dbdata.User{}, where, fuzzy, fuzzy, fuzzy, fuzzy)
		err = dbdata.FindWhere(&datas, pageSize, page, where, fuzzy, fuzzy, fuzzy, fuzzy)
	} else {
		count = dbdata.CountAll(&dbdata.User{})
		err = dbdata.Find(&datas, pageSize, page)
	}

	if err != nil && !dbdata.CheckErrNotFound(err) {
		RespError(w, RespInternalErr, err)
		return
	}

	data := map[string]interface{}{
		"count":     count,
		"page_size": pageSize,
		"datas":     datas,
	}

	RespSucess(w, data)
}

func UserDetail(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	idS := r.FormValue("id")
	id, _ := strconv.Atoi(idS)
	if id < 1 {
		RespError(w, RespParamErr, "用户名错误")
		return
	}

	var user dbdata.User
	err := dbdata.One("Id", id, &user)
	if err != nil {
		RespError(w, RespInternalErr, err)
		return
	}

	RespSucess(w, user)
}

func UserSet(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()

	body, err := io.ReadAll(r.Body)
	if err != nil {
		RespError(w, RespInternalErr, err)
		return
	}
	defer r.Body.Close()
	data := &dbdata.User{}
	err = json.Unmarshal(body, data)
	if err != nil {
		RespError(w, RespInternalErr, err)
		return
	}

	if len(data.PinCode) < 6 {
		data.PinCode = utils.RandomRunes(8)
		base.Info("用户", data.Username, "随机密码为:", data.PinCode)
	}
	plainpwd := data.PinCode
	err = dbdata.SetUser(data)
	if err != nil {
		RespError(w, RespInternalErr, err)
		return
	}
	data.PinCode = plainpwd

	// 发送邮件
	if data.SendEmail {
		err = userAccountMail(data)
		if err != nil {
			RespError(w, RespInternalErr, err)
			return
		}
	}
	// 修改用户资料后执行过期用户检测
	sessdata.CloseUserLimittimeSession()
	RespSucess(w, nil)
}

func UserDel(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	idS := r.FormValue("id")
	id, _ := strconv.Atoi(idS)

	if id < 1 {
		RespError(w, RespParamErr, "用户id错误")
		return
	}

	user := dbdata.User{Id: id}
	err := dbdata.Del(&user)
	if err != nil {
		RespError(w, RespInternalErr, err)
		return
	}
	RespSucess(w, nil)
}

func UserOtpQr(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	b64S := r.FormValue("b64")
	idS := r.FormValue("id")
	id, _ := strconv.Atoi(idS)

	var b64 bool
	if b64S == "1" {
		b64 = true
	}
	data, err := userOtpQr(id, b64)
	if err != nil {
		base.Error(err)
	}
	io.WriteString(w, data)
}

func userOtpQr(uid int, b64 bool) (string, error) {
	var user dbdata.User
	err := dbdata.One("Id", uid, &user)
	if err != nil {
		return "", err
	}

	issuer := url.QueryEscape(base.Cfg.Issuer)
	qrstr := fmt.Sprintf("otpauth://totp/%s:%s?issuer=%s&secret=%s", issuer, user.Email, issuer, user.OtpSecret)
	qr, _ := qrcode.New(qrstr, qrcode.High)

	if b64 {
		data, err := qr.PNG(300)
		if err != nil {
			return "", err
		}
		s := base64.StdEncoding.EncodeToString(data)
		return s, nil
	}

	buf := bytes.NewBuffer(nil)
	err = qr.Write(300, buf)
	return buf.String(), err
}

// 在线用户
func UserOnline(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	search_cate := r.FormValue("search_cate")
	search_text := r.FormValue("search_text")
	show_sleeper := r.FormValue("show_sleeper")
	showSleeper, _ := strconv.ParseBool(show_sleeper)
	// one_offline := r.FormValue("one_offline")

	// datas := sessdata.OnlineSess()
	datas := sessdata.GetOnlineSess(search_cate, search_text, showSleeper)

	data := map[string]interface{}{
		"count":     len(datas),
		"page_size": dbdata.PageSize,
		"datas":     datas,
	}

	RespSucess(w, data)
}

func UserOffline(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	token := r.FormValue("token")
	sessdata.CloseSess(token, dbdata.UserLogoutAdmin)
	RespSucess(w, nil)
}

func UserReline(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	token := r.FormValue("token")
	sessdata.CloseCSess(token)
	RespSucess(w, nil)
}

// 批量发送邮件
func UserBatchSendEmail(w http.ResponseWriter, r *http.Request) {
	var req struct {
		UserIds []int `json:"user_ids"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		RespError(w, RespInternalErr, err)
		return
	}

	if len(req.UserIds) == 0 {
		RespError(w, RespInternalErr, errors.New("用户ID列表不能为空"))
		return
	}

	successCount := 0
	failCount := 0

	for _, userId := range req.UserIds {
		user := &dbdata.User{}
		err := dbdata.One("Id", userId, user)
		if err != nil {
			failCount++
			continue
		}

		// 发送邮件
		err = userAccountMail(user)
		if err != nil {
			base.Error("批量发送邮件失败:", user.Username, err)
			failCount++
		} else {
			successCount++
		}
	}

	msg := fmt.Sprintf("批量发送邮件完成，成功：%d，失败：%d", successCount, failCount)

	if successCount > 0 {
		RespSucess(w, msg)
	} else {
		RespError(w, RespInternalErr, errors.New(msg))
	}
}

// 批量删除用户
func UserBatchDelete(w http.ResponseWriter, r *http.Request) {
	var req struct {
		UserIds []int `json:"user_ids"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		RespError(w, RespInternalErr, err)
		return
	}

	if len(req.UserIds) == 0 {
		RespError(w, RespInternalErr, errors.New("用户ID列表不能为空"))
		return
	}

	successCount := 0
	failCount := 0

	for _, userId := range req.UserIds {
		user := &dbdata.User{}
		err := dbdata.One("Id", userId, user)
		if err != nil {
			failCount++
			continue
		}

		err = dbdata.Del(user)
		if err != nil {
			base.Error("批量删除用户失败:", user.Username, err)
			failCount++
		} else {
			successCount++
		}
	}

	msg := fmt.Sprintf("批量删除完成，成功：%d，失败：%d", successCount, failCount)

	if successCount > 0 {
		RespSucess(w, msg)
	} else {
		RespError(w, RespInternalErr, errors.New(msg))
	}
}

type userAccountMailData struct {
	Issuer       string
	LinkAddr     string
	Group        string
	Username     string
	Nickname     string
	PinCode      string
	LimitTime    string
	OtpImg       string
	OtpImgBase64 string
	DisableOtp   bool
}

func userAccountMail(user *dbdata.User) error {
	// 平台通知
	htmlBody := `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Hello AnyLink!</title>
</head>
<body>
%s
</body>
</html>
`
	dataOther := &dbdata.SettingOther{}
	err := dbdata.SettingGet(dataOther)
	if err != nil {
		base.Error(err)
		return err
	}
	htmlBody = fmt.Sprintf(htmlBody, dataOther.AccountMail)
	// fmt.Println(htmlBody)

	// token有效期3天
	expiresAt := time.Now().Unix() + 3600*24*3
	jwtData := map[string]interface{}{"id": user.Id}
	tokenString, err := SetJwtData(jwtData, expiresAt)
	if err != nil {
		return err
	}

	setting := &dbdata.SettingOther{}
	err = dbdata.SettingGet(setting)
	if err != nil {
		base.Error(err)
		return err
	}

	otpData, _ := userOtpQr(user.Id, true)

	data := userAccountMailData{
		Issuer:       base.Cfg.Issuer,
		LinkAddr:     setting.LinkAddr,
		Group:        strings.Join(user.Groups, ","),
		Username:     user.Username,
		Nickname:     user.Nickname,
		PinCode:      user.PinCode,
		OtpImg:       fmt.Sprintf("https://%s/otp_qr?id=%d&jwt=%s", setting.LinkAddr, user.Id, tokenString),
		OtpImgBase64: "data:image/png;base64," + otpData,
		DisableOtp:   user.DisableOtp,
	}

	if user.Type == "ldap" {
		data.PinCode = "同ldap密码"
	}

	if user.LimitTime == nil {
		data.LimitTime = "无限制"
	} else {
		data.LimitTime = user.LimitTime.Local().Format("2006-01-02")
	}

	w := bytes.NewBufferString("")
	t, _ := template.New("auth_complete").Parse(htmlBody)
	err = t.Execute(w, data)
	if err != nil {
		return err
	}
	// fmt.Println(w.String())

	var attach *mail.File
	if user.DisableOtp {
		attach = nil
	} else {
		imgData, _ := userOtpQr(user.Id, false)
		attach = &mail.File{
			MimeType: "image/png",
			Name:     "userOtpQr.png",
			Data:     []byte(imgData),
			Inline:   true,
		}
	}

	return SendMail(base.Cfg.Issuer, user.Email, w.String(), attach)
}
