package clientinfo

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os/exec"
	"strings"
	"sync"

	"github.com/Control-D-Inc/ctrld/internal/router"
	"github.com/Control-D-Inc/ctrld/internal/router/ubios"
)

// ubiosDiscover provides client discovery functionality on Ubios routers.
type ubiosDiscover struct {
	hostname sync.Map // mac => hostname
}

// refresh reloads unifi devices from database.
func (u *ubiosDiscover) refresh() error {
	if router.Name() != ubios.Name {
		return nil
	}
	return u.refreshDevices()
}

// LookupHostnameByIP returns hostname for given IP.
func (u *ubiosDiscover) LookupHostnameByIP(ip string) string {
	return ""
}

// LookupHostnameByMac returns unifi device custom hostname for the given MAC address.
func (u *ubiosDiscover) LookupHostnameByMac(mac string) string {
	val, ok := u.hostname.Load(mac)
	if !ok {
		return ""
	}
	return val.(string)
}

// refreshDevices updates unifi devices name from local mongodb.
func (u *ubiosDiscover) refreshDevices() error {
	cmd := exec.Command("/usr/bin/mongo", "localhost:27117/ace", "--quiet", "--eval", `
		DBQuery.shellBatchSize = 256;
		db.user.find({name: {$exists: true, $ne: ""}}, {_id:0, mac:1, name:1});`)
	b, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("out: %s, err: %w", string(b), err)
	}
	return u.storeDevices(bytes.NewReader(b))
}

// storeDevices saves unifi devices name for caching.
func (u *ubiosDiscover) storeDevices(r io.Reader) error {
	decoder := json.NewDecoder(r)
	device := struct {
		MAC  string
		Name string
	}{}
	for {
		err := decoder.Decode(&device)
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		mac := strings.ToLower(device.MAC)
		u.hostname.Store(mac, normalizeHostname(device.Name))
	}
	return nil
}

// String returns human-readable format of ubiosDiscover.
func (u *ubiosDiscover) String() string {
	return "ubios"
}
