package config

import (
	"encoding/json"
	"fmt"
	"os"
	"sync"
)

// BlocklistMode is either "blacklist" or "whitelist".
type BlocklistSettings struct {
	Mode     string   `json:"mode"`      // "blacklist" (default) or "whitelist"
	GroupIDs []string `json:"group_ids"` // group IDs to block/allow
	UserIDs  []string `json:"user_ids"`  // private chat user IDs to block/allow
}

// BlocklistStore manages persistence of blocklist settings.
type BlocklistStore struct {
	mu       sync.RWMutex
	path     string
	settings BlocklistSettings
}

// NewBlocklistStore loads or creates blocklist settings file.
func NewBlocklistStore(path string) (*BlocklistStore, error) {
	s := &BlocklistStore{
		path: path,
		settings: BlocklistSettings{
			Mode:     "blacklist",
			GroupIDs: []string{},
			UserIDs:  []string{},
		},
	}
	if err := s.load(); err != nil {
		return nil, err
	}
	return s, nil
}

func (s *BlocklistStore) load() error {
	data, err := os.ReadFile(s.path)
	if err != nil {
		if os.IsNotExist(err) {
			return s.save() // write defaults
		}
		return fmt.Errorf("read blocklist file: %w", err)
	}
	if len(data) == 0 {
		return nil
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	if err := json.Unmarshal(data, &s.settings); err != nil {
		return fmt.Errorf("parse blocklist file: %w", err)
	}
	if s.settings.Mode == "" {
		s.settings.Mode = "blacklist"
	}
	if s.settings.GroupIDs == nil {
		s.settings.GroupIDs = []string{}
	}
	if s.settings.UserIDs == nil {
		s.settings.UserIDs = []string{}
	}
	return nil
}

func (s *BlocklistStore) save() error {
	s.mu.RLock()
	data, err := json.MarshalIndent(s.settings, "", "  ")
	s.mu.RUnlock()
	if err != nil {
		return fmt.Errorf("marshal blocklist: %w", err)
	}
	tmpPath := s.path + ".tmp"
	if err := os.WriteFile(tmpPath, data, 0640); err != nil {
		return fmt.Errorf("write blocklist: %w", err)
	}
	return os.Rename(tmpPath, s.path)
}

// Get returns current blocklist settings.
func (s *BlocklistStore) Get() BlocklistSettings {
	s.mu.RLock()
	defer s.mu.RUnlock()
	cp := BlocklistSettings{
		Mode:     s.settings.Mode,
		GroupIDs: make([]string, len(s.settings.GroupIDs)),
		UserIDs:  make([]string, len(s.settings.UserIDs)),
	}
	copy(cp.GroupIDs, s.settings.GroupIDs)
	copy(cp.UserIDs, s.settings.UserIDs)
	return cp
}

// Set updates and persists blocklist settings.
func (s *BlocklistStore) Set(bs BlocklistSettings) error {
	if bs.Mode != "blacklist" && bs.Mode != "whitelist" {
		return fmt.Errorf("invalid mode: %s (must be blacklist or whitelist)", bs.Mode)
	}
	if bs.GroupIDs == nil {
		bs.GroupIDs = []string{}
	}
	if bs.UserIDs == nil {
		bs.UserIDs = []string{}
	}
	s.mu.Lock()
	s.settings = bs
	s.mu.Unlock()
	return s.save()
}

// IsBlocked checks whether a message should be blocked based on channel type and ID.
// In blacklist mode: returns true if the id is IN the list.
// In whitelist mode: returns true if the id is NOT in the list.
// Admin users should call this with isAdmin=true to always bypass.
func (s *BlocklistStore) IsBlocked(channelType, channelID, senderID string, isAdmin bool) bool {
	if isAdmin {
		return false
	}
	s.mu.RLock()
	defer s.mu.RUnlock()

	switch s.settings.Mode {
	case "whitelist":
		// Block if NOT in the whitelist.
		if channelType == "group" {
			return !contains(s.settings.GroupIDs, channelID)
		}
		return !contains(s.settings.UserIDs, senderID)

	case "blacklist":
		fallthrough
	default:
		// Block if IN the blacklist.
		if channelType == "group" {
			return contains(s.settings.GroupIDs, channelID)
		}
		return contains(s.settings.UserIDs, senderID)
	}
}

func contains(list []string, val string) bool {
	for _, v := range list {
		if v == val {
			return true
		}
	}
	return false
}
