// ABOUTME: Deletion planner for EdgeConnect delete command
// ABOUTME: Analyzes current state to identify resources for deletion
package v2

import (
	"context"
	"fmt"
	"time"

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

// EdgeConnectClientInterface defines the methods needed for deletion planning
type EdgeConnectClientInterface interface {
	ShowApp(ctx context.Context, appKey v2.AppKey, region string) (v2.App, error)
	ShowAppInstances(ctx context.Context, instanceKey v2.AppInstanceKey, appKey v2.AppKey, region string) ([]v2.AppInstance, error)
	DeleteApp(ctx context.Context, appKey v2.AppKey, region string) error
	DeleteAppInstance(ctx context.Context, instanceKey v2.AppInstanceKey, region string) error
}

// Planner defines the interface for deletion planning
type Planner interface {
	// Plan analyzes the configuration and current state to generate a deletion plan
	Plan(ctx context.Context, config *config.EdgeConnectConfig) (*PlanResult, error)

	// PlanWithOptions allows customization of planning behavior
	PlanWithOptions(ctx context.Context, config *config.EdgeConnectConfig, opts PlanOptions) (*PlanResult, error)
}

// PlanOptions provides configuration for the planning process
type PlanOptions struct {
	// DryRun indicates this is a planning-only operation
	DryRun bool

	// Timeout for API operations
	Timeout time.Duration
}

// DefaultPlanOptions returns sensible default planning options
func DefaultPlanOptions() PlanOptions {
	return PlanOptions{
		DryRun:  false,
		Timeout: 30 * time.Second,
	}
}

// EdgeConnectPlanner implements the Planner interface for EdgeConnect
type EdgeConnectPlanner struct {
	client EdgeConnectClientInterface
}

// NewPlanner creates a new EdgeConnect deletion planner
func NewPlanner(client EdgeConnectClientInterface) Planner {
	return &EdgeConnectPlanner{
		client: client,
	}
}

// Plan analyzes the configuration and generates a deletion plan
func (p *EdgeConnectPlanner) Plan(ctx context.Context, config *config.EdgeConnectConfig) (*PlanResult, error) {
	return p.PlanWithOptions(ctx, config, DefaultPlanOptions())
}

// PlanWithOptions generates a deletion plan with custom options
func (p *EdgeConnectPlanner) PlanWithOptions(ctx context.Context, config *config.EdgeConnectConfig, opts PlanOptions) (*PlanResult, error) {
	startTime := time.Now()
	var warnings []string

	// Create the deletion plan structure
	plan := &DeletionPlan{
		ConfigName: config.Metadata.Name,
		CreatedAt:  startTime,
		DryRun:     opts.DryRun,
	}

	// Get the region from the first infra template
	region := config.Spec.InfraTemplate[0].Region

	// Step 1: Check if instances exist
	instancesResult := p.findInstancesToDelete(ctx, config, region)
	plan.InstancesToDelete = instancesResult.instances
	if instancesResult.err != nil {
		warnings = append(warnings, fmt.Sprintf("Error querying instances: %v", instancesResult.err))
	}

	// Step 2: Check if app exists
	appResult := p.findAppToDelete(ctx, config, region)
	plan.AppToDelete = appResult.app
	if appResult.err != nil && !isNotFoundError(appResult.err) {
		warnings = append(warnings, fmt.Sprintf("Error querying app: %v", appResult.err))
	}

	// Step 3: Calculate plan metadata
	p.calculatePlanMetadata(plan)

	// Step 4: Generate summary
	plan.Summary = plan.GenerateSummary()

	return &PlanResult{
		Plan:     plan,
		Warnings: warnings,
	}, nil
}

type appQueryResult struct {
	app *AppDeletion
	err error
}

type instancesQueryResult struct {
	instances []InstanceDeletion
	err       error
}

// findAppToDelete checks if the app exists and should be deleted
func (p *EdgeConnectPlanner) findAppToDelete(ctx context.Context, config *config.EdgeConnectConfig, region string) appQueryResult {
	appKey := v2.AppKey{
		Organization: config.Metadata.Organization,
		Name:         config.Metadata.Name,
		Version:      config.Metadata.AppVersion,
	}

	app, err := p.client.ShowApp(ctx, appKey, region)
	if err != nil {
		if isNotFoundError(err) {
			return appQueryResult{app: nil, err: nil}
		}
		return appQueryResult{app: nil, err: err}
	}

	return appQueryResult{
		app: &AppDeletion{
			Name:         app.Key.Name,
			Version:      app.Key.Version,
			Organization: app.Key.Organization,
			Region:       region,
		},
		err: nil,
	}
}

// findInstancesToDelete finds all instances that match the config
func (p *EdgeConnectPlanner) findInstancesToDelete(ctx context.Context, config *config.EdgeConnectConfig, region string) instancesQueryResult {
	var allInstances []InstanceDeletion

	// Query instances for each infra template
	for _, infra := range config.Spec.InfraTemplate {
		instanceKey := v2.AppInstanceKey{
			Organization: config.Metadata.Organization,
			Name:         generateInstanceName(config.Metadata.Name, config.Metadata.AppVersion),
			CloudletKey: v2.CloudletKey{
				Organization: infra.CloudletOrg,
				Name:         infra.CloudletName,
			},
		}
		appKey := v2.AppKey{Name: config.Metadata.Name}

		instances, err := p.client.ShowAppInstances(ctx, instanceKey, appKey, infra.Region)
		if err != nil {
			// If it's a not found error, just continue
			if isNotFoundError(err) {
				continue
			}
			return instancesQueryResult{instances: nil, err: err}
		}

		// Add found instances to the list
		for _, inst := range instances {
			allInstances = append(allInstances, InstanceDeletion{
				Name:         inst.Key.Name,
				Organization: inst.Key.Organization,
				Region:       infra.Region,
				CloudletOrg:  inst.Key.CloudletKey.Organization,
				CloudletName: inst.Key.CloudletKey.Name,
			})
		}
	}

	return instancesQueryResult{
		instances: allInstances,
		err:       nil,
	}
}

// calculatePlanMetadata calculates the total actions and estimated duration
func (p *EdgeConnectPlanner) calculatePlanMetadata(plan *DeletionPlan) {
	totalActions := 0

	if plan.AppToDelete != nil {
		totalActions++
	}

	totalActions += len(plan.InstancesToDelete)

	plan.TotalActions = totalActions

	// Estimate duration: ~5 seconds per instance, ~3 seconds for app
	estimatedSeconds := len(plan.InstancesToDelete) * 5
	if plan.AppToDelete != nil {
		estimatedSeconds += 3
	}
	plan.EstimatedDuration = time.Duration(estimatedSeconds) * time.Second
}

// generateInstanceName creates an instance name from app name and version
func generateInstanceName(appName, appVersion string) string {
	return fmt.Sprintf("%s-%s-instance", appName, appVersion)
}

// isNotFoundError checks if an error is a 404 not found error
func isNotFoundError(err error) bool {
	if apiErr, ok := err.(*v2.APIError); ok {
		return apiErr.StatusCode == 404
	}
	return false
}

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

	// Error if planning failed
	Error error

	// Warnings encountered during planning
	Warnings []string
}
