package models

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

const (
	ContentLengthTrim  = 1900
	ContentLengthShort = 200
)

type Message struct {
	MessageID           MessageID
	SenderUserID        UserID // user that sent the message (this is also the owner of the channel that contains it)
	ChannelInternalName string
	ChannelID           ChannelID
	SenderName          *string
	SenderIP            string
	TimestampReal       time.Time
	TimestampClient     *time.Time
	Title               string
	Content             *string
	Priority            int
	UserMessageID       *string
	UsedKeyID           KeyTokenID
	Deleted             bool
}

func (m Message) FullJSON() MessageJSON {
	return MessageJSON{
		MessageID:           m.MessageID,
		SenderUserID:        m.SenderUserID,
		ChannelInternalName: m.ChannelInternalName,
		ChannelID:           m.ChannelID,
		SenderName:          m.SenderName,
		SenderIP:            m.SenderIP,
		Timestamp:           m.Timestamp().Format(time.RFC3339Nano),
		Title:               m.Title,
		Content:             m.Content,
		Priority:            m.Priority,
		UserMessageID:       m.UserMessageID,
		UsedKeyID:           m.UsedKeyID,
		Trimmed:             false,
	}
}

func (m Message) TrimmedJSON() MessageJSON {
	return MessageJSON{
		MessageID:           m.MessageID,
		SenderUserID:        m.SenderUserID,
		ChannelInternalName: m.ChannelInternalName,
		ChannelID:           m.ChannelID,
		SenderName:          m.SenderName,
		SenderIP:            m.SenderIP,
		Timestamp:           m.Timestamp().Format(time.RFC3339Nano),
		Title:               m.Title,
		Content:             m.TrimmedContent(),
		Priority:            m.Priority,
		UserMessageID:       m.UserMessageID,
		UsedKeyID:           m.UsedKeyID,
		Trimmed:             m.NeedsTrim(),
	}
}

func (m Message) Timestamp() time.Time {
	return langext.Coalesce(m.TimestampClient, m.TimestampReal)
}

func (m Message) NeedsTrim() bool {
	return m.Content != nil && len(*m.Content) > ContentLengthTrim
}

func (m Message) TrimmedContent() *string {
	if m.Content == nil {
		return nil
	}
	if !m.NeedsTrim() {
		return m.Content
	}
	return langext.Ptr(langext.Coalesce(m.Content, "")[0:ContentLengthTrim-3] + "...")
}

func (m Message) ShortContent() string {
	if m.Content == nil {
		return ""
	}
	if len(*m.Content) < ContentLengthShort {
		return *m.Content
	}
	return (*m.Content)[0:ContentLengthShort-3] + "..."
}

func (m Message) FormatNotificationTitle(user User, channel Channel) string {
	if m.ChannelInternalName == user.DefaultChannel() {
		return m.Title
	}

	return fmt.Sprintf("[%s] %s", channel.DisplayName, m.Title)
}

type MessageJSON struct {
	MessageID           MessageID  `json:"message_id"`
	SenderUserID        UserID     `json:"sender_user_id"`
	ChannelInternalName string     `json:"channel_internal_name"`
	ChannelID           ChannelID  `json:"channel_id"`
	SenderName          *string    `json:"sender_name"`
	SenderIP            string     `json:"sender_ip"`
	Timestamp           string     `json:"timestamp"`
	Title               string     `json:"title"`
	Content             *string    `json:"content"`
	Priority            int        `json:"priority"`
	UserMessageID       *string    `json:"usr_message_id"`
	UsedKeyID           KeyTokenID `json:"used_key_id"`
	Trimmed             bool       `json:"trimmed"`
}

type MessageDB struct {
	MessageID           MessageID  `db:"message_id"`
	SenderUserID        UserID     `db:"sender_user_id"`
	ChannelInternalName string     `db:"channel_internal_name"`
	ChannelID           ChannelID  `db:"channel_id"`
	SenderName          *string    `db:"sender_name"`
	SenderIP            string     `db:"sender_ip"`
	TimestampReal       int64      `db:"timestamp_real"`
	TimestampClient     *int64     `db:"timestamp_client"`
	Title               string     `db:"title"`
	Content             *string    `db:"content"`
	Priority            int        `db:"priority"`
	UserMessageID       *string    `db:"usr_message_id"`
	UsedKeyID           KeyTokenID `db:"used_key_id"`
	Deleted             int        `db:"deleted"`
}

func (m MessageDB) Model() Message {
	return Message{
		MessageID:           m.MessageID,
		SenderUserID:        m.SenderUserID,
		ChannelInternalName: m.ChannelInternalName,
		ChannelID:           m.ChannelID,
		SenderName:          m.SenderName,
		SenderIP:            m.SenderIP,
		TimestampReal:       timeFromMilli(m.TimestampReal),
		TimestampClient:     timeOptFromMilli(m.TimestampClient),
		Title:               m.Title,
		Content:             m.Content,
		Priority:            m.Priority,
		UserMessageID:       m.UserMessageID,
		UsedKeyID:           m.UsedKeyID,
		Deleted:             m.Deleted != 0,
	}
}

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

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