// Copyright (c) 2026 Sidero Labs, Inc.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.

package siderolink

import (
	"context"
	"fmt"
	"net/netip"

	"github.com/siderolabs/gen/channel"
	"github.com/siderolabs/siderolink/pkg/wireguard"
	"go.uber.org/zap"
	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"

	"github.com/siderolabs/omni/client/api/omni/specs"
)

// WireguardHandler abstraction around peer handler and wgDevice.
type WireguardHandler interface {
	SetupDevice(wireguard.DeviceConfig) error
	Shutdown() error
	Run(context.Context, *zap.Logger) error
	PeerEvent(context.Context, *specs.SiderolinkSpec, bool) error
	Peers() ([]wgtypes.Peer, error)
}

// PhysicalWireguardHandler implements WireguardHandler interface.
type PhysicalWireguardHandler struct {
	wgDevice *wireguard.Device
	events   chan wireguard.PeerEvent
}

// DefaultWireguardHandler is a default wireguard handler to be used with the siderolink manager.
var DefaultWireguardHandler = &PhysicalWireguardHandler{events: make(chan wireguard.PeerEvent, 500)}

// PeerEvent implements WireguardHandler.
func (handler *PhysicalWireguardHandler) PeerEvent(ctx context.Context, spec *specs.SiderolinkSpec, deleted bool) error {
	address, err := netip.ParsePrefix(spec.NodeSubnet)
	if err != nil {
		return err
	}

	pubKey, err := wgtypes.ParseKey(spec.NodePublicKey)
	if err != nil {
		return err
	}

	var virtualAddrPort netip.AddrPort

	if spec.VirtualAddrport != "" {
		virtualAddrPort, err = netip.ParseAddrPort(spec.VirtualAddrport)
		if err != nil {
			return err
		}
	}

	channel.SendWithContext(ctx, handler.events, wireguard.PeerEvent{
		PubKey:                      pubKey,
		Remove:                      deleted,
		Endpoint:                    spec.LastEndpoint,
		Address:                     address.Addr(),
		PersistentKeepAliveInterval: new(wireguard.RecommendedPersistentKeepAliveInterval),
		VirtualAddr:                 virtualAddrPort.Addr(),
	})

	return nil
}

// EventCh implements the wireguard.PeerSource interface.
func (handler *PhysicalWireguardHandler) EventCh() <-chan wireguard.PeerEvent {
	return handler.events
}

// SetupDevice implements WireguardHandler.
func (handler *PhysicalWireguardHandler) SetupDevice(cfg wireguard.DeviceConfig) error {
	var err error

	handler.wgDevice, err = wireguard.NewDevice(cfg)
	if err != nil {
		return fmt.Errorf("error initializing wgDevice: %w", err)
	}

	return nil
}

// Shutdown implements WireguardHandler.
func (handler *PhysicalWireguardHandler) Shutdown() error {
	return handler.wgDevice.Close()
}

// Run implements WireguardHandler.
func (handler *PhysicalWireguardHandler) Run(ctx context.Context, logger *zap.Logger) error {
	return handler.wgDevice.Run(ctx, logger, handler)
}

// Peers implements WireguardHandler.
func (handler *PhysicalWireguardHandler) Peers() ([]wgtypes.Peer, error) {
	if handler.wgDevice == nil {
		return []wgtypes.Peer{}, nil
	}

	return handler.wgDevice.Peers()
}
