// ABOUTME: Comprehensive EdgeXR SDK example demonstrating complete app deployment workflow
// ABOUTME: Shows app creation, instance deployment, cloudlet management, and cleanup

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

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

func main() {
	// Configure SDK client
	baseURL := getEnvOrDefault("EDGEXR_BASE_URL", "https://hub.apps.edge.platform.mg3.mdb.osc.live")

	// Support both authentication methods
	token := getEnvOrDefault("EDGEXR_TOKEN", "")
	username := getEnvOrDefault("EDGEXR_USERNAME", "")
	password := getEnvOrDefault("EDGEXR_PASSWORD", "")

	var client *v2.Client

	if token != "" {
		fmt.Println("🔐 Using Bearer token authentication")
		client = v2.NewClient(baseURL,
			v2.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
			v2.WithAuthProvider(v2.NewStaticTokenProvider(token)),
			v2.WithLogger(log.Default()),
		)
	} else if username != "" && password != "" {
		fmt.Println("🔐 Using username/password authentication")
		client = v2.NewClientWithCredentials(baseURL, username, password,
			v2.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
			v2.WithLogger(log.Default()),
		)
	} else {
		log.Fatal("Authentication required: Set either EDGEXR_TOKEN or both EDGEXR_USERNAME and EDGEXR_PASSWORD")
	}

	ctx := context.Background()

	// Configuration for the workflow
	config := WorkflowConfig{
		Organization: "edp2",
		Region:       "EU",
		AppName:      "edge-app-demo",
		AppVersion:   "1.0.0",
		CloudletOrg:  "TelekomOP",
		CloudletName: "Munich",
		InstanceName: "app-instance-1",
		FlavorName:   "EU.small",
	}

	fmt.Printf("🚀 Starting comprehensive EdgeXR workflow demonstration\n")
	fmt.Printf("Organization: %s, Region: %s\n\n", config.Organization, config.Region)

	// Run the complete workflow
	if err := runComprehensiveWorkflow(ctx, client, config); err != nil {
		log.Fatalf("Workflow failed: %v", err)
	}

	fmt.Println("\n✅ Comprehensive EdgeXR SDK workflow completed successfully!")
	fmt.Println("\n📊 Summary:")
	fmt.Println("   • Created and managed applications")
	fmt.Println("   • Deployed and managed application instances")
	fmt.Println("   • Queried cloudlet information")
	fmt.Println("   • Demonstrated complete lifecycle management")
}

// WorkflowConfig holds configuration for the demonstration workflow
type WorkflowConfig struct {
	Organization string
	Region       string
	AppName      string
	AppVersion   string
	CloudletOrg  string
	CloudletName string
	InstanceName string
	FlavorName   string
}

func runComprehensiveWorkflow(ctx context.Context, c *v2.Client, config WorkflowConfig) error {
	fmt.Println("═══ Phase 1: Application Management ═══")

	// 1. Create Application
	fmt.Println("\n1️⃣  Creating application...")
	app := &v2.NewAppInput{
		Region: config.Region,
		App: v2.App{
			Key: v2.AppKey{
				Organization: config.Organization,
				Name:         config.AppName,
				Version:      config.AppVersion,
			},
			Deployment:       "kubernetes",
			ImageType:        "ImageTypeDocker",                                   // field is ignored
			ImagePath:        "https://registry-1.docker.io/library/nginx:latest", // must be set. Even for kubernetes
			DefaultFlavor:    v2.Flavor{Name: config.FlavorName},
			ServerlessConfig: v2.ServerlessConfig{}, // must be set
			AllowServerless:  true,       // must be set to true for kubernetes
			RequiredOutboundConnections: []v2.SecurityRule{
				{
					Protocol:     "tcp",
					PortRangeMin: 80,
					PortRangeMax: 80,
					RemoteCIDR:   "0.0.0.0/0",
				},
				{
					Protocol:     "tcp",
					PortRangeMin: 443,
					PortRangeMax: 443,
					RemoteCIDR:   "0.0.0.0/0",
				},
			},
		},
	}

	if err := c.CreateApp(ctx, app); err != nil {
		return fmt.Errorf("failed to create app: %w", err)
	}
	fmt.Printf("✅ App created: %s/%s v%s\n", config.Organization, config.AppName, config.AppVersion)

	// 2. Show Application Details
	fmt.Println("\n2️⃣  Querying application details...")
	appKey := v2.AppKey{
		Organization: config.Organization,
		Name:         config.AppName,
		Version:      config.AppVersion,
	}

	appDetails, err := c.ShowApp(ctx, appKey, config.Region)
	if err != nil {
		return fmt.Errorf("failed to show app: %w", err)
	}
	fmt.Printf("✅ App details retrieved:\n")
	fmt.Printf("   • Name: %s/%s v%s\n", appDetails.Key.Organization, appDetails.Key.Name, appDetails.Key.Version)
	fmt.Printf("   • Deployment: %s\n", appDetails.Deployment)
	fmt.Printf("   • Image: %s\n", appDetails.ImagePath)
	fmt.Printf("   • Security Rules: %d configured\n", len(appDetails.RequiredOutboundConnections))

	// 3. List Applications in Organization
	fmt.Println("\n3️⃣  Listing applications in organization...")
	filter := v2.AppKey{Organization: config.Organization}
	apps, err := c.ShowApps(ctx, filter, config.Region)
	if err != nil {
		return fmt.Errorf("failed to list apps: %w", err)
	}
	fmt.Printf("✅ Found %d applications in organization '%s'\n", len(apps), config.Organization)
	for i, app := range apps {
		fmt.Printf("   %d. %s v%s (%s)\n", i+1, app.Key.Name, app.Key.Version, app.Deployment)
	}

	fmt.Println("\n═══ Phase 2: Application Instance Management ═══")

	// 4. Create Application Instance
	fmt.Println("\n4️⃣  Creating application instance...")
	instance := &v2.NewAppInstanceInput{
		Region: config.Region,
		AppInst: v2.AppInstance{
			Key: v2.AppInstanceKey{
				Organization: config.Organization,
				Name:         config.InstanceName,
				CloudletKey: v2.CloudletKey{
					Organization: config.CloudletOrg,
					Name:         config.CloudletName,
				},
			},
			AppKey: appKey,
			Flavor: v2.Flavor{Name: config.FlavorName},
		},
	}

	if err := c.CreateAppInstance(ctx, instance); err != nil {
		return fmt.Errorf("failed to create app instance: %w", err)
	}
	fmt.Printf("✅ App instance created: %s on cloudlet %s/%s\n",
		config.InstanceName, config.CloudletOrg, config.CloudletName)

	// 5. Wait for Application Instance to be Ready
	fmt.Println("\n5️⃣  Waiting for application instance to be ready...")
	instanceKey := v2.AppInstanceKey{
		Organization: config.Organization,
		Name:         config.InstanceName,
		CloudletKey: v2.CloudletKey{
			Organization: config.CloudletOrg,
			Name:         config.CloudletName,
		},
	}

	instanceDetails, err := waitForInstanceReady(ctx, c, instanceKey, v2.AppKey{}, config.Region, 5*time.Minute)
	if err != nil {
		return fmt.Errorf("failed to wait for instance ready: %w", err)
	}
	fmt.Printf("✅ Instance is ready:\n")
	fmt.Printf("   • Name: %s\n", instanceDetails.Key.Name)
	fmt.Printf("   • App: %s/%s v%s\n", instanceDetails.AppKey.Organization, instanceDetails.AppKey.Name, instanceDetails.AppKey.Version)
	fmt.Printf("   • Cloudlet: %s/%s\n", instanceDetails.Key.CloudletKey.Organization, instanceDetails.Key.CloudletKey.Name)
	fmt.Printf("   • Flavor: %s\n", instanceDetails.Flavor.Name)
	fmt.Printf("   • State: %s\n", instanceDetails.State)

	// 6. List Application Instances
	fmt.Println("\n6️⃣  Listing application instances...")
	instances, err := c.ShowAppInstances(ctx, v2.AppInstanceKey{Organization: config.Organization}, v2.AppKey{}, config.Region)
	if err != nil {
		return fmt.Errorf("failed to list app instances: %w", err)
	}
	fmt.Printf("✅ Found %d application instances in organization '%s'\n", len(instances), config.Organization)
	for i, inst := range instances {
		fmt.Printf("   %d. %s (state: %s, cloudlet: %s)\n",
			i+1, inst.Key.Name, inst.State, inst.Key.CloudletKey.Name)
	}

	// 7. Refresh Application Instance
	fmt.Println("\n7️⃣  Refreshing application instance...")
	if err := c.RefreshAppInstance(ctx, instanceKey, config.Region); err != nil {
		return fmt.Errorf("failed to refresh app instance: %w", err)
	}
	fmt.Printf("✅ Instance refreshed: %s\n", config.InstanceName)

	fmt.Println("\n═══ Phase 3: Cloudlet Information ═══")

	// 8. Show Cloudlet Details
	fmt.Println("\n8️⃣  Querying cloudlet information...")
	cloudletKey := v2.CloudletKey{
		Organization: config.CloudletOrg,
		Name:         config.CloudletName,
	}

	cloudlets, err := c.ShowCloudlets(ctx, cloudletKey, config.Region)
	if err != nil {
		// This might fail in demo environment, so we'll continue
		fmt.Printf("⚠️  Could not retrieve cloudlet details: %v\n", err)
	} else {
		fmt.Printf("✅ Found %d cloudlets matching criteria\n", len(cloudlets))
		for i, cloudlet := range cloudlets {
			fmt.Printf("   %d. %s/%s (state: %s)\n",
				i+1, cloudlet.Key.Organization, cloudlet.Key.Name, cloudlet.State)
			fmt.Printf("      Location: lat=%.4f, lng=%.4f\n",
				cloudlet.Location.Latitude, cloudlet.Location.Longitude)
		}
	}

	// 9. Try to Get Cloudlet Manifest (may not be available in demo)
	fmt.Println("\n9️⃣  Attempting to retrieve cloudlet manifest...")
	manifest, err := c.GetCloudletManifest(ctx, cloudletKey, config.Region)
	if err != nil {
		fmt.Printf("⚠️  Could not retrieve cloudlet manifest: %v\n", err)
	} else {
		fmt.Printf("✅ Cloudlet manifest retrieved (%d bytes)\n", len(manifest.Manifest))
	}

	// 10. Try to Get Cloudlet Resource Usage (may not be available in demo)
	fmt.Println("\n🔟  Attempting to retrieve cloudlet resource usage...")
	usage, err := c.GetCloudletResourceUsage(ctx, cloudletKey, config.Region)
	if err != nil {
		fmt.Printf("⚠️  Could not retrieve cloudlet usage: %v\n", err)
	} else {
		fmt.Printf("✅ Cloudlet resource usage retrieved\n")
		for resource, value := range usage.Usage {
			fmt.Printf("   • %s: %v\n", resource, value)
		}
	}

	fmt.Println("\n═══ Phase 4: Cleanup ═══")

	// 11. Delete Application Instance
	fmt.Println("\n1️⃣1️⃣ Cleaning up application instance...")
	if err := c.DeleteAppInstance(ctx, instanceKey, config.Region); err != nil {
		return fmt.Errorf("failed to delete app instance: %w", err)
	}
	fmt.Printf("✅ App instance deleted: %s\n", config.InstanceName)

	// 12. Delete Application
	fmt.Println("\n1️⃣2️⃣ Cleaning up application...")
	if err := c.DeleteApp(ctx, appKey, config.Region); err != nil {
		return fmt.Errorf("failed to delete app: %w", err)
	}
	fmt.Printf("✅ App deleted: %s/%s v%s\n", config.Organization, config.AppName, config.AppVersion)

	// 13. Verify Cleanup
	fmt.Println("\n1️⃣3️⃣ Verifying cleanup...")
	_, err = c.ShowApp(ctx, appKey, config.Region)
	if err != nil && fmt.Sprintf("%v", err) == v2.ErrResourceNotFound.Error() {
		fmt.Printf("✅ Cleanup verified - app no longer exists\n")
	} else if err != nil {
		fmt.Printf("✅ Cleanup appears successful (verification returned: %v)\n", err)
	} else {
		fmt.Printf("⚠️  App may still exist after deletion\n")
	}

	return nil
}

func getEnvOrDefault(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return defaultValue
}

// waitForInstanceReady polls the instance status until it's no longer "Creating" or timeout
func waitForInstanceReady(ctx context.Context, c *v2.Client, instanceKey v2.AppInstanceKey, appKey v2.AppKey, region string, timeout time.Duration) (v2.AppInstance, error) {
	timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	ticker := time.NewTicker(10 * time.Second) // Poll every 10 seconds
	defer ticker.Stop()

	fmt.Printf("   Polling instance state (timeout: %.0f minutes)...\n", timeout.Minutes())

	for {
		select {
		case <-timeoutCtx.Done():
			return v2.AppInstance{}, fmt.Errorf("timeout waiting for instance to be ready after %v", timeout)

		case <-ticker.C:
			instance, err := c.ShowAppInstance(timeoutCtx, instanceKey, appKey, region)
			if err != nil {
				// Log error but continue polling
				fmt.Printf("   ⚠️  Error checking instance state: %v\n", err)
				continue
			}

			fmt.Printf("   📊 Instance state: %s\n", instance.State)

			// Check if instance is ready (not in creating state)
			state := strings.ToLower(instance.State)
			if state != "" && state != "creating" && state != "create requested" {
				if state == "ready" || state == "running" {
					fmt.Printf("   ✅ Instance reached ready state: %s\n", instance.State)
					return instance, nil
				} else if state == "error" || state == "failed" || strings.Contains(state, "error") {
					return instance, fmt.Errorf("instance entered error state: %s", instance.State)
				} else {
					// Instance is in some other stable state (not creating)
					fmt.Printf("   ✅ Instance reached stable state: %s\n", instance.State)
					return instance, nil
				}
			}
		}
	}
}
