package broadcasts

import (
	"strconv"
	"time"

	"github.com/safing/portmaster/base/config"
	"github.com/safing/portmaster/service/core"
	"github.com/safing/portmaster/service/intel/geoip"
	"github.com/safing/portmaster/service/netenv"
	"github.com/safing/portmaster/spn/access"
	"github.com/safing/portmaster/spn/access/account"
	"github.com/safing/portmaster/spn/captain"
)

var portmasterStarted = time.Now()

func collectData() interface{} {
	data := make(map[string]interface{})

	// Get data about versions.
	versions := core.GetSimpleVersions()
	data["Updates"] = versions
	data["Version"] = versions.Build.Version
	numericVersion, err := MakeNumericVersion(versions.Build.Version)
	if err != nil {
		data["NumericVersion"] = &DataError{
			Error: err,
		}
	} else {
		data["NumericVersion"] = numericVersion
	}

	// Get data about install.
	installInfo, err := GetInstallInfo()
	if err != nil {
		data["Install"] = &DataError{
			Error: err,
		}
	} else {
		data["Install"] = installInfo
	}

	// Get global configuration.
	data["Config"] = config.GetActiveConfigValues()

	// Get data about device location.
	locs, ok := netenv.GetInternetLocation()
	if ok && locs.Best().LocationOrNil() != nil {
		loc := locs.Best()
		data["Location"] = &Location{
			Country:        loc.Location.Country.Code,
			Coordinates:    loc.Location.Coordinates,
			ASN:            loc.Location.AutonomousSystemNumber,
			ASOrg:          loc.Location.AutonomousSystemOrganization,
			Source:         loc.Source,
			SourceAccuracy: loc.SourceAccuracy,
		}
	}

	// Get data about SPN status.
	data["SPN"] = captain.GetSPNStatus()

	// Get data about account.
	userRecord, err := access.GetUser()
	if err != nil {
		data["Account"] = &DataError{
			Error: err,
		}
	} else {
		account := &Account{
			UserRecord: userRecord,
			Active:     userRecord.MayUse(""),
			UpToDate:   userRecord.Meta().Modified > time.Now().Add(-7*24*time.Hour).Unix(),
		}
		// Only add feature IDs when account is active.
		if account.Active {
			account.FeatureIDs = userRecord.CurrentPlan.FeatureIDs
		}
		data["Account"] = account
	}

	// Time running.
	data["UptimeHours"] = int(time.Since(portmasterStarted).Hours())

	// Get current time and date.
	now := time.Now()
	data["Current"] = &Current{
		UnixTime: now.Unix(),
		UTC:      makeDateTimeInfo(now.UTC()),
		Local:    makeDateTimeInfo(now),
	}

	return data
}

// Location holds location matching data.
type Location struct {
	Country        string
	Coordinates    geoip.Coordinates
	ASN            uint
	ASOrg          string
	Source         netenv.DeviceLocationSource
	SourceAccuracy int
}

// Account holds SPN account matching data.
type Account struct {
	*access.UserRecord
	Active     bool
	UpToDate   bool
	FeatureIDs []account.FeatureID
}

// DataError represents an error getting some matching data.
type DataError struct {
	Error error
}

// Current holds current date and time data.
type Current struct {
	UnixTime int64
	UTC      *DateTime
	Local    *DateTime
}

// DateTime holds date and time data in different formats.
type DateTime struct {
	NumericDateTime int64
	NumericDate     int64
	NumericTime     int64
}

func makeDateTimeInfo(t time.Time) *DateTime {
	info := &DateTime{}
	info.NumericDateTime, _ = strconv.ParseInt(t.Format("20060102150405"), 10, 64)
	info.NumericDate, _ = strconv.ParseInt(t.Format("20060102"), 10, 64)
	info.NumericTime, _ = strconv.ParseInt(t.Format("150405"), 10, 64)

	return info
}
