package models

import (
	"context"
	"github.com/jmoiron/sqlx"
	"gogs.mikescher.com/BlackForestBytes/goext/langext"
	"gogs.mikescher.com/BlackForestBytes/goext/sq"
	"strings"
	"time"
)

type TokenPerm string //@enum:type

const (
	PermAdmin       TokenPerm = "A"  // Edit userdata (+ includes all other permissions)
	PermChannelRead TokenPerm = "CR" // Read messages
	PermChannelSend TokenPerm = "CS" // Send messages
	PermUserRead    TokenPerm = "UR" // Read userdata
)

type TokenPermissionList []TokenPerm

func (e TokenPermissionList) Any(p ...TokenPerm) bool {
	for _, v1 := range e {
		for _, v2 := range p {
			if v1 == v2 {
				return true
			}
		}
	}
	return false
}

func (e TokenPermissionList) String() string {
	return strings.Join(langext.ArrMap(e, func(v TokenPerm) string { return string(v) }), ";")
}

func ParseTokenPermissionList(input string) TokenPermissionList {
	r := make([]TokenPerm, 0, len(input))
	for _, v := range strings.Split(input, ";") {
		if vv, ok := ParseTokenPerm(v); ok {
			r = append(r, vv)
		}
	}
	return r
}

type KeyToken struct {
	KeyTokenID        KeyTokenID
	Name              string
	TimestampCreated  time.Time
	TimestampLastUsed *time.Time
	OwnerUserID       UserID
	AllChannels       bool
	Channels          []ChannelID // can also be owned by other user (needs active subscription)
	Token             string
	Permissions       TokenPermissionList
	MessagesSent      int
}

func (k KeyToken) IsUserRead(uid UserID) bool {
	return k.OwnerUserID == uid && k.Permissions.Any(PermAdmin, PermUserRead)
}

func (k KeyToken) IsAllMessagesRead(uid UserID) bool {
	return k.OwnerUserID == uid && k.AllChannels == true && k.Permissions.Any(PermAdmin, PermChannelRead)
}

func (k KeyToken) IsChannelMessagesRead(cid ChannelID) bool {
	return (k.AllChannels == true || langext.InArray(cid, k.Channels)) && k.Permissions.Any(PermAdmin, PermChannelRead)
}

func (k KeyToken) IsAdmin(uid UserID) bool {
	return k.OwnerUserID == uid && k.Permissions.Any(PermAdmin)
}

func (k KeyToken) IsChannelMessagesSend(c Channel) bool {
	return (k.AllChannels == true || langext.InArray(c.ChannelID, k.Channels)) && k.OwnerUserID == c.OwnerUserID && k.Permissions.Any(PermAdmin, PermChannelSend)
}

func (k KeyToken) JSON() KeyTokenJSON {
	return KeyTokenJSON{
		KeyTokenID:        k.KeyTokenID,
		Name:              k.Name,
		TimestampCreated:  k.TimestampCreated,
		TimestampLastUsed: k.TimestampLastUsed,
		OwnerUserID:       k.OwnerUserID,
		AllChannels:       k.AllChannels,
		Channels:          k.Channels,
		Permissions:       k.Permissions.String(),
		MessagesSent:      k.MessagesSent,
	}
}

func (k KeyToken) JSONPreview() KeyTokenPreviewJSON {
	return KeyTokenPreviewJSON{
		KeyTokenID:  k.KeyTokenID,
		Name:        k.Name,
		OwnerUserID: k.OwnerUserID,
		AllChannels: k.AllChannels,
		Channels:    k.Channels,
		Permissions: k.Permissions.String(),
	}
}

type KeyTokenJSON struct {
	KeyTokenID        KeyTokenID  `json:"keytoken_id"`
	Name              string      `json:"name"`
	TimestampCreated  time.Time   `json:"timestamp_created"`
	TimestampLastUsed *time.Time  `json:"timestamp_lastused"`
	OwnerUserID       UserID      `json:"owner_user_id"`
	AllChannels       bool        `json:"all_channels"`
	Channels          []ChannelID `json:"channels"`
	Permissions       string      `json:"permissions"`
	MessagesSent      int         `json:"messages_sent"`
}

type KeyTokenWithTokenJSON struct {
	KeyTokenJSON
	Token string `json:"token"`
}

type KeyTokenPreviewJSON struct {
	KeyTokenID  KeyTokenID  `json:"keytoken_id"`
	Name        string      `json:"name"`
	OwnerUserID UserID      `json:"owner_user_id"`
	AllChannels bool        `json:"all_channels"`
	Channels    []ChannelID `json:"channels"`
	Permissions string      `json:"permissions"`
}

func (j KeyTokenJSON) WithToken(tok string) KeyTokenWithTokenJSON {
	return KeyTokenWithTokenJSON{
		KeyTokenJSON: j,
		Token:        tok,
	}
}

type KeyTokenDB struct {
	KeyTokenID        KeyTokenID `db:"keytoken_id"`
	Name              string     `db:"name"`
	TimestampCreated  int64      `db:"timestamp_created"`
	TimestampLastUsed *int64     `db:"timestamp_lastused"`
	OwnerUserID       UserID     `db:"owner_user_id"`
	AllChannels       bool       `db:"all_channels"`
	Channels          string     `db:"channels"`
	Token             string     `db:"token"`
	Permissions       string     `db:"permissions"`
	MessagesSent      int        `db:"messages_sent"`
}

func (k KeyTokenDB) Model() KeyToken {

	channels := make([]ChannelID, 0)
	if strings.TrimSpace(k.Channels) != "" {
		channels = langext.ArrMap(strings.Split(k.Channels, ";"), func(v string) ChannelID { return ChannelID(v) })
	}

	return KeyToken{
		KeyTokenID:        k.KeyTokenID,
		Name:              k.Name,
		TimestampCreated:  timeFromMilli(k.TimestampCreated),
		TimestampLastUsed: timeOptFromMilli(k.TimestampLastUsed),
		OwnerUserID:       k.OwnerUserID,
		AllChannels:       k.AllChannels,
		Channels:          channels,
		Token:             k.Token,
		Permissions:       ParseTokenPermissionList(k.Permissions),
		MessagesSent:      k.MessagesSent,
	}
}

func DecodeKeyToken(ctx context.Context, q sq.Queryable, r *sqlx.Rows) (KeyToken, error) {
	data, err := sq.ScanSingle[KeyTokenDB](ctx, q, r, sq.SModeFast, sq.Safe, true)
	if err != nil {
		return KeyToken{}, err
	}
	return data.Model(), nil
}

func DecodeKeyTokens(ctx context.Context, q sq.Queryable, r *sqlx.Rows) ([]KeyToken, error) {
	data, err := sq.ScanAll[KeyTokenDB](ctx, q, r, sq.SModeFast, sq.Safe, true)
	if err != nil {
		return nil, err
	}
	return langext.ArrMap(data, func(v KeyTokenDB) KeyToken { return v.Model() }), nil
}
