package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/larkox/mattermost-plugin-badges/badgesmodel"
	"github.com/mattermost/mattermost-server/v5/model"
)

func areRolesAllowed(userRoles []string, allowedRoles map[string]bool) bool {
	for ar, b := range allowedRoles {
		if !b {
			continue
		}
		for _, ur := range userRoles {
			if ar == ur {
				return true
			}
		}
	}

	return false
}

func canGrantBadge(user *model.User, badgeAdminIDs map[string]bool, badge *badgesmodel.Badge, badgeType *badgesmodel.BadgeTypeDefinition) bool {
	if badgeAdminIDs[user.Id] {
		return true
	}

	if user.IsSystemAdmin() {
		return true
	}

	if badgeType.CreatedBy == user.Id {
		return true
	}

	if badge.CreatedBy == user.Id {
		return true
	}

	blocked := badgeType.CanGrant.BlockList[user.Id]
	if blocked {
		return false
	}

	if areRolesAllowed(user.GetRoles(), badgeType.CanGrant.Roles) {
		return true
	}

	allowed := badgeType.CanGrant.AllowList[user.Id]
	if allowed {
		return true
	}

	return badgeType.CanGrant.Everyone
}

func canCreateBadge(user *model.User, badgeAdminIDs map[string]bool, badgeType *badgesmodel.BadgeTypeDefinition) bool {
	if badgeAdminIDs[user.Id] {
		return true
	}

	if user.IsSystemAdmin() {
		return true
	}

	if badgeType.CreatedBy == user.Id {
		return true
	}

	blocked := badgeType.CanCreate.BlockList[user.Id]
	if blocked {
		return false
	}

	if areRolesAllowed(user.GetRoles(), badgeType.CanCreate.Roles) {
		return true
	}

	allowed := badgeType.CanCreate.AllowList[user.Id]
	if allowed {
		return true
	}

	return badgeType.CanCreate.Everyone
}

func canEditType(user *model.User, badgeAdminIDs map[string]bool, badgeType *badgesmodel.BadgeTypeDefinition) bool {
	if badgeAdminIDs[user.Id] {
		return true
	}

	return user.IsSystemAdmin()
}

func canEditBadge(user *model.User, badgeAdminIDs map[string]bool, badge *badgesmodel.Badge, badgeType *badgesmodel.BadgeTypeDefinition) bool {
	if badgeAdminIDs[user.Id] {
		return true
	}

	if user.IsSystemAdmin() {
		return true
	}

	if badgeType != nil && canCreateBadge(user, badgeAdminIDs, badgeType) {
		return true
	}

	return false
}

func canCreateType(user *model.User, badgeAdminIDs map[string]bool, isPlugin bool) bool {
	if isPlugin {
		return true
	}

	if badgeAdminIDs[user.Id] {
		return true
	}

	return user.IsSystemAdmin()
}

func canCreateSubscription(user *model.User, badgeAdminIDs map[string]bool, channelID string) bool {
	if badgeAdminIDs[user.Id] {
		return true
	}

	return user.IsSystemAdmin()
}

func dumpObject(o interface{}) {
	b, err := json.MarshalIndent(o, "", "    ")
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(string(b))
}

func (p *Plugin) notifyGrant(badgeID badgesmodel.BadgeID, granter string, granted *model.User, inChannel bool, channelID string, reason string) {
	b, errBadge := p.store.GetBadgeDetails(badgeID)
	granterUser, errUser := p.mm.User.Get(granter)
	if errBadge != nil {
		p.mm.Log.Debug("badge error", "err", errBadge)
	}
	if errUser != nil {
		p.mm.Log.Debug("user error", "err", errUser)
	}

	subs, _ := p.store.GetTypeSubscriptions(b.Type)

	if errBadge == nil && errUser == nil {
		image := ""
		switch b.ImageType {
		case badgesmodel.ImageTypeEmoji:
			image = fmt.Sprintf(":%s: ", b.Image)
		case badgesmodel.ImageTypeAbsoluteURL:
			image = fmt.Sprintf("![icon](%s) ", b.Image)
		}

		// DM to the granted user — use their locale
		Tdm := p.getT(granted.Locale)
		dmPost := &model.Post{}
		dmText := Tdm("badges.notify.dm_text", "@%s выдал вам значок %s`%s`.", granterUser.Username, image, b.Name)
		if reason != "" {
			dmText += Tdm("badges.notify.dm_reason", "\nПочему? ") + reason
		}
		dmAttachment := model.SlackAttachment{
			Fallback: dmText,
			Title:    Tdm("badges.notify.title", "%sзначок выдан!", image),
			Text:     dmText,
		}
		model.ParseSlackAttachment(dmPost, []*model.SlackAttachment{&dmAttachment})
		err := p.mm.Post.DM(p.BotUserID, granted.Id, dmPost)
		if err != nil {
			p.mm.Log.Debug("dm error", "err", err)
		}

		// Channel/subscription notifications — use granter's locale
		Tch := p.getT(granterUser.Locale)
		basePost := model.Post{
			UserId:    p.BotUserID,
			ChannelId: channelID,
		}
		text := Tch("badges.notify.channel_text", "@%s выдал @%s значок %s`%s`.", granterUser.Username, granted.Username, image, b.Name)
		if reason != "" {
			text += Tch("badges.notify.dm_reason", "\nПочему? ") + reason
		}
		attachment := model.SlackAttachment{
			Title: Tch("badges.notify.title", "%sзначок выдан!", image),
			Text:  text,
		}
		model.ParseSlackAttachment(&basePost, []*model.SlackAttachment{&attachment})
		for _, sub := range subs {
			post := basePost.Clone()
			post.ChannelId = sub
			err := p.mm.Post.CreatePost(post)
			if err != nil {
				p.mm.Log.Debug("notify subscription error", "err", err)
			}
		}
		alreadyNotified := false
		for _, sub := range subs {
			if sub == channelID {
				alreadyNotified = true
				break
			}
		}
		if inChannel && !alreadyNotified {
			if !p.API.HasPermissionToChannel(granter, channelID, model.PERMISSION_CREATE_POST) {
				Tg := p.getT(granterUser.Locale)
				p.mm.Post.SendEphemeralPost(granter, &model.Post{Message: Tg("badges.notify.no_permission_channel", "У вас нет прав на отправку уведомления о выдаче в этот канал."), ChannelId: channelID})
			} else {
				post := basePost.Clone()
				post.ChannelId = channelID
				err := p.mm.Post.CreatePost(post)
				if err != nil {
					p.mm.Log.Debug("notify here error", "err", err)
				}
			}
		}
	}
}

// resolveUsernameList parses a comma-separated list of usernames and returns a map of user IDs.
func (p *Plugin) resolveUsernameList(csv string) (map[string]bool, error) {
	result := map[string]bool{}
	usernames := strings.Split(csv, ",")
	for _, username := range usernames {
		username = strings.TrimSpace(username)
		if username == "" {
			continue
		}
		user, err := p.mm.User.GetByUsername(username)
		if err != nil {
			return nil, fmt.Errorf("user not found: %s", username)
		}
		result[user.Id] = true
	}
	return result, nil
}

// resolveUserIDList converts a map of user IDs to a comma-separated list of usernames.
func (p *Plugin) resolveUserIDList(ids map[string]bool) string {
	var names []string
	for id, allowed := range ids {
		if !allowed {
			continue
		}
		user, err := p.mm.User.Get(id)
		if err != nil {
			continue
		}
		names = append(names, user.Username)
	}
	return strings.Join(names, ", ")
}

func getBooleanString(in bool) string {
	if in {
		return TrueString
	}
	return FalseString
}
