package kvm

import (
	"fmt"

	"kvm/internal/network"
	"kvm/internal/udhcpc"

	"github.com/guregu/null/v6"
)

const (
	NetIfName = "eth0"
)

var (
	networkState *network.NetworkInterfaceState
)

func networkStateChanged() {
	// do not block the main thread
	go waitCtrlAndRequestDisplayUpdate(true)

	// always restart mDNS when the network state changes
	if mDNS != nil {
		_ = mDNS.SetListenOptions(config.NetworkConfig.GetMDNSMode())
		_ = mDNS.SetLocalNames([]string{
			networkState.GetHostname(),
			networkState.GetFQDN(),
		}, true)
	}
}

func initNetwork() error {
	ensureConfigLoaded()

	state, err := network.NewNetworkInterfaceState(&network.NetworkInterfaceOptions{
		DefaultHostname: GetDefaultHostname(),
		InterfaceName:   NetIfName,
		NetworkConfig:   config.NetworkConfig,
		Logger:          networkLogger,
		OnStateChange: func(state *network.NetworkInterfaceState) {
			networkStateChanged()
		},
		OnInitialCheck: func(state *network.NetworkInterfaceState) {
			networkStateChanged()
		},
		OnDhcpLeaseChange: func(lease *udhcpc.Lease) {
			networkStateChanged()

			if currentSession == nil {
				return
			}

			writeJSONRPCEvent("networkState", networkState.RpcGetNetworkState(), currentSession)
		},
		OnConfigChange: func(networkConfig *network.NetworkConfig) {
			config.NetworkConfig = networkConfig
			config.AppliedNetworkConfig = networkConfig
			networkStateChanged()
		},
	})

	if state == nil {
		if err == nil {
			return fmt.Errorf("failed to create NetworkInterfaceState")
		}
		return err
	}

	if err := state.Run(); err != nil {
		return err
	}

	networkState = state

	if config != nil && config.NetworkConfig != nil {
		if config.NetworkConfig.PendingReboot.Valid && config.NetworkConfig.PendingReboot.Bool {
			if config.AppliedNetworkConfig != nil && network.IsSame(config.AppliedNetworkConfig, *config.NetworkConfig) {
				config.NetworkConfig.PendingReboot = null.BoolFrom(false)
				_ = SaveConfig()
			}
		}
	}

	if config != nil && config.NetworkConfig != nil {
		if config.NetworkConfig.PendingReboot.Valid && config.NetworkConfig.PendingReboot.Bool {
			config.NetworkConfig.PendingReboot = null.BoolFrom(false)
			_ = SaveConfig()
		}
	}

	return nil
}

func rpcGetNetworkState() network.RpcNetworkState {
	return networkState.RpcGetNetworkState()
}

func rpcGetNetworkSettings() network.RpcNetworkSettings {
	rpcSettings := networkState.RpcGetNetworkSettings()
	hostname := GetHostname()
	rpcSettings.Hostname = null.NewString(hostname, hostname != "")
	return rpcSettings
}

func rpcSetNetworkSettings(settings network.RpcNetworkSettings) (*network.RpcNetworkSettings, error) {
	current := networkState.RpcGetNetworkSettings()
	changedCore := !network.IsSame(current.NetworkConfig, settings.NetworkConfig)
	if changedCore {
		settings.NetworkConfig.PendingReboot = null.BoolFrom(true)
	}

	s := networkState.RpcSetNetworkSettings(settings)
	if s != nil {
		return nil, s
	}
	applied := networkState.RpcGetNetworkSettings()
	config.NetworkConfig = &applied.NetworkConfig
	// If we just reverted to the same core config as applied, clear pending_reboot
	if config.AppliedNetworkConfig != nil {
		// create copies ignoring PendingReboot
		a := *config.AppliedNetworkConfig
		b := applied.NetworkConfig
		a.PendingReboot = null.Bool{}
		b.PendingReboot = null.Bool{}
		if network.IsSame(a, b) {
			config.NetworkConfig.PendingReboot = null.BoolFrom(false)
		}
	}
	if err := SaveConfig(); err != nil {
		return nil, err
	}

	return &network.RpcNetworkSettings{NetworkConfig: *config.NetworkConfig}, nil
}

func rpcRenewDHCPLease() error {
	return networkState.RpcRenewDHCPLease()
}

func rpcRequestDHCPAddress(ip string) error {
	return networkState.RpcRequestDHCPAddress(ip)
}
