// Package domain contains pure domain models with no external dependencies.
package domain

import "regexp"

// ComponentType represents the type of component in a monorepo.
type ComponentType string

const (
	ComponentTypeService   ComponentType = "service"
	ComponentTypeWorker    ComponentType = "worker"
	ComponentTypeAppAstro  ComponentType = "app-astro"
	ComponentTypeAppReact  ComponentType = "app-react"
	ComponentTypeAppNextJS ComponentType = "app-nextjs"
	ComponentTypeCLI       ComponentType = "cli"
	// Infrastructure component types - these trigger provisioning, not scaffolding.
	ComponentTypePostgres ComponentType = "postgres"
	ComponentTypeRedis    ComponentType = "redis"
	ComponentTypeGCS      ComponentType = "gcs"
)

// ValidComponentTypes lists all valid component types.
var ValidComponentTypes = []ComponentType{
	ComponentTypeService,
	ComponentTypeWorker,
	ComponentTypeAppAstro,
	ComponentTypeAppReact,
	ComponentTypeAppNextJS,
	ComponentTypeCLI,
	ComponentTypePostgres,
	ComponentTypeRedis,
	ComponentTypeGCS,
}

// IsValidComponentType checks if a string is a valid component type.
func IsValidComponentType(t string) bool {
	for _, valid := range ValidComponentTypes {
		if string(valid) == t {
			return true
		}
	}
	return false
}

// Component represents a component in a monorepo project.
type Component struct {
	Type         ComponentType `json:"type"`
	Name         string        `json:"name"`
	Path         string        `json:"path"`         // e.g., "services/auth-api"
	Port         int           `json:"port"`         // 0 if not applicable
	Template     string        `json:"template"`     // template used
	Dependencies []string      `json:"dependencies"` // e.g., ["postgres", "redis"]
}

// DestDir returns the destination directory for this component type.
func (c ComponentType) DestDir() string {
	switch c {
	case ComponentTypeService:
		return "services"
	case ComponentTypeWorker:
		return "workers"
	case ComponentTypeAppAstro, ComponentTypeAppReact, ComponentTypeAppNextJS:
		return "apps"
	case ComponentTypeCLI:
		return "cli"
	default:
		return ""
	}
}

// StartingPort returns the starting port number for this component type.
// Workers and CLIs don't expose ports (return 0).
func (c ComponentType) StartingPort() int {
	switch c {
	case ComponentTypeService:
		return 8001
	case ComponentTypeAppAstro, ComponentTypeAppReact, ComponentTypeAppNextJS:
		return 3001
	case ComponentTypeWorker, ComponentTypeCLI:
		return 0
	default:
		return 0
	}
}

// NeedsPort returns true if this component type requires a port assignment.
func (c ComponentType) NeedsPort() bool {
	return c == ComponentTypeService || c == ComponentTypeAppAstro || c == ComponentTypeAppReact || c == ComponentTypeAppNextJS
}

// IsGoComponent returns true if this component type uses Go (and needs go.work entry).
func (c ComponentType) IsGoComponent() bool {
	return c == ComponentTypeService || c == ComponentTypeWorker || c == ComponentTypeCLI
}

// IsAppComponent returns true if this component type is a frontend app (and gets "/" path).
func (c ComponentType) IsAppComponent() bool {
	return c == ComponentTypeAppAstro || c == ComponentTypeAppReact || c == ComponentTypeAppNextJS
}

// IsInfraComponent returns true if this component type is infrastructure (database, cache, storage).
// Infrastructure components trigger provisioning instead of template scaffolding.
func (c ComponentType) IsInfraComponent() bool {
	return c == ComponentTypePostgres || c == ComponentTypeRedis || c == ComponentTypeGCS
}

// componentNameRegex validates component names (slug format: lowercase, alphanumeric, dashes).
var componentNameRegex = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)

// ValidateComponentName validates that a component name is in slug format.
// Must be lowercase, start with a letter, and contain only letters, numbers, and dashes.
func ValidateComponentName(name string) error {
	if name == "" {
		return ErrInvalidComponentName
	}
	if len(name) > MaxProjectNameLen { // Reuse the 63-char limit from K8s
		return ErrInvalidComponentName
	}
	if !componentNameRegex.MatchString(name) {
		return ErrInvalidComponentName
	}
	return nil
}
