package describe

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/stackitcloud/stackit-cli/internal/pkg/types"

	"github.com/spf13/cobra"
	"github.com/stackitcloud/stackit-cli/internal/pkg/args"
	"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
	"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
	"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
	"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
	"github.com/stackitcloud/stackit-cli/internal/pkg/print"
	"github.com/stackitcloud/stackit-cli/internal/pkg/services/load-balancer/client"
	lbUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/load-balancer/utils"
	"github.com/stackitcloud/stackit-cli/internal/pkg/tables"
	"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
	"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer"
)

const (
	targetPoolNameArg = "TARGET_POOL_NAME"

	lbNameFlag = "lb-name"
)

type inputModel struct {
	*globalflags.GlobalFlagModel
	TargetPoolName string
	LBName         string
}

func NewCmd(params *types.CmdParams) *cobra.Command {
	cmd := &cobra.Command{
		Use:   fmt.Sprintf("describe %s", targetPoolNameArg),
		Short: "Shows details of a target pool in a Load Balancer",
		Long:  "Shows details of a target pool in a Load Balancer.",
		Args:  args.SingleArg(targetPoolNameArg, nil),
		Example: examples.Build(
			examples.NewExample(
				`Get details of a target pool with name "pool" in load balancer with name "my-load-balancer"`,
				"$ stackit load-balancer target-pool describe pool --lb-name my-load-balancer"),
			examples.NewExample(
				`Get details of a target pool with name "pool" in load balancer with name "my-load-balancer in JSON output"`,
				"$ stackit load-balancer target-pool describe pool --lb-name my-load-balancer --output-format json"),
		),
		RunE: func(cmd *cobra.Command, args []string) error {
			ctx := context.Background()
			model, err := parseInput(params.Printer, cmd, args)
			if err != nil {
				return err
			}
			// Configure API client
			apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
			if err != nil {
				return err
			}

			// Call API
			req := buildRequest(ctx, model, apiClient)
			resp, err := req.Execute()
			if err != nil {
				return fmt.Errorf("read load balancer: %w", err)
			}

			targetPool := lbUtils.FindLoadBalancerTargetPoolByName(*resp.TargetPools, model.TargetPoolName)
			if targetPool == nil {
				return fmt.Errorf("target pool not found")
			}

			listener := lbUtils.FindLoadBalancerListenerByTargetPool(*resp.Listeners, *targetPool.Name)

			return outputResult(params.Printer, model.OutputFormat, *targetPool, listener)
		},
	}
	configureFlags(cmd)
	return cmd
}

func configureFlags(cmd *cobra.Command) {
	cmd.Flags().String(lbNameFlag, "", "Name of the load balancer")

	err := flags.MarkFlagsRequired(cmd, lbNameFlag)
	cobra.CheckErr(err)
}

func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
	targetPoolName := inputArgs[0]

	globalFlags := globalflags.Parse(p, cmd)
	if globalFlags.ProjectId == "" {
		return nil, &errors.ProjectIdError{}
	}

	model := inputModel{
		GlobalFlagModel: globalFlags,
		TargetPoolName:  targetPoolName,
		LBName:          cmd.Flag(lbNameFlag).Value.String(),
	}

	p.DebugInputModel(model)
	return &model, nil
}

func buildRequest(ctx context.Context, model *inputModel, apiClient *loadbalancer.APIClient) loadbalancer.ApiGetLoadBalancerRequest {
	req := apiClient.GetLoadBalancer(ctx, model.ProjectId, model.Region, model.LBName)
	return req
}

func outputResult(p *print.Printer, outputFormat string, targetPool loadbalancer.TargetPool, listener *loadbalancer.Listener) error {
	if listener == nil {
		return fmt.Errorf("listener response is empty")
	}
	output := struct {
		*loadbalancer.TargetPool
		Listener *loadbalancer.Listener `json:"attached_listener"`
	}{
		&targetPool,
		listener,
	}

	return p.OutputResult(outputFormat, output, func() error {
		sessionPersistence := "None"
		if targetPool.SessionPersistence != nil && targetPool.SessionPersistence.UseSourceIpAddress != nil && *targetPool.SessionPersistence.UseSourceIpAddress {
			sessionPersistence = "Use Source IP"
		}

		healthCheckInterval := "-"
		healthCheckUnhealthyThreshold := "-"
		healthCheckHealthyThreshold := "-"
		if targetPool.ActiveHealthCheck != nil {
			if targetPool.ActiveHealthCheck.Interval != nil {
				healthCheckInterval = *targetPool.ActiveHealthCheck.Interval
			}
			if targetPool.ActiveHealthCheck.UnhealthyThreshold != nil {
				healthCheckUnhealthyThreshold = strconv.FormatInt(*targetPool.ActiveHealthCheck.UnhealthyThreshold, 10)
			}
			if targetPool.ActiveHealthCheck.HealthyThreshold != nil {
				healthCheckHealthyThreshold = strconv.FormatInt(*targetPool.ActiveHealthCheck.HealthyThreshold, 10)
			}
		}

		targets := "-"
		if targetPool.Targets != nil {
			var targetsSlice []string
			for _, target := range *targetPool.Targets {
				targetStr := fmt.Sprintf("%s (%s)", *target.DisplayName, *target.Ip)
				targetsSlice = append(targetsSlice, targetStr)
			}
			targets = strings.Join(targetsSlice, "\n")
		}

		listenerStr := "-"
		if listener != nil {
			listenerStr = fmt.Sprintf("%s (Port:%s, Protocol: %s)",
				utils.PtrString(listener.Name),
				utils.PtrString(listener.Port),
				utils.PtrString(listener.Protocol),
			)
		}

		table := tables.NewTable()
		table.AddRow("NAME", utils.PtrString(targetPool.Name))
		table.AddSeparator()
		table.AddRow("TARGET PORT", utils.PtrString(targetPool.TargetPort))
		table.AddSeparator()
		table.AddRow("ATTACHED LISTENER", listenerStr)
		table.AddSeparator()
		table.AddRow("TARGETS", targets)
		table.AddSeparator()
		table.AddRow("SESSION PERSISTENCE", sessionPersistence)
		table.AddSeparator()
		table.AddRow("HEALTH CHECK INTERVAL", healthCheckInterval)
		table.AddSeparator()
		table.AddRow("HEALTH CHECK DOWN AFTER", healthCheckUnhealthyThreshold)
		table.AddSeparator()
		table.AddRow("HEALTH CHECK UP AFTER", healthCheckHealthyThreshold)
		table.AddSeparator()

		err := p.PagerDisplay(table.Render())
		if err != nil {
			return fmt.Errorf("display output: %w", err)
		}

		return nil
	})
}
