package message

import (
	"context"
	"database/sql"
	"log/slog"
	"regexp"
	"strings"

	"git.dustin.coffee/hobokenchicken/dumpsterChat/internal/push"
)

var mentionRegex = regexp.MustCompile(`<@([0-9a-f-]+)>`)
var everyoneMention = "@everyone"
var roleMentionRegex = regexp.MustCompile(`<@&([0-9a-f-]+)>`)

// MentionHandler dispatches push notifications for @mentions.
type MentionHandler struct {
	db     *sql.DB
	push   *push.Handler
	logger *slog.Logger
}

func NewMentionHandler(db *sql.DB, pushHandler *push.Handler, logger *slog.Logger) *MentionHandler {
	return &MentionHandler{
		db:     db,
		push:   pushHandler,
		logger: logger,
	}
}

// ParseAndNotify parses message content for mentions and sends push notifications.
func (m *MentionHandler) ParseAndNotify(ctx context.Context, channelID, authorID, content string) {
	// Find individual user mentions
	userMatches := mentionRegex.FindAllStringSubmatch(content, -1)
	mentionedUsers := make(map[string]bool)
	for _, match := range userMatches {
		if len(match) > 1 {
			mentionedUsers[match[1]] = true
		}
	}

	// Check for @everyone
	isEveryone := strings.Contains(content, everyoneMention)

	// Get channel info for notification
	var serverID, channelName string
	err := m.db.QueryRowContext(ctx,
		`SELECT server_id, name FROM channels WHERE id = $1`, channelID,
	).Scan(&serverID, &channelName)
	if err != nil {
		m.logger.Error("failed to get channel info for mentions", "error", err)
		return
	}

	// Get author info
	var authorName string
	err = m.db.QueryRowContext(ctx,
		`SELECT COALESCE(display_name, username) FROM users WHERE id = $1`, authorID,
	).Scan(&authorName)
	if err != nil {
		m.logger.Error("failed to get author info for mentions", "error", err)
		return
	}

	// Truncate content for notification
	notifContent := content
	if len(notifContent) > 200 {
		notifContent = notifContent[:200] + "..."
	}

	payload := map[string]interface{}{
		"title": authorName + " in #" + channelName,
		"body":  notifContent,
		"url":   "/channels/" + channelID,
	}

	if isEveryone {
		// Send to all server members except those who muted this channel
		rows, err := m.db.QueryContext(ctx,
			`SELECT m.user_id FROM members m
			 LEFT JOIN notification_settings ns ON ns.user_id = m.user_id AND ns.channel_id = $3
			 WHERE m.server_id = $1 AND m.user_id != $2
			   AND (ns.level IS NULL OR ns.level != 'none')`,
			serverID, authorID, channelID,
		)
		if err != nil {
			m.logger.Error("failed to query server members for @everyone", "error", err)
			return
		}
		defer rows.Close()

		for rows.Next() {
			var userID string
			if err := rows.Scan(&userID); err != nil {
				continue
			}
			go m.push.SendPush(ctx, userID, payload)
		}
		return
	}

	// Check for role mentions
	roleMatches := roleMentionRegex.FindAllStringSubmatch(content, -1)
	if len(roleMatches) > 0 {
		for _, match := range roleMatches {
			if len(match) > 1 {
				roleID := match[1]
				// Get users with this role
				rows, err := m.db.QueryContext(ctx,
					`SELECT user_id FROM member_roles WHERE role_id = $1 AND user_id != $2`,
					roleID, authorID,
				)
				if err != nil {
					m.logger.Error("failed to query role members", "error", err, "role_id", roleID)
					continue
				}
				for rows.Next() {
					var userID string
					if err := rows.Scan(&userID); err != nil {
						continue
					}
					mentionedUsers[userID] = true
				}
				rows.Close()
			}
		}
	}

	// Remove the author from mentions
	delete(mentionedUsers, authorID)

	// Send push to individually mentioned users
	for userID := range mentionedUsers {
		// Check if user is in DND status
		var status string
		err := m.db.QueryRowContext(ctx,
			`SELECT COALESCE(status, 'online') FROM users WHERE id = $1`, userID,
		).Scan(&status)
		if err != nil {
			continue
		}
		if status == "dnd" {
			continue
		}

		// Check if user muted this channel
		var level string
		err = m.db.QueryRowContext(ctx,
			`SELECT level FROM notification_settings WHERE user_id = $1 AND channel_id = $2`,
			userID, channelID,
		).Scan(&level)
		if err == nil && level == "none" {
			continue
		}

		go m.push.SendPush(ctx, userID, payload)
	}
}
