package collect

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"strings"

	"github.com/godbus/dbus/v5"
	"github.com/pkg/errors"
	troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)

type NTPStatus string

type TimeInfo struct {
	Timezone        string `json:"timezone"`
	NTPSynchronized bool   `json:"ntp_synchronized"`
	NTPActive       bool   `json:"ntp_active"`
}

const HostTimePath = `host-collectors/system/time.json`
const HostTimeFileName = `time.json`

type CollectHostTime struct {
	hostCollector *troubleshootv1beta2.HostTime
	BundlePath    string
}

func (c *CollectHostTime) Title() string {
	return hostCollectorTitleOrDefault(c.hostCollector.HostCollectorMeta, "TCP Port Status")
}

func (c *CollectHostTime) IsExcluded() (bool, error) {
	return isExcluded(c.hostCollector.Exclude)
}

func (c *CollectHostTime) Collect(progressChan chan<- interface{}) (map[string][]byte, error) {
	timeInfo := TimeInfo{}

	conn, err := dbus.SystemBus()
	if err != nil {
		return nil, errors.Wrap(err, "failed to connect to dbus")
	}
	defer func() {
		if err := conn.Close(); err != nil {
			log.Printf("Failed to close dbus connection: %v\n", err)
		}
	}()

	prop := "org.freedesktop.timedate1.Timezone"
	variant, err := conn.Object("org.freedesktop.timedate1", "/org/freedesktop/timedate1").GetProperty(prop)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to read property %s", prop)
	}
	timeInfo.Timezone = strings.Trim(variant.String(), `"`)
	// UTC is reported as Etc/UTC on Ubuntu
	if strings.ToLower(timeInfo.Timezone) == "etc/utc" {
		timeInfo.Timezone = "UTC"
	}

	timeInfo.Timezone = strings.ToUpper(timeInfo.Timezone)

	prop = "org.freedesktop.timedate1.NTPSynchronized"
	variant, err = conn.Object("org.freedesktop.timedate1", "/org/freedesktop/timedate1").GetProperty(prop)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to read property %s", prop)
	}
	switch variant.String() {
	case "true":
		timeInfo.NTPSynchronized = true
	case "false":
		timeInfo.NTPSynchronized = false
	default:
		return nil, fmt.Errorf("Unexpected value for property %s: %s", prop, variant.String())
	}

	prop = "org.freedesktop.timedate1.NTP"
	variant, err = conn.Object("org.freedesktop.timedate1", "/org/freedesktop/timedate1").GetProperty(prop)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to read property %s", prop)
	}
	switch variant.String() {
	case "true":
		timeInfo.NTPActive = true
	case "false":
		timeInfo.NTPActive = false
	default:
		return nil, fmt.Errorf("Unexpected value for property %s: %s", prop, variant.String())
	}

	b, err := json.Marshal(timeInfo)
	if err != nil {
		return nil, errors.Wrap(err, "failed to marshal time info")
	}

	output := NewResult()
	output.SaveResult(c.BundlePath, HostTimePath, bytes.NewBuffer(b))

	return map[string][]byte{
		HostTimePath: b,
	}, nil
}

func (c *CollectHostTime) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) {
	return nil, ErrRemoteCollectorNotImplemented
}
