package inspection

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"

	"dc-inspection/internal/config"
	"dc-inspection/internal/worker"

	sshpkg "dc-inspection/pkg/ssh"
)

// SwitchInspector 交换机巡检器
type SwitchInspector struct {
	cfg    *config.SwitchConfig
	pool   *worker.Pool
}

// NewSwitchInspector 创建交换机巡检器
func NewSwitchInspector(cfg *config.SwitchConfig, maxConcurrency int) *SwitchInspector {
	return &SwitchInspector{
		cfg:  cfg,
		pool: worker.NewPool(maxConcurrency, maxConcurrency*2),
	}
}

// SwitchMetrics 交换机巡检指标
type SwitchMetrics struct {
	DeviceName    string             `json:"device_name"`
	Host          string             `json:"host"`
	Reachable     bool               `json:"reachable"`
	CPUUsage      float64            `json:"cpu_usage"`
	MemoryUsage   float64            `json:"memory_usage"`
	Interfaces    []InterfaceStatus  `json:"interfaces"`
	Uptime        string             `json:"uptime"`
	RawOutput     string             `json:"-"`
	Errors        []string           `json:"errors,omitempty"`
}

// InterfaceStatus 接口状态
type InterfaceStatus struct {
	Name       string `json:"name"`
	Status     string `json:"status"`
	InErrors   int    `json:"in_errors"`
	OutErrors  int    `json:"out_errors"`
	InOctets   int64  `json:"in_octets"`
	OutOctets  int64  `json:"out_octets"`
}

// Inspect 执行交换机批量巡检
func (s *SwitchInspector) Inspect() []*SwitchMetrics {
	tasks := make([]worker.Task, 0, len(s.cfg.Devices))

	for _, dev := range s.cfg.Devices {
		device := dev // 避免闭包捕获问题
		task := &worker.GenericTask{
			TaskID: fmt.Sprintf("switch-%s", device.Name),
			Fn: func() (interface{}, error) {
				return s.inspectDevice(device), nil
			},
		}
		tasks = append(tasks, task)
	}

	results := s.pool.RunTasks(tasks)

	metricsList := make([]*SwitchMetrics, 0, len(results))
	for _, result := range results {
		if result.Err != nil {
			log.Printf("[ERROR] 交换机巡检任务 %s 失败: %v", result.TaskID, result.Err)
			continue
		}
		if metrics, ok := result.Data.(*SwitchMetrics); ok {
			metricsList = append(metricsList, metrics)
		}
	}

	return metricsList
}

// inspectDevice 巡检单台交换机
func (s *SwitchInspector) inspectDevice(dev config.SwitchDevice) *SwitchMetrics {
	metrics := &SwitchMetrics{
		DeviceName: dev.Name,
		Host:       dev.Host,
	}

	// 1. 检测连通性
	if err := sshpkg.CheckConnection(dev.Host, dev.Port, 10*time.Second); err != nil {
		metrics.Reachable = false
		metrics.Errors = append(metrics.Errors, fmt.Sprintf("设备不可达: %v", err))
		return metrics
	}
	metrics.Reachable = true

	// 2. SSH 连接并执行命令
	clientCfg := sshpkg.ClientConfig{
		Host:     dev.Host,
		Port:     dev.Port,
		Username: dev.Username,
		Password: dev.Password,
		Timeout:  30 * time.Second,
	}

	client, err := sshpkg.NewClient(clientCfg)
	if err != nil {
		metrics.Errors = append(metrics.Errors, fmt.Sprintf("SSH 连接失败: %v", err))
		return metrics
	}
	defer client.Close()

	// 3. 执行巡检命令
	commands := s.getCommands(dev.Type)
	outputs, err := client.ExecBatch(commands)
	if err != nil {
		metrics.Errors = append(metrics.Errors, fmt.Sprintf("命令执行失败: %v", err))
	}

	// 4. 解析输出
	var rawBuilder strings.Builder
	for cmd, output := range outputs {
		rawBuilder.WriteString(fmt.Sprintf("=== %s ===\n%s\n", cmd, output))
		metrics.RawOutput = rawBuilder.String()

		switch dev.Type {
		case "cisco":
			s.parseCiscoOutput(metrics, cmd, output)
		case "h3c", "huawei":
			s.parseH3COutput(metrics, cmd, output)
		default:
			s.parseGenericOutput(metrics, cmd, output)
		}
	}

	return metrics
}

// getCommands 根据设备类型返回巡检命令
func (s *SwitchInspector) getCommands(devType string) []string {
	// 如果配置了自定义命令，优先使用
	if len(s.cfg.Commands) > 0 {
		return s.cfg.Commands
	}

	switch devType {
	case "cisco":
		return []string{
			"show processes cpu sorted",
			"show processes memory",
			"show interfaces",
			"show version | include uptime",
		}
	case "h3c":
		return []string{
			"display cpu-usage",
			"display memory",
			"display interface",
			"display version",
		}
	case "huawei":
		return []string{
			"display cpu-usage",
			"display memory-usage",
			"display interface",
			"display version",
		}
	default:
		return []string{
			"show version",
			"show interfaces",
		}
	}
}

// parseCiscoOutput 解析 Cisco 交换机输出
func (s *SwitchInspector) parseCiscoOutput(metrics *SwitchMetrics, cmd, output string) {
	if strings.Contains(cmd, "cpu") {
		metrics.CPUUsage = extractPercent(output, "CPU utilization")
	}
	if strings.Contains(cmd, "memory") {
		metrics.MemoryUsage = extractMemoryUsageCisco(output)
	}
	if strings.Contains(cmd, "interface") {
		metrics.Interfaces = parseInterfaces(output)
	}
	if strings.Contains(cmd, "uptime") || strings.Contains(cmd, "version") {
		metrics.Uptime = extractUptime(output)
	}
}

// parseH3COutput 解析 H3C/华为交换机输出
func (s *SwitchInspector) parseH3COutput(metrics *SwitchMetrics, cmd, output string) {
	if strings.Contains(cmd, "cpu") {
		metrics.CPUUsage = extractPercent(output, "CPU usage")
	}
	if strings.Contains(cmd, "memory") {
		metrics.MemoryUsage = extractMemoryUsageH3C(output)
	}
	if strings.Contains(cmd, "interface") {
		metrics.Interfaces = parseInterfaces(output)
	}
	if strings.Contains(cmd, "version") {
		metrics.Uptime = extractUptime(output)
	}
}

// parseGenericOutput 通用解析
func (s *SwitchInspector) parseGenericOutput(metrics *SwitchMetrics, cmd, output string) {
	metrics.CPUUsage = extractPercent(output, "cpu")
	metrics.MemoryUsage = extractPercent(output, "memory")
	if strings.Contains(cmd, "interface") {
		metrics.Interfaces = parseInterfaces(output)
	}
}

// CheckThresholds 检查阈值并生成告警
func (s *SwitchInspector) CheckThresholds(metrics *SwitchMetrics) []string {
	var alerts []string

	if !metrics.Reachable {
		alerts = append(alerts, fmt.Sprintf("设备 %s (%s) 不可达", metrics.DeviceName, metrics.Host))
		return alerts
	}

	if metrics.CPUUsage > s.cfg.CPUThreshold {
		alerts = append(alerts, fmt.Sprintf("设备 %s CPU 使用率 %.1f%% 超过阈值 %.1f%%",
			metrics.DeviceName, metrics.CPUUsage, s.cfg.CPUThreshold))
	}

	if metrics.MemoryUsage > s.cfg.MemoryThreshold {
		alerts = append(alerts, fmt.Sprintf("设备 %s 内存使用率 %.1f%% 超过阈值 %.1f%%",
			metrics.DeviceName, metrics.MemoryUsage, s.cfg.MemoryThreshold))
	}

	for _, iface := range metrics.Interfaces {
		if iface.InErrors > s.cfg.InterfaceErrorThreshold || iface.OutErrors > s.cfg.InterfaceErrorThreshold {
			alerts = append(alerts, fmt.Sprintf("设备 %s 接口 %s 错误包过高 (in:%d out:%d)",
				metrics.DeviceName, iface.Name, iface.InErrors, iface.OutErrors))
		}
	}

	if len(metrics.Errors) > 0 {
		alerts = append(alerts, fmt.Sprintf("设备 %s 巡检异常: %s",
			metrics.DeviceName, strings.Join(metrics.Errors, "; ")))
	}

	return alerts
}

// ToRecord 将巡检指标转换为数据库记录
func (s *SwitchInspector) ToRecord(metrics *SwitchMetrics) *Record {
	status := "ok"
	alerts := s.CheckThresholds(metrics)
	if !metrics.Reachable || len(metrics.Errors) > 0 {
		status = "critical"
	} else if len(alerts) > 0 {
		status = "warning"
	}

	metricsJSON, _ := json.Marshal(metrics)

	return &Record{
		InspectionType: "switch",
		Target:         metrics.DeviceName,
		Status:         status,
		Metrics:        string(metricsJSON),
		Details:        metrics.RawOutput,
		Message:        strings.Join(alerts, "\n"),
		InspectedAt:    time.Now(),
	}
}

// 辅助解析函数

// extractPercent 从文本中提取百分比数值
func extractPercent(text, pattern string) float64 {
	re := regexp.MustCompile(`(?i)` + regexp.QuoteMeta(pattern) + `[^0-9]*(\d+\.?\d*)\s*%`)
	matches := re.FindStringSubmatch(text)
	if len(matches) >= 2 {
		val, err := strconv.ParseFloat(matches[1], 64)
		if err == nil {
			return val
		}
	}
	return 0
}

// extractMemoryUsageCisco 解析 Cisco 内存使用率
func extractMemoryUsageCisco(text string) float64 {
	// Cisco 格式: Processor Pool Total: xxx Used: yyy Free: zzz
	re := regexp.MustCompile(`Total:\s*(\d+)\s+Used:\s*(\d+)`)
	matches := re.FindStringSubmatch(text)
	if len(matches) >= 3 {
		total, _ := strconv.ParseFloat(matches[1], 64)
		used, _ := strconv.ParseFloat(matches[2], 64)
		if total > 0 {
			return (used / total) * 100
		}
	}
	return 0
}

// extractMemoryUsageH3C 解析 H3C 内存使用率
func extractMemoryUsageH3C(text string) float64 {
	// H3C 格式: Memory utilization: xx%
	re := regexp.MustCompile(`(?i)utilization[^0-9]*(\d+\.?\d*)\s*%`)
	matches := re.FindStringSubmatch(text)
	if len(matches) >= 2 {
		val, err := strconv.ParseFloat(matches[1], 64)
		if err == nil {
			return val
		}
	}
	return 0
}

// parseInterfaces 解析接口状态
func parseInterfaces(text string) []InterfaceStatus {
	var interfaces []InterfaceStatus
	lines := strings.Split(text, "\n")

	for _, line := range lines {
		line = strings.TrimSpace(line)
		// 匹配接口名行，如 GigabitEthernet0/0/1
		if isInterfaceLine(line) {
			iface := InterfaceStatus{
				Name:   extractInterfaceName(line),
				Status: extractInterfaceStatus(line),
			}
			if iface.Name != "" {
				interfaces = append(interfaces, iface)
			}
		}
	}

	return interfaces
}

// isInterfaceLine 判断是否为接口信息行
func isInterfaceLine(line string) bool {
	patterns := []string{"Ethernet", "eth", "Gigabit", "Ten-Gig", "Vlan", "Loopback"}
	for _, p := range patterns {
		if strings.Contains(line, p) {
			return true
		}
	}
	return false
}

// extractInterfaceName 提取接口名
func extractInterfaceName(line string) string {
	parts := strings.Fields(line)
	if len(parts) > 0 {
		return parts[0]
	}
	return ""
}

// extractInterfaceStatus 提取接口状态
func extractInterfaceStatus(line string) string {
	if strings.Contains(line, "up") && strings.Contains(line, "down") {
		// 如 "up, line protocol is down" 或 "down, line protocol is down"
		if strings.Contains(line, "administratively") {
			return "administratively down"
		}
		if strings.HasPrefix(line, strings.Fields(line)[0]+" is up") {
			return "up"
		}
		return "down"
	}
	if strings.Contains(line, "up") {
		return "up"
	}
	return "down"
}

// extractUptime 提取设备运行时间
func extractUptime(text string) string {
	// Cisco: "xxx uptime is y weeks, z days, ..."
	re := regexp.MustCompile(`(?i)uptime is (.+)`)
	matches := re.FindStringSubmatch(text)
	if len(matches) >= 2 {
		return strings.TrimSpace(matches[1])
	}
	// H3C: "xxx uptime is y days, z hours, ..."
	re = regexp.MustCompile(`(?i)uptime\s+(.+)`)
	matches = re.FindStringSubmatch(text)
	if len(matches) >= 2 {
		return strings.TrimSpace(matches[1])
	}
	return ""
}
