package pop3_server

import (
	"database/sql"
	"github.com/Jinnrry/gopop"
	"github.com/Jinnrry/pmail/consts"
	"github.com/Jinnrry/pmail/db"
	"github.com/Jinnrry/pmail/dto"
	"github.com/Jinnrry/pmail/models"
	"github.com/Jinnrry/pmail/services/del_email"
	"github.com/Jinnrry/pmail/services/detail"
	"github.com/Jinnrry/pmail/services/list"
	"github.com/Jinnrry/pmail/utils/array"
	"github.com/Jinnrry/pmail/utils/context"
	"github.com/Jinnrry/pmail/utils/errors"
	"github.com/Jinnrry/pmail/utils/id"
	"github.com/Jinnrry/pmail/utils/password"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cast"
	"strings"
)

type action struct {
}

// Custom 非标准命令
func (a action) Custom(session *gopop.Session, cmd string, args []string) ([]string, error) {
	if session.Ctx == nil {
		tc := &context.Context{}
		tc.SetValue(context.LogID, id.GenLogID())
		session.Ctx = tc
	}

	log.WithContext(session.Ctx).Warnf("not supported cmd request! cmd:%s args:%v", cmd, args)
	return nil, nil
}

// Capa 说明服务端支持的命令列表
func (a action) Capa(session *gopop.Session) ([]string, error) {
	if session.Ctx == nil {
		tc := &context.Context{}
		tc.SetValue(context.LogID, id.GenLogID())
		session.Ctx = tc
	}

	if session.InTls {
		log.WithContext(session.Ctx).Debugf("POP3 CMD: CAPA With Tls")
	} else {
		log.WithContext(session.Ctx).Debugf("POP3 CMD: CAPA Without Tls")
	}

	ret := []string{
		"USER",
		"PASS",
		"TOP",
		"APOP",
		"STAT",
		"UIDL",
		"LIST",
		"RETR",
		"DELE",
		"REST",
		"NOOP",
		"QUIT",
	}
	if !session.InTls {
		ret = append(ret, "STLS")
	}

	return ret, nil
}

// User 提交登陆的用户名
func (a action) User(session *gopop.Session, username string) error {
	if session.Ctx == nil {
		tc := &context.Context{}
		tc.SetValue(context.LogID, id.GenLogID())
		session.Ctx = tc
	}
	log.WithContext(session.Ctx).Debugf("POP3 CMD: USER, Args:%s", username)

	infos := strings.Split(username, "@")
	if len(infos) > 1 {
		username = infos[0]
	}

	log.WithContext(session.Ctx).Debugf("POP3 User %s", username)

	session.User = username
	return nil
}

// Pass 提交密码验证
func (a action) Pass(session *gopop.Session, pwd string) error {
	if session.Ctx == nil {
		tc := &context.Context{}
		tc.SetValue(context.LogID, id.GenLogID())
		session.Ctx = tc
	}

	log.WithContext(session.Ctx).Debugf("POP3 PASS %s , User:%s", pwd, session.User)

	var user models.User

	encodePwd := password.Encode(pwd)

	_, err := db.Instance.Where("account =? and password =? and disabled = 0", session.User, encodePwd).Get(&user)
	if err != nil && !errors.Is(err, sql.ErrNoRows) {
		log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
	}

	if user.ID > 0 {
		session.Status = gopop.TRANSACTION

		session.Ctx.(*context.Context).UserID = user.ID
		session.Ctx.(*context.Context).UserName = user.Name
		session.Ctx.(*context.Context).UserAccount = user.Account

		return nil
	}

	return errors.New("password error")
}

// Apop APOP登陆命令
func (a action) Apop(session *gopop.Session, username, digest string) error {
	if session.Ctx == nil {
		tc := &context.Context{}
		tc.SetValue(context.LogID, id.GenLogID())
		session.Ctx = tc
	}
	log.WithContext(session.Ctx).Debugf("POP3 CMD: APOP, Args:%s,%s", username, digest)

	infos := strings.Split(username, "@")
	if len(infos) > 1 {
		username = infos[0]
	}

	log.WithContext(session.Ctx).Debugf("POP3 APOP %s %s", username, digest)

	var user models.User

	_, err := db.Instance.Where("account =? and disabled = 0", username).Get(&user)
	if err != nil && !errors.Is(err, sql.ErrNoRows) {
		log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
	}

	if user.ID > 0 && digest == password.Md5Encode(user.Password) {
		session.User = username
		session.Status = gopop.TRANSACTION

		session.Ctx.(*context.Context).UserID = user.ID
		session.Ctx.(*context.Context).UserName = user.Name
		session.Ctx.(*context.Context).UserAccount = user.Account

		return nil
	}

	return errors.New("password error")

}

// Stat 查询邮件数量
func (a action) Stat(session *gopop.Session) (msgNum, msgSize int64, err error) {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: STAT")

	num, size := list.Stat(session.Ctx.(*context.Context))
	log.WithContext(session.Ctx).Debugf("POP3 STAT RETURT : %d,%d", num, size)
	return num, size, nil
}

// Uidl 查询某封邮件的唯一标志符
func (a action) Uidl(session *gopop.Session, msg string) ([]gopop.UidlItem, error) {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: UIDL ,Args:%s", msg)

	reqId := cast.ToInt64(msg)
	if reqId > 0 {
		return []gopop.UidlItem{
			{
				Id:      reqId,
				UnionId: msg,
			},
		}, nil
	}

	var res []listItem

	var err error
	var ssql string

	err = db.Instance.Where("type=0 and status=0").Select("id").Table("email").Find(&res)

	if err != nil && !errors.Is(err, sql.ErrNoRows) {
		log.WithContext(session.Ctx.(*context.Context)).Errorf("SQL:%s  Error: %+v", ssql, err)
		err = nil
		return []gopop.UidlItem{}, nil
	}
	ret := []gopop.UidlItem{}
	for _, re := range res {
		ret = append(ret, gopop.UidlItem{
			Id:      re.Id,
			UnionId: cast.ToString(re.Id),
		})
	}
	return ret, nil
}

type listItem struct {
	Id   int64 `json:"id"`
	Size int64 `json:"size"`
}

// List 邮件列表
func (a action) List(session *gopop.Session, msg string) ([]gopop.MailInfo, error) {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: LIST ,Args:%s", msg)
	var res []listItem
	var listId int
	if msg != "" {
		listId = cast.ToInt(msg)
		if listId == 0 {
			return nil, errors.New("params error")
		}
	}

	if listId != 0 {
		info, err := detail.GetEmailDetail(session.Ctx.(*context.Context), listId, false)
		if err != nil {
			return nil, err
		}
		res = append(res, listItem{
			Id:   cast.ToInt64(info.Id),
			Size: cast.ToInt64(info.Size),
		})
	} else {
		emailList, _ := list.GetEmailList(session.Ctx.(*context.Context), dto.SearchTag{Type: consts.EmailTypeReceive, Status: -1, GroupId: -1}, "", true, 0, 99999)
		for _, info := range emailList {
			res = append(res, listItem{
				Id:   cast.ToInt64(info.Id),
				Size: cast.ToInt64(info.Size),
			})
		}
	}
	ret := []gopop.MailInfo{}
	for _, re := range res {
		ret = append(ret, gopop.MailInfo{
			Id:   re.Id,
			Size: re.Size,
		})
	}
	return ret, nil
}

// Retr 获取邮件详情
func (a action) Retr(session *gopop.Session, id int64) (string, int64, error) {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: RETR ,Args:%d", id)
	email, err := detail.GetEmailDetail(session.Ctx.(*context.Context), cast.ToInt(id), false)
	if err != nil {
		log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
		return "", 0, errors.New("server error")
	}

	ret := email.ToTransObj().BuildBytes(session.Ctx.(*context.Context), false)
	return string(ret), cast.ToInt64(len(ret)), nil

}

// Delete 删除邮件
func (a action) Delete(session *gopop.Session, id int64) error {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: DELE ,Args:%d", id)

	session.DeleteIds = append(session.DeleteIds, id)
	session.DeleteIds = array.Unique(session.DeleteIds)
	return nil
}

func (a action) Rest(session *gopop.Session) error {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: REST ")
	session.DeleteIds = []int64{}
	return nil
}

func (a action) Top(session *gopop.Session, id int64, n int) (string, error) {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: TOP %d %d", id, n)
	email, err := detail.GetEmailDetail(session.Ctx.(*context.Context), cast.ToInt(id), false)
	if err != nil {
		log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
		return "", errors.New("server error")
	}

	ret := email.ToTransObj().BuildBytes(session.Ctx.(*context.Context), false)
	res := strings.Split(string(ret), "\n")
	headerEndLine := len(res) - 1
	for i, re := range res {
		if re == "\r" {
			headerEndLine = i
			break
		}
	}
	if len(res) <= headerEndLine+n+1 {
		return string(ret), nil
	}

	return array.Join(res[0:headerEndLine+n+1], "\n"), nil

}

func (a action) Noop(session *gopop.Session) error {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: NOOP ")
	return nil
}

func (a action) Quit(session *gopop.Session) error {
	log.WithContext(session.Ctx).Debugf("POP3 CMD: QUIT ")
	if len(session.DeleteIds) > 0 {
		del_email.DelEmail(session.Ctx.(*context.Context), session.DeleteIds, false)
	}

	return nil
}
