package importer

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/vstockwell/wraith/internal/plugin"
)

// MobaConfImporter parses MobaXTerm .mobaconf configuration files and extracts
// connections, groups, host keys, and terminal theme settings.
type MobaConfImporter struct{}

func (m *MobaConfImporter) Name() string              { return "MobaXTerm" }
func (m *MobaConfImporter) FileExtensions() []string   { return []string{".mobaconf"} }

// Parse reads a .mobaconf file and returns the extracted import result.
func (m *MobaConfImporter) Parse(data []byte) (*plugin.ImportResult, error) {
	result := &plugin.ImportResult{
		Groups:      []plugin.ImportGroup{},
		Connections: []plugin.ImportConnection{},
		HostKeys:    []plugin.ImportHostKey{},
	}

	sections := parseSections(string(data))

	// Parse [Bookmarks_N] sections
	for name, lines := range sections {
		if !strings.HasPrefix(name, "Bookmarks") {
			continue
		}

		groupName := ""
		for _, line := range lines {
			key, value := splitKV(line)
			if key == "SubRep" && value != "" {
				groupName = value
				result.Groups = append(result.Groups, plugin.ImportGroup{
					Name: groupName,
				})
			}
		}

		// Parse session lines within the bookmarks section
		for _, line := range lines {
			key, value := splitKV(line)
			if key == "SubRep" || key == "ImgNum" || key == "" {
				continue
			}

			// Session lines start with a name (possibly prefixed with *)
			// and contain = followed by the session definition
			if value == "" {
				continue
			}

			sessionName := key
			// Strip leading * from session names (marks favorites)
			sessionName = strings.TrimPrefix(sessionName, "*")

			conn := parseSessionLine(sessionName, value)
			if conn != nil {
				conn.GroupName = groupName
				result.Connections = append(result.Connections, *conn)
			}
		}
	}

	// Parse [SSH_Hostkeys] section
	if hostKeyLines, ok := sections["SSH_Hostkeys"]; ok {
		for _, line := range hostKeyLines {
			key, value := splitKV(line)
			if key == "" || value == "" {
				continue
			}
			hk := parseHostKeyLine(key, value)
			if hk != nil {
				result.HostKeys = append(result.HostKeys, *hk)
			}
		}
	}

	// Parse [Colors] section
	if colorLines, ok := sections["Colors"]; ok {
		theme := parseColorSection(colorLines)
		if theme != nil {
			result.Theme = theme
		}
	}

	return result, nil
}

// parseSections splits .mobaconf INI content into named sections.
// Returns a map of section name -> lines within that section.
func parseSections(content string) map[string][]string {
	sections := make(map[string][]string)
	currentSection := ""

	for _, line := range strings.Split(content, "\n") {
		line = strings.TrimRight(line, "\r")

		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
			currentSection = line[1 : len(line)-1]
			if _, exists := sections[currentSection]; !exists {
				sections[currentSection] = []string{}
			}
			continue
		}

		if currentSection != "" && line != "" {
			sections[currentSection] = append(sections[currentSection], line)
		}
	}

	return sections
}

// splitKV splits a line on the first = sign into key and value.
func splitKV(line string) (string, string) {
	idx := strings.Index(line, "=")
	if idx < 0 {
		return line, ""
	}
	return strings.TrimSpace(line[:idx]), strings.TrimSpace(line[idx+1:])
}

// parseSessionLine parses a MobaXTerm session definition string.
// Format: #type#flags%field1%field2%...
// SSH (#109#): fields[0]=host, fields[1]=port, fields[2]=username
// RDP (#91#):  fields[0]=host, fields[1]=port, fields[2]=username
func parseSessionLine(name, value string) *plugin.ImportConnection {
	// Find the protocol type marker
	hashIdx := strings.Index(value, "#")
	if hashIdx < 0 {
		return nil
	}

	// Extract the type number between # markers
	rest := value[hashIdx+1:]
	secondHash := strings.Index(rest, "#")
	if secondHash < 0 {
		return nil
	}

	typeStr := rest[:secondHash]
	afterType := rest[secondHash+1:]

	// The remainder after the type and flags section: flags%field1%field2%...
	// Split on the first % to separate flags from fields, then split the rest
	parts := strings.Split(afterType, "%")
	if len(parts) < 2 {
		return nil
	}

	// parts[0] = flags (numeric), parts[1:] = fields
	fields := parts[1:]

	conn := &plugin.ImportConnection{
		Name: name,
	}

	switch typeStr {
	case "109": // SSH
		conn.Protocol = "ssh"
		if len(fields) >= 1 {
			conn.Hostname = fields[0]
		}
		if len(fields) >= 2 {
			port, err := strconv.Atoi(fields[1])
			if err == nil && port > 0 {
				conn.Port = port
			} else {
				conn.Port = 22
			}
		} else {
			conn.Port = 22
		}
		if len(fields) >= 3 {
			conn.Username = fields[2]
		}
	case "91": // RDP
		conn.Protocol = "rdp"
		if len(fields) >= 1 {
			conn.Hostname = fields[0]
		}
		if len(fields) >= 2 {
			port, err := strconv.Atoi(fields[1])
			if err == nil && port > 0 {
				conn.Port = port
			} else {
				conn.Port = 3389
			}
		} else {
			conn.Port = 3389
		}
		if len(fields) >= 3 {
			conn.Username = fields[2]
		}
	default:
		// Unknown protocol type — skip
		return nil
	}

	if conn.Hostname == "" {
		return nil
	}

	return conn
}

// parseHostKeyLine parses a line from the [SSH_Hostkeys] section.
// Key format: keytype@port:hostname
// Value: the fingerprint data
func parseHostKeyLine(key, value string) *plugin.ImportHostKey {
	// Parse key format: keytype@port:hostname
	atIdx := strings.Index(key, "@")
	if atIdx < 0 {
		return nil
	}

	keyType := key[:atIdx]
	rest := key[atIdx+1:]

	colonIdx := strings.Index(rest, ":")
	if colonIdx < 0 {
		return nil
	}

	portStr := rest[:colonIdx]
	hostname := rest[colonIdx+1:]

	port, err := strconv.Atoi(portStr)
	if err != nil {
		return nil
	}

	return &plugin.ImportHostKey{
		Hostname:    hostname,
		Port:        port,
		KeyType:     keyType,
		Fingerprint: value,
	}
}

// parseColorSection extracts a terminal theme from the [Colors] section.
func parseColorSection(lines []string) *plugin.ImportTheme {
	colorMap := make(map[string]string)
	for _, line := range lines {
		key, value := splitKV(line)
		if key != "" && value != "" {
			colorMap[key] = value
		}
	}

	// Need at least foreground and background to form a theme
	fg, hasFg := colorMap["ForegroundColour"]
	bg, hasBg := colorMap["BackgroundColour"]
	if !hasFg || !hasBg {
		return nil
	}

	theme := &plugin.ImportTheme{
		Name:       "MobaXTerm Import",
		Foreground: rgbToHex(fg),
		Background: rgbToHex(bg),
	}

	if cursor, ok := colorMap["CursorColour"]; ok {
		theme.Cursor = rgbToHex(cursor)
	}

	// Map MobaXTerm color names to the 16-color array positions
	// Standard ANSI order: black, red, green, yellow, blue, magenta, cyan, white,
	// then bright variants in the same order
	colorNames := [16][2]string{
		{"Black", ""},
		{"Red", ""},
		{"Green", ""},
		{"Yellow", ""},
		{"Blue", ""},
		{"Magenta", ""},
		{"Cyan", ""},
		{"White", ""},
		{"BoldBlack", ""},
		{"BoldRed", ""},
		{"BoldGreen", ""},
		{"BoldYellow", ""},
		{"BoldBlue", ""},
		{"BoldMagenta", ""},
		{"BoldCyan", ""},
		{"BoldWhite", ""},
	}

	for i, cn := range colorNames {
		name := cn[0]
		if val, ok := colorMap[name]; ok {
			theme.Colors[i] = rgbToHex(val)
		}
	}

	return theme
}

// rgbToHex converts a "R,G,B" string to "#RRGGBB" hex format.
func rgbToHex(rgb string) string {
	parts := strings.Split(rgb, ",")
	if len(parts) != 3 {
		return rgb // Return as-is if not in expected format
	}

	r, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
	g, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
	b, err3 := strconv.Atoi(strings.TrimSpace(parts[2]))

	if err1 != nil || err2 != nil || err3 != nil {
		return rgb
	}

	return fmt.Sprintf("#%02x%02x%02x", r, g, b)
}
