package dsl

import (
	"fmt"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

const (
	// AgentTeamPublishWikiUse publishes staged Markdown files into an AGS wiki.
	AgentTeamPublishWikiUse = "agentteam/publish-wiki"

	AgentTeamPublishWikiWithRepository  = "repository"
	AgentTeamPublishWikiWithSourcePath  = "source_path"
	AgentTeamPublishWikiWithMessage     = "message"
	AgentTeamPublishWikiWithDescription = "description"
	AgentTeamPublishWikiWithLabel       = "label"
	AgentTeamPublishWikiWithMaxPages    = "max_pages"
)

// AgentTeamPublishWikiOptions are the normalized options for a agentteam/publish-wiki step.
type AgentTeamPublishWikiOptions struct {
	Repository  string
	SourcePath  string
	Message     string
	Description string
	Label       string
	MaxPages    int
}

// AgentTeamPublishWikiOptionError describes one invalid agentteam/publish-wiki option.
type AgentTeamPublishWikiOptionError struct {
	Code    string
	Key     string
	Message string
}

func (e AgentTeamPublishWikiOptionError) Error() string {
	return e.Message
}

// ParseAgentTeamPublishWikiOptions validates and normalizes agentteam/publish-wiki with values.
func ParseAgentTeamPublishWikiOptions(values map[string]any) (AgentTeamPublishWikiOptions, []AgentTeamPublishWikiOptionError) {
	options := AgentTeamPublishWikiOptions{SourcePath: "."}
	keys := make([]string, 0, len(values))
	for key := range values {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	var errs []AgentTeamPublishWikiOptionError
	for _, key := range keys {
		value := values[key]
		switch key {
		case AgentTeamPublishWikiWithRepository:
			text, ok := stringOption(value)
			if !ok || !validAgentTeamPublishRepository(text) {
				errs = append(errs, AgentTeamPublishWikiOptionError{
					Code:    "agentteam_publish_wiki_repository_invalid",
					Key:     key,
					Message: "agentteam/publish-wiki with.repository must be owner/repo",
				})
				continue
			}
			options.Repository = text
		case AgentTeamPublishWikiWithSourcePath:
			text, ok := stringOption(value)
			if !ok || !validAgentTeamPublishSourcePath(text) {
				errs = append(errs, AgentTeamPublishWikiOptionError{
					Code:    "agentteam_publish_wiki_source_path_invalid",
					Key:     key,
					Message: "agentteam/publish-wiki with.source_path must be a relative path outside .git control directories",
				})
				continue
			}
			options.SourcePath = text
		case AgentTeamPublishWikiWithMessage:
			text, ok := stringOption(value)
			if !ok {
				errs = append(errs, AgentTeamPublishWikiOptionError{Code: "agentteam_publish_wiki_message_invalid", Key: key, Message: "agentteam/publish-wiki with.message must be a string"})
				continue
			}
			options.Message = text
		case AgentTeamPublishWikiWithDescription:
			text, ok := stringOption(value)
			if !ok {
				errs = append(errs, AgentTeamPublishWikiOptionError{Code: "agentteam_publish_wiki_description_invalid", Key: key, Message: "agentteam/publish-wiki with.description must be a string"})
				continue
			}
			options.Description = text
		case AgentTeamPublishWikiWithLabel:
			text, ok := stringOption(value)
			if !ok {
				errs = append(errs, AgentTeamPublishWikiOptionError{Code: "agentteam_publish_wiki_label_invalid", Key: key, Message: "agentteam/publish-wiki with.label must be a string"})
				continue
			}
			options.Label = text
		case AgentTeamPublishWikiWithMaxPages:
			maxPages, ok := intOption(value)
			if !ok || maxPages <= 0 {
				errs = append(errs, AgentTeamPublishWikiOptionError{Code: "agentteam_publish_wiki_max_pages_invalid", Key: key, Message: "agentteam/publish-wiki with.max_pages must be a positive integer"})
				continue
			}
			options.MaxPages = maxPages
		default:
			errs = append(errs, AgentTeamPublishWikiOptionError{
				Code:    "agentteam_publish_wiki_with_unsupported",
				Key:     key,
				Message: "agentteam/publish-wiki only supports with.repository, with.source_path, with.message, with.description, with.label, and with.max_pages",
			})
		}
	}
	if strings.TrimSpace(options.Repository) == "" {
		errs = append(errs, AgentTeamPublishWikiOptionError{
			Code:    "agentteam_publish_wiki_repository_required",
			Key:     AgentTeamPublishWikiWithRepository,
			Message: "agentteam/publish-wiki with.repository is required",
		})
	}
	return options, errs
}

func stringOption(value any) (string, bool) {
	text, ok := value.(string)
	if !ok {
		return "", false
	}
	return strings.TrimSpace(text), true
}

func intOption(value any) (int, bool) {
	switch typed := value.(type) {
	case int:
		return typed, true
	case float64:
		if typed != float64(int(typed)) {
			return 0, false
		}
		return int(typed), true
	case string:
		parsed, err := strconv.Atoi(strings.TrimSpace(typed))
		return parsed, err == nil
	default:
		return 0, false
	}
}

func validAgentTeamPublishRepository(value string) bool {
	owner, repo, ok := strings.Cut(strings.TrimSpace(value), "/")
	if !ok || strings.Contains(repo, "/") {
		return false
	}
	return strings.TrimSpace(owner) != "" && strings.TrimSpace(repo) != ""
}

func validAgentTeamPublishSourcePath(value string) bool {
	path := strings.TrimSpace(value)
	if path == "" || filepath.IsAbs(path) || AgentTeamCheckoutPathHasGitSegment(path) {
		return false
	}
	cleaned := filepath.Clean(filepath.FromSlash(path))
	return cleaned != ".." && !strings.HasPrefix(cleaned, fmt.Sprintf("..%c", filepath.Separator))
}
