package loadbalancer

import (
	"fmt"
	"time"

	"github.com/spf13/cobra"

	"github.com/hetznercloud/cli/internal/cmd/base"
	"github.com/hetznercloud/cli/internal/cmd/cmpl"
	"github.com/hetznercloud/cli/internal/hcapi2"
	"github.com/hetznercloud/cli/internal/state"
	"github.com/hetznercloud/hcloud-go/v2/hcloud"
)

var UpdateServiceCmd = base.Cmd{
	BaseCobraCommand: func(client hcapi2.Client) *cobra.Command {
		cmd := &cobra.Command{
			Use:                   "update-service [options] --listen-port <1-65535> <load-balancer>",
			Short:                 "Updates a service from a Load Balancer",
			ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(client.LoadBalancer().Names)),
			TraverseChildren:      true,
			DisableFlagsInUseLine: true,
		}

		cmd.Flags().Int("listen-port", 0, "The listen port of the service that you want to update (required)")
		_ = cmd.MarkFlagRequired("listen-port")

		cmd.Flags().Int("destination-port", 0, "Destination port of the service on the targets")

		cmd.Flags().String("protocol", "", "The protocol to use for load balancing traffic")
		cmd.Flags().Bool("proxy-protocol", false, "Enable or disable (with --proxy-protocol=false) Proxy Protocol (true, false)")
		cmd.Flags().Bool("http-redirect-http", false, "Enable or disable redirect all traffic on port 80 to port 443 (true, false)")

		cmd.Flags().Bool("http-sticky-sessions", false, "Enable or disable (with --http-sticky-sessions=false) Sticky Sessions (true, false)")
		cmd.Flags().String("http-cookie-name", "", "Sticky Sessions: Cookie Name which will be set")
		cmd.Flags().Duration("http-cookie-lifetime", 0, "Sticky Sessions: Lifetime of the cookie")
		cmd.Flags().StringSlice("http-certificates", []string{}, "IDs or names of Certificates which should be attached to this Load Balancer")
		cmd.Flags().Duration("http-timeout-idle", 0, "Idle timeout for HTTP connections (30s-300s)")

		cmd.Flags().String("health-check-protocol", "", "The protocol the health check is performed over")
		cmd.Flags().Int("health-check-port", 0, "The port the health check is performed over")
		cmd.Flags().Duration("health-check-interval", 15*time.Second, "The interval the health check is performed")
		cmd.Flags().Duration("health-check-timeout", 10*time.Second, "The timeout after a health check is marked as failed")
		cmd.Flags().Int("health-check-retries", 3, "Number of retries after a health check is marked as failed")

		cmd.Flags().String("health-check-http-domain", "", "The domain we request when performing a http health check")
		cmd.Flags().String("health-check-http-path", "", "The path we request when performing a http health check")

		cmd.Flags().StringSlice("health-check-http-status-codes", []string{}, "List of status codes we expect to determine a target as healthy")
		cmd.Flags().String("health-check-http-response", "", "The response we expect to determine a target as healthy")
		cmd.Flags().Bool("health-check-http-tls", false, "Determine if the health check should verify if the target answers with a valid TLS certificate (true, false)")
		return cmd
	},
	Run: func(s state.State, cmd *cobra.Command, args []string) error {
		idOrName := args[0]
		listenPort, _ := cmd.Flags().GetInt("listen-port")

		loadBalancer, _, err := s.Client().LoadBalancer().Get(s, idOrName)
		if err != nil {
			return err
		}
		if loadBalancer == nil {
			return fmt.Errorf("Load Balancer not found: %s", idOrName)
		}
		var service hcloud.LoadBalancerService
		for _, _service := range loadBalancer.Services {
			if _service.ListenPort == listenPort {
				if _service.HealthCheck.HTTP != nil {
					service = _service
				}
			}
		}
		opts := hcloud.LoadBalancerUpdateServiceOpts{
			HTTP:        &hcloud.LoadBalancerUpdateServiceOptsHTTP{},
			HealthCheck: &hcloud.LoadBalancerUpdateServiceOptsHealthCheck{},
		}
		if cmd.Flag("protocol").Changed {
			protocol, _ := cmd.Flags().GetString("protocol")
			opts.Protocol = hcloud.LoadBalancerServiceProtocol(protocol)
		}
		if cmd.Flag("destination-port").Changed {
			destinationPort, _ := cmd.Flags().GetInt("destination-port")
			opts.DestinationPort = &destinationPort
		}
		if cmd.Flag("proxy-protocol").Changed {
			proxyProtocol, _ := cmd.Flags().GetBool("proxy-protocol")
			opts.Proxyprotocol = &proxyProtocol
		}
		// HTTP
		if cmd.Flag("http-redirect-http").Changed {
			redirectHTTP, _ := cmd.Flags().GetBool("http-redirect-http")
			opts.HTTP.RedirectHTTP = &redirectHTTP
		}
		if cmd.Flag("http-sticky-sessions").Changed {
			stickySessions, _ := cmd.Flags().GetBool("http-sticky-sessions")
			opts.HTTP.StickySessions = &stickySessions
		}
		if cmd.Flag("http-cookie-name").Changed {
			cookieName, _ := cmd.Flags().GetString("http-cookie-name")
			opts.HTTP.CookieName = &cookieName
		}
		if cmd.Flag("http-cookie-lifetime").Changed {
			cookieLifetime, _ := cmd.Flags().GetDuration("http-cookie-lifetime")
			opts.HTTP.CookieLifetime = &cookieLifetime
		}
		if cmd.Flag("http-timeout-idle").Changed {
			timeoutIdle, _ := cmd.Flags().GetDuration("http-timeout-idle")
			opts.HTTP.TimeoutIdle = &timeoutIdle
		}
		if cmd.Flag("http-certificates").Changed {
			certificates, _ := cmd.Flags().GetStringSlice("http-certificates")
			for _, idOrName := range certificates {
				cert, _, err := s.Client().Certificate().Get(s, idOrName)
				if err != nil {
					return err
				}
				opts.HTTP.Certificates = append(opts.HTTP.Certificates, cert)
			}
		}
		// Health Check
		if cmd.Flag("health-check-protocol").Changed {
			healthCheckProtocol, _ := cmd.Flags().GetString("health-check-protocol")
			opts.HealthCheck.Protocol = hcloud.LoadBalancerServiceProtocol(healthCheckProtocol)
		}
		if cmd.Flag("health-check-port").Changed {
			healthCheckPort, _ := cmd.Flags().GetInt("health-check-port")
			opts.HealthCheck.Port = &healthCheckPort
		}
		if cmd.Flag("health-check-interval").Changed {
			healthCheckInterval, _ := cmd.Flags().GetDuration("health-check-interval")
			opts.HealthCheck.Interval = &healthCheckInterval
		}
		if cmd.Flag("health-check-timeout").Changed {
			healthCheckTimeout, _ := cmd.Flags().GetDuration("health-check-timeout")
			opts.HealthCheck.Timeout = &healthCheckTimeout
		}
		if cmd.Flag("health-check-retries").Changed {
			healthCheckRetries, _ := cmd.Flags().GetInt("health-check-retries")
			opts.HealthCheck.Retries = &healthCheckRetries
		}

		// Health Check HTTP
		healthCheckProtocol, _ := cmd.Flags().GetString("health-check-protocol")
		if healthCheckProtocol != string(hcloud.LoadBalancerServiceProtocolTCP) || service.HealthCheck.Protocol != hcloud.LoadBalancerServiceProtocolTCP {
			opts.HealthCheck.HTTP = &hcloud.LoadBalancerUpdateServiceOptsHealthCheckHTTP{}

			if cmd.Flag("health-check-http-domain").Changed {
				healthCheckHTTPDomain, _ := cmd.Flags().GetString("health-check-http-domain")
				opts.HealthCheck.HTTP.Domain = &healthCheckHTTPDomain
			}
			if cmd.Flag("health-check-http-path").Changed {
				healthCheckHTTPPath, _ := cmd.Flags().GetString("health-check-http-path")
				opts.HealthCheck.HTTP.Path = &healthCheckHTTPPath
			}
			if cmd.Flag("health-check-http-response").Changed {
				healthCheckHTTPResponse, _ := cmd.Flags().GetString("health-check-http-response")
				opts.HealthCheck.HTTP.Response = &healthCheckHTTPResponse
			}
			if cmd.Flag("health-check-http-status-codes").Changed {
				healthCheckHTTPStatusCodes, _ := cmd.Flags().GetStringSlice("health-check-http-status-codes")
				opts.HealthCheck.HTTP.StatusCodes = healthCheckHTTPStatusCodes
			}
			if cmd.Flag("health-check-http-tls").Changed {
				healthCheckHTTPTLS, _ := cmd.Flags().GetBool("health-check-http-tls")
				opts.HealthCheck.HTTP.TLS = &healthCheckHTTPTLS
			}
		}

		action, _, err := s.Client().LoadBalancer().UpdateService(s, loadBalancer, listenPort, opts)
		if err != nil {
			return err
		}
		if err := s.WaitForActions(s, cmd, action); err != nil {
			return err
		}
		cmd.Printf("Service %d on Load Balancer %d was updated\n", listenPort, loadBalancer.ID)

		return nil
	},
}
