package updater

import (
	"context"
	"errors"
	"fmt"
	"sort"

	"github.com/qdm12/gluetun/internal/constants/vpn"
	"github.com/qdm12/gluetun/internal/models"
	"github.com/qdm12/gluetun/internal/provider/common"
)

var (
	ErrNotIPv4 = errors.New("IP address is not IPv4")
)

func (u *Updater) FetchServers(ctx context.Context, minServers int) (
	servers []models.Server, err error) {
	const recommended = true
	const limit = 0
	data, err := fetchAPI(ctx, u.client, recommended, limit)
	if err != nil {
		return nil, err
	}

	servers = make([]models.Server, 0, len(data))

	for _, jsonServer := range data {
		if jsonServer.Status != "online" {
			u.warner.Warn(fmt.Sprintf("ignoring offline server %s", jsonServer.Name))
			continue
		}

		server := models.Server{
			Country:  jsonServer.country(),
			Region:   jsonServer.region(),
			City:     jsonServer.city(),
			Hostname: jsonServer.Hostname,
			IPs:      jsonServer.ips(),
		}

		number, err := parseServerName(jsonServer.Name)
		switch {
		case errors.Is(err, ErrNoIDInServerName):
			u.warner.Warn(fmt.Sprintf("%s - leaving server number as 0", err))
		case err != nil:
			u.warner.Warn(fmt.Sprintf("failed parsing server name: %s", err))
			continue
		default: // no error
			server.Number = number
		}

		var wireguardFound, openvpnFound bool
		wireguardServer := server
		wireguardServer.VPN = vpn.Wireguard
		openVPNServer := server // accumulate UDP+TCP technologies
		openVPNServer.VPN = vpn.OpenVPN

		for _, technology := range jsonServer.Technologies {
			switch technology.Identifier {
			case "openvpn_udp":
				openvpnFound = true
				openVPNServer.UDP = true
			case "openvpn_tcp":
				openvpnFound = true
				openVPNServer.TCP = true
			case "wireguard_udp":
				wireguardFound = true
				wireguardServer.WgPubKey, err = jsonServer.wireguardPublicKey()
				if err != nil {
					u.warner.Warn(fmt.Sprintf("ignoring Wireguard server %s: %s",
						jsonServer.Name, err))
					wireguardFound = false
					continue
				}
			default: // Ignore other technologies
				continue
			}
		}

		if openvpnFound {
			servers = append(servers, openVPNServer)
		}
		if wireguardFound {
			servers = append(servers, wireguardServer)
		}
	}

	if len(servers) < minServers {
		return nil, fmt.Errorf("%w: %d and expected at least %d",
			common.ErrNotEnoughServers, len(servers), minServers)
	}

	sort.Sort(models.SortableServers(servers))

	return servers, nil
}
