package security

import (
	"context"
	"fmt"
	"net"
	"sync"
	"time"
)

// IPRule IP 规则
type IPRule struct {
	IP        string    // CIDR 或精确 IP
	Reason    string    // 封禁原因
	ExpireAt  time.Time // 过期时间（零值表示永久）
	CreatedAt time.Time
}

// isExpired 是否已过期
func (r *IPRule) isExpired() bool {
	return !r.ExpireAt.IsZero() && time.Now().After(r.ExpireAt)
}

// IPFilter IP 黑白名单过滤器
type IPFilter struct {
	mu        sync.RWMutex
	blacklist map[string]*IPRule // key: IP/CIDR
	whitelist map[string]*IPRule // key: IP/CIDR
}

// NewIPFilter 创建 IP 过滤器
func NewIPFilter() *IPFilter {
	return &IPFilter{
		blacklist: make(map[string]*IPRule),
		whitelist: make(map[string]*IPRule),
	}
}

// AddToBlacklist 将 IP/CIDR 加入黑名单
// duration 为 0 表示永久封禁
func (f *IPFilter) AddToBlacklist(ip, reason string, duration time.Duration) error {
	if err := validateIPOrCIDR(ip); err != nil {
		return err
	}
	f.mu.Lock()
	defer f.mu.Unlock()

	rule := &IPRule{
		IP:        ip,
		Reason:    reason,
		CreatedAt: time.Now(),
	}
	if duration > 0 {
		rule.ExpireAt = time.Now().Add(duration)
	}
	f.blacklist[ip] = rule
	return nil
}

// RemoveFromBlacklist 从黑名单移除
func (f *IPFilter) RemoveFromBlacklist(ip string) {
	f.mu.Lock()
	defer f.mu.Unlock()
	delete(f.blacklist, ip)
}

// AddToWhitelist 将 IP/CIDR 加入白名单
func (f *IPFilter) AddToWhitelist(ip, reason string) error {
	if err := validateIPOrCIDR(ip); err != nil {
		return err
	}
	f.mu.Lock()
	defer f.mu.Unlock()
	f.whitelist[ip] = &IPRule{
		IP:        ip,
		Reason:    reason,
		CreatedAt: time.Now(),
	}
	return nil
}

// RemoveFromWhitelist 从白名单移除
func (f *IPFilter) RemoveFromWhitelist(ip string) {
	f.mu.Lock()
	defer f.mu.Unlock()
	delete(f.whitelist, ip)
}

// IsBlocked 检查 IP 是否被封禁
// 白名单优先：白名单中的 IP 永远不被封禁
func (f *IPFilter) IsBlocked(ip string) (bool, string) {
	f.mu.RLock()
	defer f.mu.RUnlock()

	// 白名单检查（优先）
	if f.matchesAnyRule(ip, f.whitelist) {
		return false, ""
	}

	// 黑名单检查
	for _, rule := range f.blacklist {
		if rule.isExpired() {
			continue
		}
		if matchIP(ip, rule.IP) {
			return true, rule.Reason
		}
	}
	return false, ""
}

// CleanExpired 清理过期规则
func (f *IPFilter) CleanExpired() {
	f.mu.Lock()
	defer f.mu.Unlock()
	for k, rule := range f.blacklist {
		if rule.isExpired() {
			delete(f.blacklist, k)
		}
	}
}

// ListBlacklist 列出黑名单（不含已过期的）
func (f *IPFilter) ListBlacklist() []*IPRule {
	f.mu.RLock()
	defer f.mu.RUnlock()
	var result []*IPRule
	for _, rule := range f.blacklist {
		if !rule.isExpired() {
			result = append(result, rule)
		}
	}
	return result
}

// ListWhitelist 列出白名单
func (f *IPFilter) ListWhitelist() []*IPRule {
	f.mu.RLock()
	defer f.mu.RUnlock()
	var result []*IPRule
	for _, rule := range f.whitelist {
		result = append(result, rule)
	}
	return result
}

// matchesAnyRule 检查 IP 是否匹配任意规则集
func (f *IPFilter) matchesAnyRule(ip string, rules map[string]*IPRule) bool {
	for _, rule := range rules {
		if matchIP(ip, rule.IP) {
			return true
		}
	}
	return false
}

// matchIP 检查 ip 是否匹配 target（精确 IP 或 CIDR）
func matchIP(ip, target string) bool {
	if ip == target {
		return true
	}
	// 尝试 CIDR 匹配
	_, network, err := net.ParseCIDR(target)
	if err != nil {
		return false
	}
	parsed := net.ParseIP(ip)
	return parsed != nil && network.Contains(parsed)
}

// validateIPOrCIDR 验证 IP 或 CIDR 格式
func validateIPOrCIDR(s string) error {
	if net.ParseIP(s) != nil {
		return nil
	}
	if _, _, err := net.ParseCIDR(s); err == nil {
		return nil
	}
	return fmt.Errorf("无效的 IP 或 CIDR 格式: %s", s)
}

// ---- 异常登录检测 ----

// AnomalyEvent 异常登录事件类型
type AnomalyEvent string

const (
	AnomalyBruteForce    AnomalyEvent = "brute_force"    // 暴力破解（短时间大量失败）
	AnomalyNewLocation   AnomalyEvent = "new_location"   // 新地区登录
	AnomalyMultipleIP    AnomalyEvent = "multiple_ip"    // 短时间内多个 IP 登录
	AnomalyOffHours      AnomalyEvent = "off_hours"      // 非工作时间登录（可配置）
	AnomalyNewDevice     AnomalyEvent = "new_device"     // 新设备登录
	AnomalySuspicious    AnomalyEvent = "suspicious"     // 可疑活动（综合判断）
)

// LoginRecord 登录记录
type LoginRecord struct {
	UserID           int64
	IP              string
	Location        string // 登录地区
	DeviceFingerprint string // 设备指纹
	Success         bool
	Timestamp       time.Time
}

// AnomalyDetector 异常登录检测器
type AnomalyDetector struct {
	mu                  sync.Mutex
	records             map[int64][]LoginRecord // userID -> 最近登录记录
	maxRecords          int                     // 每用户保留的最大记录数
	windowSize          time.Duration           // 检测时间窗口
	maxFailures         int                     // 窗口内最大失败次数（触发暴力破解告警）
	maxIPs              int                     // 窗口内最大不同 IP 数（触发多 IP 告警）
	ipFilter            *IPFilter               // 用于自动封禁
	autoBlockDur        time.Duration           // 自动封禁时长
	knownLocationsLimit int                     // 常用地区数量阈值
	knownDevicesLimit   int                     // 已知设备数量阈值
}

// AnomalyDetectorConfig 检测器配置
type AnomalyDetectorConfig struct {
	MaxRecordsPerUser int
	Window            time.Duration
	MaxFailures       int
	MaxDistinctIPs    int
	AutoBlockDuration time.Duration
	// 跨区域检测配置
	KnownLocationsLimit int // 常用地区数量阈值，超过则不再告警新地区（默认 5）
	// 新设备检测配置
	KnownDevicesLimit int // 已知设备数量阈值，超过则不再告警新设备（默认 10）
}

// DefaultAnomalyConfig 默认配置
var DefaultAnomalyConfig = AnomalyDetectorConfig{
	MaxRecordsPerUser:  100,
	Window:             15 * time.Minute,
	MaxFailures:        10,
	MaxDistinctIPs:     5,
	AutoBlockDuration:  30 * time.Minute,
	KnownLocationsLimit: 5,
	KnownDevicesLimit:   10,
}

// NewAnomalyDetector 创建异常登录检测器
func NewAnomalyDetector(cfg AnomalyDetectorConfig, ipFilter *IPFilter) *AnomalyDetector {
	if cfg.KnownLocationsLimit <= 0 {
		cfg.KnownLocationsLimit = 5
	}
	if cfg.KnownDevicesLimit <= 0 {
		cfg.KnownDevicesLimit = 10
	}
	return &AnomalyDetector{
		records:             make(map[int64][]LoginRecord),
		maxRecords:          cfg.MaxRecordsPerUser,
		windowSize:          cfg.Window,
		maxFailures:         cfg.MaxFailures,
		maxIPs:              cfg.MaxDistinctIPs,
		ipFilter:            ipFilter,
		autoBlockDur:        cfg.AutoBlockDuration,
		knownLocationsLimit: cfg.KnownLocationsLimit,
		knownDevicesLimit:   cfg.KnownDevicesLimit,
	}
}

// RecordLogin 记录登录事件，返回检测到的异常列表
// location: 登录地区信息（如"广东省广州市"）
// deviceFingerprint: 设备指纹（如浏览器的UserAgent+屏幕分辨率+时区等组合hash）
func (d *AnomalyDetector) RecordLogin(_ context.Context, userID int64, ip, location, deviceFingerprint string, success bool) []AnomalyEvent {
	d.mu.Lock()
	defer d.mu.Unlock()

	now := time.Now()
	record := LoginRecord{
		UserID:           userID,
		IP:               ip,
		Location:         location,
		DeviceFingerprint: deviceFingerprint,
		Success:         success,
		Timestamp:       now,
	}

	// 追加记录，保留最新的 maxRecords 条
	records := append(d.records[userID], record)
	if len(records) > d.maxRecords {
		records = records[len(records)-d.maxRecords:]
	}
	d.records[userID] = records

	// 检测异常
	return d.detect(userID, records, now)
}

// detect 在持有锁的情况下检测异常
func (d *AnomalyDetector) detect(userID int64, records []LoginRecord, now time.Time) []AnomalyEvent {
	windowStart := now.Add(-d.windowSize)

	var failures int
	ipSet := make(map[string]struct{})
	locationSet := make(map[string]struct{}) // 历史地区集合
	deviceSet := make(map[string]struct{})   // 历史设备集合
	var currentLocation string
	var currentDeviceFingerprint string

	for _, r := range records {
		if r.Timestamp.Before(windowStart) {
			continue
		}
		if !r.Success {
			failures++
		}
		ipSet[r.IP] = struct{}{}
		// 记录当前登录的 location 和 deviceFingerprint（最后一个在窗口内的记录）
		currentLocation = r.Location
		currentDeviceFingerprint = r.DeviceFingerprint
		if r.Location != "" {
			locationSet[r.Location] = struct{}{}
		}
		if r.DeviceFingerprint != "" {
			deviceSet[r.DeviceFingerprint] = struct{}{}
		}
	}

	var events []AnomalyEvent

	// 暴力破解检测
	if failures >= d.maxFailures {
		events = append(events, AnomalyBruteForce)
		// 自动封禁
		if d.ipFilter != nil && len(ipSet) == 1 {
			for ip := range ipSet {
				_ = d.ipFilter.AddToBlacklist(ip,
					fmt.Sprintf("自动封禁：用户 %d 暴力破解检测", userID),
					d.autoBlockDur,
				)
			}
		}
	}

	// 多 IP 登录检测
	if len(ipSet) >= d.maxIPs {
		events = append(events, AnomalyMultipleIP)
	}

	// 新地区登录检测
	// 如果当前登录地区与历史记录都不相同，且历史地区数量在阈值内，则告警
	if currentLocation != "" && len(locationSet) > 0 {
		if _, seen := locationSet[currentLocation]; !seen && len(locationSet) <= d.knownLocationsLimit {
			events = append(events, AnomalyNewLocation)
		}
	}

	// 新设备登录检测
	// 如果当前设备指纹与历史记录都不相同，且历史设备数量在阈值内，则告警
	if currentDeviceFingerprint != "" && len(deviceSet) > 0 {
		if _, seen := deviceSet[currentDeviceFingerprint]; !seen && len(deviceSet) <= d.knownDevicesLimit {
			events = append(events, AnomalyNewDevice)
		}
	}

	return events
}

// GetRecentLogins 获取用户最近的登录记录
func (d *AnomalyDetector) GetRecentLogins(userID int64, limit int) []LoginRecord {
	d.mu.Lock()
	defer d.mu.Unlock()

	records := d.records[userID]
	if len(records) <= limit {
		return records
	}
	return records[len(records)-limit:]
}
