package cmd

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

	"edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect"
	v2 "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect/v2"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	organization string
	appName      string
	appVersion   string
	region       string
)

func validateBaseURL(baseURL string) error {
	url, err := url.Parse(baseURL)
	if err != nil {
		return fmt.Errorf("decoding error '%s'", err.Error())
	}

	if url.Scheme == "" {
		return fmt.Errorf("schema should be set (add https://)")
	}

	if len(url.User.Username()) > 0 {
		return fmt.Errorf("user and or password should not be set")
	}

	if url.Path != "" && url.Path != "/" {
		return fmt.Errorf("should not contain any path '%s'", url.Path)
	}

	if len(url.Query()) > 0 {
		return fmt.Errorf("should not contain any queries '%s'", url.RawQuery)
	}

	if len(url.Fragment) > 0 {
		return fmt.Errorf("should not contain any fragment '%s'", url.Fragment)
	}

	return nil
}

func getAPIVersion() string {
	version := viper.GetString("api_version")
	if version == "" {
		version = "v2" // default to v2
	}
	return strings.ToLower(version)
}

func newSDKClientV1() *edgeconnect.Client {
	baseURL := viper.GetString("base_url")
	username := viper.GetString("username")
	password := viper.GetString("password")

	err := validateBaseURL(baseURL)
	if err != nil {
		fmt.Printf("Error parsing baseURL: '%s' with error: %s\n", baseURL, err.Error())
		os.Exit(1)
	}

	// Build options
	opts := []edgeconnect.Option{
		edgeconnect.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
	}

	// Add logger only if debug flag is set
	if debug {
		logger := log.New(os.Stderr, "[DEBUG] ", log.LstdFlags)
		opts = append(opts, edgeconnect.WithLogger(logger))
	}

	if username != "" && password != "" {
		return edgeconnect.NewClientWithCredentials(baseURL, username, password, opts...)
	}

	// Fallback to no auth for now - in production should require auth
	return edgeconnect.NewClient(baseURL, opts...)
}

func newSDKClientV2() *v2.Client {
	baseURL := viper.GetString("base_url")
	username := viper.GetString("username")
	password := viper.GetString("password")

	err := validateBaseURL(baseURL)
	if err != nil {
		fmt.Printf("Error parsing baseURL: '%s' with error: %s\n", baseURL, err.Error())
		os.Exit(1)
	}

	// Build options
	opts := []v2.Option{
		v2.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
	}

	// Add logger only if debug flag is set
	if debug {
		logger := log.New(os.Stderr, "[DEBUG] ", log.LstdFlags)
		opts = append(opts, v2.WithLogger(logger))
	}

	if username != "" && password != "" {
		return v2.NewClientWithCredentials(baseURL, username, password, opts...)
	}

	// Fallback to no auth for now - in production should require auth
	return v2.NewClient(baseURL, opts...)
}

var appCmd = &cobra.Command{
	Use:   "app",
	Short: "Manage Edge Connect applications",
	Long:  `Create, show, list, and delete Edge Connect applications.`,
}

var createAppCmd = &cobra.Command{
	Use:   "create",
	Short: "Create a new Edge Connect application",
	Run: func(cmd *cobra.Command, args []string) {
		apiVersion := getAPIVersion()
		var err error

		if apiVersion == "v1" {
			c := newSDKClientV1()
			input := &edgeconnect.NewAppInput{
				Region: region,
				App: edgeconnect.App{
					Key: edgeconnect.AppKey{
						Organization: organization,
						Name:         appName,
						Version:      appVersion,
					},
				},
			}
			err = c.CreateApp(context.Background(), input)
		} else {
			c := newSDKClientV2()
			input := &v2.NewAppInput{
				Region: region,
				App: v2.App{
					Key: v2.AppKey{
						Organization: organization,
						Name:         appName,
						Version:      appVersion,
					},
				},
			}
			err = c.CreateApp(context.Background(), input)
		}

		if err != nil {
			fmt.Printf("Error creating app: %v\n", err)
			os.Exit(1)
		}
		fmt.Println("Application created successfully")
	},
}

var showAppCmd = &cobra.Command{
	Use:   "show",
	Short: "Show details of an Edge Connect application",
	Run: func(cmd *cobra.Command, args []string) {
		apiVersion := getAPIVersion()

		if apiVersion == "v1" {
			c := newSDKClientV1()
			appKey := edgeconnect.AppKey{
				Organization: organization,
				Name:         appName,
				Version:      appVersion,
			}
			app, err := c.ShowApp(context.Background(), appKey, region)
			if err != nil {
				fmt.Printf("Error showing app: %v\n", err)
				os.Exit(1)
			}
			fmt.Printf("Application details:\n%+v\n", app)
		} else {
			c := newSDKClientV2()
			appKey := v2.AppKey{
				Organization: organization,
				Name:         appName,
				Version:      appVersion,
			}
			app, err := c.ShowApp(context.Background(), appKey, region)
			if err != nil {
				fmt.Printf("Error showing app: %v\n", err)
				os.Exit(1)
			}
			fmt.Printf("Application details:\n%+v\n", app)
		}
	},
}

var listAppsCmd = &cobra.Command{
	Use:   "list",
	Short: "List Edge Connect applications",
	Run: func(cmd *cobra.Command, args []string) {
		apiVersion := getAPIVersion()

		if apiVersion == "v1" {
			c := newSDKClientV1()
			appKey := edgeconnect.AppKey{
				Organization: organization,
				Name:         appName,
				Version:      appVersion,
			}
			apps, err := c.ShowApps(context.Background(), appKey, region)
			if err != nil {
				fmt.Printf("Error listing apps: %v\n", err)
				os.Exit(1)
			}
			fmt.Println("Applications:")
			for _, app := range apps {
				fmt.Printf("%+v\n", app)
			}
		} else {
			c := newSDKClientV2()
			appKey := v2.AppKey{
				Organization: organization,
				Name:         appName,
				Version:      appVersion,
			}
			apps, err := c.ShowApps(context.Background(), appKey, region)
			if err != nil {
				fmt.Printf("Error listing apps: %v\n", err)
				os.Exit(1)
			}
			fmt.Println("Applications:")
			for _, app := range apps {
				fmt.Printf("%+v\n", app)
			}
		}
	},
}

var deleteAppCmd = &cobra.Command{
	Use:   "delete",
	Short: "Delete an Edge Connect application",
	Run: func(cmd *cobra.Command, args []string) {
		apiVersion := getAPIVersion()
		var err error

		if apiVersion == "v1" {
			c := newSDKClientV1()
			appKey := edgeconnect.AppKey{
				Organization: organization,
				Name:         appName,
				Version:      appVersion,
			}
			err = c.DeleteApp(context.Background(), appKey, region)
		} else {
			c := newSDKClientV2()
			appKey := v2.AppKey{
				Organization: organization,
				Name:         appName,
				Version:      appVersion,
			}
			err = c.DeleteApp(context.Background(), appKey, region)
		}

		if err != nil {
			fmt.Printf("Error deleting app: %v\n", err)
			os.Exit(1)
		}
		fmt.Println("Application deleted successfully")
	},
}

func init() {
	rootCmd.AddCommand(appCmd)
	appCmd.AddCommand(createAppCmd, showAppCmd, listAppsCmd, deleteAppCmd)

	// Add common flags to all app commands
	appCmds := []*cobra.Command{createAppCmd, showAppCmd, listAppsCmd, deleteAppCmd}
	for _, cmd := range appCmds {
		cmd.Flags().StringVarP(&organization, "org", "o", "", "organization name (required)")
		cmd.Flags().StringVarP(&appName, "name", "n", "", "application name")
		cmd.Flags().StringVarP(&appVersion, "version", "v", "", "application version")
		cmd.Flags().StringVarP(&region, "region", "r", "", "region (required)")
		if err := cmd.MarkFlagRequired("org"); err != nil {
			panic(err)
		}
		if err := cmd.MarkFlagRequired("region"); err != nil {
			panic(err)
		}
	}

	// Add required name flag for specific commands
	for _, cmd := range []*cobra.Command{createAppCmd, showAppCmd, deleteAppCmd} {
		if err := cmd.MarkFlagRequired("name"); err != nil {
			panic(err)
		}
	}
}
