// ABOUTME: Deployment planning types for EdgeConnect apply command with state management
// ABOUTME: Defines structures for deployment plans, actions, and state comparison results
package v1

import (
	"fmt"
	"strings"
	"time"

	"edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/internal/config"
	"edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect"
)

// SecurityRule defines network access rules (alias to SDK type for consistency)
type SecurityRule = edgeconnect.SecurityRule

// ActionType represents the type of action to be performed
type ActionType string

const (
	// ActionCreate indicates a resource needs to be created
	ActionCreate ActionType = "CREATE"
	// ActionUpdate indicates a resource needs to be updated
	ActionUpdate ActionType = "UPDATE"
	// ActionNone indicates no action is needed
	ActionNone ActionType = "NONE"
	// ActionDelete indicates a resource needs to be deleted (for rollback scenarios)
	ActionDelete ActionType = "DELETE"
)

// String returns the string representation of ActionType
func (a ActionType) String() string {
	return string(a)
}

// DeploymentPlan represents the complete deployment plan for a configuration
type DeploymentPlan struct {
	// ConfigName is the name from metadata
	ConfigName string

	// AppAction defines what needs to be done with the application
	AppAction AppAction

	// InstanceActions defines what needs to be done with each instance
	InstanceActions []InstanceAction

	// Summary provides a human-readable summary of the plan
	Summary string

	// TotalActions is the count of all actions that will be performed
	TotalActions int

	// EstimatedDuration is the estimated time to complete the deployment
	EstimatedDuration time.Duration

	// CreatedAt timestamp when the plan was created
	CreatedAt time.Time

	// DryRun indicates if this is a dry-run plan
	DryRun bool
}

// AppAction represents an action to be performed on an application
type AppAction struct {
	// Type of action to perform
	Type ActionType

	// Current state of the app (nil if doesn't exist)
	Current *AppState

	// Desired state of the app
	Desired *AppState

	// Changes describes what will change
	Changes []string

	// Reason explains why this action is needed
	Reason string

	// ManifestHash is the hash of the current manifest file
	ManifestHash string

	// ManifestChanged indicates if the manifest content has changed
	ManifestChanged bool
}

// InstanceAction represents an action to be performed on an application instance
type InstanceAction struct {
	// Type of action to perform
	Type ActionType

	// Target infrastructure where the instance will be deployed
	Target config.InfraTemplate

	// Current state of the instance (nil if doesn't exist)
	Current *InstanceState

	// Desired state of the instance
	Desired *InstanceState

	// Changes describes what will change
	Changes []string

	// Reason explains why this action is needed
	Reason string

	// InstanceName is the generated name for this instance
	InstanceName string

	// Dependencies lists other instances this depends on
	Dependencies []string
}

// AppState represents the current state of an application
type AppState struct {
	// Name of the application
	Name string

	// Version of the application
	Version string

	// Organization that owns the app
	Organization string

	// Region where the app is deployed
	Region string

	// ManifestHash is the stored hash of the manifest file
	ManifestHash string

	// LastUpdated timestamp when the app was last modified
	LastUpdated time.Time

	// Exists indicates if the app currently exists
	Exists bool

	// AppType indicates whether this is a k8s or docker app
	AppType AppType

	// OutboundConnections contains the required outbound network connections
	OutboundConnections []SecurityRule
}

// InstanceState represents the current state of an application instance
type InstanceState struct {
	// Name of the instance
	Name string

	// AppName that this instance belongs to
	AppName string

	// AppVersion of the associated app
	AppVersion string

	// Organization that owns the instance
	Organization string

	// Region where the instance is deployed
	Region string

	// CloudletOrg that hosts the cloudlet
	CloudletOrg string

	// CloudletName where the instance is running
	CloudletName string

	// FlavorName used for the instance
	FlavorName string

	// State of the instance (e.g., "Ready", "Pending", "Error")
	State string

	// PowerState of the instance
	PowerState string

	// LastUpdated timestamp when the instance was last modified
	LastUpdated time.Time

	// Exists indicates if the instance currently exists
	Exists bool
}

// AppType represents the type of application
type AppType string

const (
	// AppTypeK8s represents a Kubernetes application
	AppTypeK8s AppType = "k8s"
	// AppTypeDocker represents a Docker application
	AppTypeDocker AppType = "docker"
)

// String returns the string representation of AppType
func (a AppType) String() string {
	return string(a)
}

// DeploymentSummary provides a high-level overview of the deployment plan
type DeploymentSummary struct {
	// TotalActions is the total number of actions to be performed
	TotalActions int

	// ActionCounts breaks down actions by type
	ActionCounts map[ActionType]int

	// EstimatedDuration for the entire deployment
	EstimatedDuration time.Duration

	// ResourceSummary describes the resources involved
	ResourceSummary ResourceSummary

	// Warnings about potential issues
	Warnings []string
}

// ResourceSummary provides details about resources in the deployment
type ResourceSummary struct {
	// AppsToCreate number of apps that will be created
	AppsToCreate int

	// AppsToUpdate number of apps that will be updated
	AppsToUpdate int

	// InstancesToCreate number of instances that will be created
	InstancesToCreate int

	// InstancesToUpdate number of instances that will be updated
	InstancesToUpdate int

	// CloudletsAffected number of unique cloudlets involved
	CloudletsAffected int

	// RegionsAffected number of unique regions involved
	RegionsAffected int
}

// PlanResult represents the result of a deployment planning operation
type PlanResult struct {
	// Plan is the generated deployment plan
	Plan *DeploymentPlan

	// Error if planning failed
	Error error

	// Warnings encountered during planning
	Warnings []string
}

// ExecutionResult represents the result of executing a deployment plan
type ExecutionResult struct {
	// Plan that was executed
	Plan *DeploymentPlan

	// Success indicates if the deployment was successful
	Success bool

	// CompletedActions lists actions that were successfully completed
	CompletedActions []ActionResult

	// FailedActions lists actions that failed
	FailedActions []ActionResult

	// Error that caused the deployment to fail (if any)
	Error error

	// Duration taken to execute the plan
	Duration time.Duration

	// RollbackPerformed indicates if rollback was executed
	RollbackPerformed bool

	// RollbackSuccess indicates if rollback was successful
	RollbackSuccess bool
}

// ActionResult represents the result of executing a single action
type ActionResult struct {
	// Type of action that was attempted
	Type ActionType

	// Target describes what was being acted upon
	Target string

	// Success indicates if the action succeeded
	Success bool

	// Error if the action failed
	Error error

	// Duration taken to complete the action
	Duration time.Duration

	// Details provides additional information about the action
	Details string
}

// IsEmpty returns true if the deployment plan has no actions to perform
func (dp *DeploymentPlan) IsEmpty() bool {
	if dp.AppAction.Type != ActionNone {
		return false
	}

	for _, action := range dp.InstanceActions {
		if action.Type != ActionNone {
			return false
		}
	}

	return true
}

// HasErrors returns true if the plan contains any error conditions
func (dp *DeploymentPlan) HasErrors() bool {
	// Check for conflicting actions or invalid states
	return false // Implementation would check for various error conditions
}

// GetTargetCloudlets returns a list of unique cloudlets that will be affected
func (dp *DeploymentPlan) GetTargetCloudlets() []string {
	cloudletSet := make(map[string]bool)
	var cloudlets []string

	for _, action := range dp.InstanceActions {
		if action.Type != ActionNone {
			key := fmt.Sprintf("%s:%s", action.Target.CloudletOrg, action.Target.CloudletName)
			if !cloudletSet[key] {
				cloudletSet[key] = true
				cloudlets = append(cloudlets, key)
			}
		}
	}

	return cloudlets
}

// GetTargetRegions returns a list of unique regions that will be affected
func (dp *DeploymentPlan) GetTargetRegions() []string {
	regionSet := make(map[string]bool)
	var regions []string

	for _, action := range dp.InstanceActions {
		if action.Type != ActionNone && !regionSet[action.Target.Region] {
			regionSet[action.Target.Region] = true
			regions = append(regions, action.Target.Region)
		}
	}

	return regions
}

// GenerateSummary creates a human-readable summary of the deployment plan
func (dp *DeploymentPlan) GenerateSummary() string {
	if dp.IsEmpty() {
		return "No changes required - configuration matches current state"
	}

	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("Deployment plan for '%s':\n", dp.ConfigName))

	// App actions
	if dp.AppAction.Type != ActionNone {
		sb.WriteString(fmt.Sprintf("- %s application '%s'\n", dp.AppAction.Type, dp.AppAction.Desired.Name))
		if len(dp.AppAction.Changes) > 0 {
			for _, change := range dp.AppAction.Changes {
				sb.WriteString(fmt.Sprintf("  - %s\n", change))
			}
		}
	}

	// Instance actions
	createCount := 0
	updateActions := []InstanceAction{}
	for _, action := range dp.InstanceActions {
		switch action.Type {
		case ActionCreate:
			createCount++
		case ActionUpdate:
			updateActions = append(updateActions, action)
		}
	}

	if createCount > 0 {
		sb.WriteString(fmt.Sprintf("- CREATE %d instance(s) across %d cloudlet(s)\n", createCount, len(dp.GetTargetCloudlets())))
	}

	if len(updateActions) > 0 {
		sb.WriteString(fmt.Sprintf("- UPDATE %d instance(s)\n", len(updateActions)))
		for _, action := range updateActions {
			if len(action.Changes) > 0 {
				sb.WriteString(fmt.Sprintf("  - Instance '%s':\n", action.InstanceName))
				for _, change := range action.Changes {
					sb.WriteString(fmt.Sprintf("    - %s\n", change))
				}
			}
		}
	}

	sb.WriteString(fmt.Sprintf("Estimated duration: %s", dp.EstimatedDuration.String()))

	return sb.String()
}

// Validate checks if the deployment plan is valid and safe to execute
func (dp *DeploymentPlan) Validate() error {
	if dp.ConfigName == "" {
		return fmt.Errorf("deployment plan must have a config name")
	}

	// Validate app action
	if dp.AppAction.Type != ActionNone && dp.AppAction.Desired == nil {
		return fmt.Errorf("app action of type %s must have desired state", dp.AppAction.Type)
	}

	// Validate instance actions
	for i, action := range dp.InstanceActions {
		if action.Type != ActionNone {
			if action.Desired == nil {
				return fmt.Errorf("instance action %d of type %s must have desired state", i, action.Type)
			}
			if action.InstanceName == "" {
				return fmt.Errorf("instance action %d must have an instance name", i)
			}
		}
	}

	return nil
}

// Clone creates a deep copy of the deployment plan
func (dp *DeploymentPlan) Clone() *DeploymentPlan {
	clone := &DeploymentPlan{
		ConfigName:        dp.ConfigName,
		Summary:           dp.Summary,
		TotalActions:      dp.TotalActions,
		EstimatedDuration: dp.EstimatedDuration,
		CreatedAt:         dp.CreatedAt,
		DryRun:            dp.DryRun,
		AppAction:         dp.AppAction, // Struct copy is sufficient for this use case
	}

	// Deep copy instance actions
	clone.InstanceActions = make([]InstanceAction, len(dp.InstanceActions))
	copy(clone.InstanceActions, dp.InstanceActions)

	return clone
}

// convertNetworkRules converts config network rules to EdgeConnect SecurityRules
func convertNetworkRules(network *config.NetworkConfig) []edgeconnect.SecurityRule {
	rules := make([]edgeconnect.SecurityRule, len(network.OutboundConnections))

	for i, conn := range network.OutboundConnections {
		rules[i] = edgeconnect.SecurityRule{
			Protocol:     conn.Protocol,
			PortRangeMin: conn.PortRangeMin,
			PortRangeMax: conn.PortRangeMax,
			RemoteCIDR:   conn.RemoteCIDR,
		}
	}

	return rules
}
