// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s

package watch

import (
	"fmt"
	"log/slog"
	"strings"
	"time"

	"github.com/derailed/k9s/internal/port"
	"github.com/derailed/k9s/internal/slogs"
	"k8s.io/client-go/tools/portforward"
)

// Forwarder represents a port forwarder.
type Forwarder interface {
	// Start starts a port-forward.
	Start(path string, tunnel port.PortTunnel) (*portforward.PortForwarder, error)

	// Stop terminates a port forward.
	Stop()

	// ID returns the pf id.
	ID() string

	// Container returns a container name.
	Container() string

	// Port returns the port mapping.
	Port() string

	// Address returns the host address.
	Address() string

	// FQN returns the full port-forward name.
	FQN() string

	// Active returns forwarder current state.
	Active() bool

	// SetActive sets port-forward state.
	SetActive(bool)

	// Age returns forwarder age.
	Age() time.Time

	// HasPortMapping returns true if port mapping exists.
	HasPortMapping(string) bool
}

// Forwarders tracks active port forwards.
type Forwarders map[string]Forwarder

// NewForwarders returns new forwarders.
func NewForwarders() Forwarders {
	return make(map[string]Forwarder)
}

// IsPodForwarded checks if pod has a forward.
func (ff Forwarders) IsPodForwarded(fqn string) bool {
	fqn += "|"
	for k := range ff {
		if strings.HasPrefix(k, fqn) {
			return true
		}
	}

	return false
}

// IsContainerForwarded checks if pod has a forward.
func (ff Forwarders) IsContainerForwarded(fqn, co string) bool {
	fqn += "|" + co
	for k := range ff {
		if strings.HasPrefix(k, fqn) {
			return true
		}
	}

	return false
}

// DeleteAll stops and delete all port-forwards.
func (ff Forwarders) DeleteAll() {
	for k, f := range ff {
		slog.Debug("Deleting forwarder", slogs.ID, f.ID())
		f.Stop()
		delete(ff, k)
	}
}

// Kill stops and delete a port-forwards associated with pod.
func (ff Forwarders) Kill(path string) int {
	var stats int

	// The way port forwards are stored is `pod_fqn|container|local_port:container_port`
	// The '|' is added to make sure we do not delete port forwards from other pods that have the same prefix
	// Without the `|` port forwards for pods, default/web-0 and default/web-0-bla would be both deleted
	// even if we want only port forwards for default/web-0 to be deleted
	prefix := path + "|"
	for k, f := range ff {
		if k == path || strings.HasPrefix(k, prefix) {
			stats++
			slog.Debug("Stop and delete port-forward", slogs.Name, k)
			f.Stop()
			delete(ff, k)
		}
	}

	return stats
}

// Dump for debug!
func (ff Forwarders) Dump() {
	slog.Debug("----------- PORT-FORWARDS --------------")
	for k, f := range ff {
		slog.Debug(fmt.Sprintf("  %s -- %s", k, f))
	}
}
