package cms

import (
	"fmt"
	"strconv"
	"strings"
)

type simpleTable struct {
	values map[string]string
	lists  map[string][]string
}

func parseSimpleTable(data string) (simpleTable, error) {
	table := simpleTable{
		values: map[string]string{},
		lists:  map[string][]string{},
	}

	for lineNo, raw := range strings.Split(data, "\n") {
		line := strings.TrimSpace(raw)
		if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "[[") {
			continue
		}
		key, rawValue, ok := strings.Cut(line, "=")
		if !ok {
			return table, fmt.Errorf("line %d: expected key = value", lineNo+1)
		}
		key = strings.TrimSpace(key)
		rawValue = strings.TrimSpace(rawValue)
		if key == "" {
			return table, fmt.Errorf("line %d: empty key", lineNo+1)
		}
		if strings.HasPrefix(rawValue, "[") {
			list, err := parseStringList(rawValue)
			if err != nil {
				return table, fmt.Errorf("line %d: %w", lineNo+1, err)
			}
			table.lists[key] = list
			continue
		}
		value, err := parseScalar(rawValue)
		if err != nil {
			return table, fmt.Errorf("line %d: %w", lineNo+1, err)
		}
		table.values[key] = value
	}

	return table, nil
}

func parseMetadata(data string) (simpleTable, error) {
	table, err := parseSimpleTable(data)
	if err == nil {
		return table, nil
	}
	yamlTable, yamlErr := parseSimpleYAMLTable(data)
	if yamlErr == nil {
		return yamlTable, nil
	}
	return table, err
}

func parseSimpleYAMLTable(data string) (simpleTable, error) {
	table := simpleTable{
		values: map[string]string{},
		lists:  map[string][]string{},
	}

	for lineNo, raw := range strings.Split(data, "\n") {
		line := strings.TrimSpace(raw)
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		key, rawValue, ok := strings.Cut(line, ":")
		if !ok {
			return table, fmt.Errorf("line %d: expected key: value", lineNo+1)
		}
		key = strings.TrimSpace(key)
		rawValue = strings.TrimSpace(rawValue)
		if key == "" {
			return table, fmt.Errorf("line %d: empty key", lineNo+1)
		}
		if strings.HasPrefix(rawValue, "[") {
			list, err := parseStringList(rawValue)
			if err != nil {
				return table, fmt.Errorf("line %d: %w", lineNo+1, err)
			}
			table.lists[key] = list
			continue
		}
		if key == "tags" && strings.Contains(rawValue, ",") {
			table.lists[key] = splitCommaList(rawValue)
			continue
		}
		value, err := parseScalar(rawValue)
		if err != nil {
			return table, fmt.Errorf("line %d: %w", lineNo+1, err)
		}
		table.values[key] = value
	}

	return table, nil
}

func parseScalar(raw string) (string, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return "", nil
	}
	if strings.HasPrefix(raw, "\"") {
		value, err := strconv.Unquote(raw)
		if err != nil {
			return "", err
		}
		return value, nil
	}
	return raw, nil
}

func splitCommaList(value string) []string {
	var out []string
	for _, part := range strings.Split(value, ",") {
		part = strings.TrimSpace(part)
		if part != "" {
			out = append(out, part)
		}
	}
	return out
}

func parseStringList(raw string) ([]string, error) {
	raw = strings.TrimSpace(raw)
	if !strings.HasPrefix(raw, "[") || !strings.HasSuffix(raw, "]") {
		return nil, fmt.Errorf("expected string list")
	}
	raw = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(raw, "["), "]"))
	if raw == "" {
		return nil, nil
	}

	var values []string
	for _, part := range strings.Split(raw, ",") {
		value, err := parseScalar(strings.TrimSpace(part))
		if err != nil {
			return nil, err
		}
		if value != "" {
			values = append(values, value)
		}
	}
	return values, nil
}

func formatString(value string) string {
	return strconv.Quote(value)
}

func formatStringList(values []string) string {
	quoted := make([]string, 0, len(values))
	for _, value := range values {
		quoted = append(quoted, strconv.Quote(value))
	}
	return "[" + strings.Join(quoted, ", ") + "]"
}

func boolString(value bool) string {
	if value {
		return "true"
	}
	return "false"
}
