package networkmonitor

import (
	"context"
	"fmt"
	"strings"

	log "github.com/sirupsen/logrus"

	"github.com/netbirdio/netbird/client/internal/routemanager/systemops"
)

func checkChange(ctx context.Context, nexthopv4, nexthopv6 systemops.Nexthop, callback func()) error {
	routeMonitor, err := systemops.NewRouteMonitor(ctx)
	if err != nil {
		return fmt.Errorf("failed to create route monitor: %w", err)
	}
	defer func() {
		if err := routeMonitor.Stop(); err != nil {
			log.Errorf("Network monitor: failed to stop route monitor: %v", err)
		}
	}()

	for {
		select {
		case <-ctx.Done():
			return ErrStopped
		case route := <-routeMonitor.RouteUpdates():
			if route.Destination.Bits() != 0 {
				continue
			}

			if routeChanged(route, nexthopv4, nexthopv6, callback) {
				break
			}
		}
	}
}

func routeChanged(route systemops.RouteUpdate, nexthopv4, nexthopv6 systemops.Nexthop, callback func()) bool {
	intf := "<nil>"
	if route.Interface != nil {
		intf = route.Interface.Name
		if isSoftInterface(intf) {
			log.Debugf("Network monitor: ignoring default route change for soft interface %s", intf)
			return false
		}
	}

	switch route.Type {
	case systemops.RouteModified:
		// TODO: get routing table to figure out if our route is affected for modified routes
		log.Infof("Network monitor: default route changed: via %s, interface %s", route.NextHop, intf)
		go callback()
		return true
	case systemops.RouteAdded:
		if route.NextHop.Is4() && route.NextHop != nexthopv4.IP || route.NextHop.Is6() && route.NextHop != nexthopv6.IP {
			log.Infof("Network monitor: default route added: via %s, interface %s", route.NextHop, intf)
			go callback()
			return true
		}
	case systemops.RouteDeleted:
		if nexthopv4.Intf != nil && route.NextHop == nexthopv4.IP || nexthopv6.Intf != nil && route.NextHop == nexthopv6.IP {
			log.Infof("Network monitor: default route removed: via %s, interface %s", route.NextHop, intf)
			go callback()
			return true
		}
	}

	return false
}

func isSoftInterface(name string) bool {
	return strings.Contains(strings.ToLower(name), "isatap") || strings.Contains(strings.ToLower(name), "teredo")
}