package upgrade

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/BurntSushi/toml"
	"github.com/google/go-cmp/cmp"
	"github.com/influxdata/influxdb/v2/pkg/testing/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap/zaptest"
)

func TestConfigUpgrade(t *testing.T) {
	type testCase struct {
		name     string
		config1x string
		config2x string
	}

	var testCases = []testCase{
		{
			name:     "minimal",
			config1x: testConfigV1minimal,
			config2x: testConfigV2minimal,
		},
		{
			name:     "default",
			config1x: testConfigV1default,
			config2x: testConfigV2default,
		},
		{
			name:     "empty",
			config1x: testConfigV1empty,
			config2x: testConfigV2empty,
		},
		{
			name:     "obsolete / arrays",
			config1x: testConfigV1obsoleteArrays,
			config2x: testConfigV2obsoleteArrays,
		},
		{
			name:     "query concurrency",
			config1x: testConfigV1QueryConcurrency,
			config2x: testConfigV2QueryConcurrency,
		},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			tmpdir := t.TempDir()
			configFile := filepath.Join(tmpdir, "influxdb.conf")
			configFileV2 := filepath.Join(filepath.Dir(configFile), "config.toml")
			err := os.WriteFile(configFile, []byte(tc.config1x), 0444)
			require.NoError(t, err)

			targetOtions := optionsV2{
				boltPath:   "/db/.influxdbv2/influxd.bolt",
				enginePath: "/db/.influxdbv2/engine",
				configPath: configFileV2,
			}

			var rawV1Config map[string]interface{}
			if _, err = toml.Decode(tc.config1x, &rawV1Config); err != nil {
				t.Fatal(err)
			}
			err = upgradeConfig(rawV1Config, targetOtions, zaptest.NewLogger(t))
			assert.NoError(t, err)

			var actual, expected map[string]interface{}
			if _, err = toml.Decode(tc.config2x, &expected); err != nil {
				t.Fatal(err)
			}
			if _, err = toml.DecodeFile(configFileV2, &actual); err != nil {
				t.Fatal(err)
			}
			if diff := cmp.Diff(expected, actual); diff != "" {
				t.Fatal(diff)
			}
		})
	}
}

func TestConfigLoadFile(t *testing.T) {
	var typicalRetval, emptyRetval configV1
	_, err := toml.Decode(
		"[meta]\ndir=\"/var/lib/influxdb/meta\"\n[data]\ndir=\"/var/lib/influxdb/data\"\nwal-dir=\"/var/lib/influxdb/wal\"\n[http]\nbind-address=\":8086\"\nhttps-enabled=false",
		&typicalRetval,
	)
	require.NoError(t, err)

	type testCase struct {
		name     string
		config1x string
		retval   *configV1
	}

	var testCases = []testCase{
		{
			name:     "minimal",
			config1x: testConfigV1minimal,
			retval:   &typicalRetval,
		},
		{
			name:     "default",
			config1x: testConfigV1default,
			retval:   &typicalRetval,
		},
		{
			name:     "empty",
			config1x: testConfigV1empty,
			retval:   &emptyRetval,
		},
		{
			name:     "obsolete / arrays",
			config1x: testConfigV1obsoleteArrays,
			retval:   &typicalRetval,
		},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			tmpdir := t.TempDir()
			configFile := filepath.Join(tmpdir, "influxdb.conf")
			err := os.WriteFile(configFile, []byte(tc.config1x), 0444)
			require.NoError(t, err)
			retval, _, err := loadV1Config(configFile)
			require.NoError(t, err)

			if diff := cmp.Diff(tc.retval, retval); diff != "" {
				t.Fatal(diff)
			}
		})
	}
}

func TestConfigLoadFileNotExists(t *testing.T) {
	configFile := "/there/is/no/such/path/influxdb.conf"

	// try upgrade

	_, _, err := loadV1Config(configFile)
	if err == nil {
		t.Fatal("error expected")
	}
}

// 1.x test configs

var testConfigV1minimal = `### Welcome to the InfluxDB configuration file.

# Change this option to true to disable reporting.
reporting-disabled = false

# Bind address to use for the RPC service for backup and restore.
bind-address = "127.0.0.1:8088"

[meta]
  dir = "/var/lib/influxdb/meta"

[data]
  dir = "/var/lib/influxdb/data"
  wal-dir = "/var/lib/influxdb/wal"
  wal-fsync-delay = "100s"
  index-version = "inmem"

[coordinator]
  max-select-point = 0

[retention]
  check-interval = "30m"

[shard-precreation]
  check-interval = "5m"

[monitor]
  store-enabled = true

[http]
  flux-enabled = false
  bind-address = ":8086"
  https-certificate = "/etc/ssl/influxdb.pem"
  https-private-key = "/etc/ssl/influxdb-key.pem"
  pprof-enabled = false

[logging]
  level = "debug"

[subscriber]

[[graphite]]

[[collectd]]

[[opentsdb]]

[[udp]]

[continuous_queries]
  query-stats-enabled = true

[tls]
`

var testConfigV1default = `reporting-disabled = false
bind-address = "127.0.0.1:8088"

[meta]
  dir = "/var/lib/influxdb/meta"
  retention-autocreate = true
  logging-enabled = true

[data]
  dir = "/var/lib/influxdb/data"
  wal-dir = "/var/lib/influxdb/wal"
  wal-fsync-delay = "0s"
  validate-keys = false
  index-version = "tsi1"
  query-log-enabled = true
  cache-max-memory-size = 1073741824
  cache-snapshot-memory-size = 26214400
  cache-snapshot-write-cold-duration = "10m0s"
  compact-full-write-cold-duration = "4h0m0s"
  compact-throughput = 50331648
  compact-throughput-burst = 50331648
  max-concurrent-compactions = 0
  max-index-log-file-size = 1048576
  series-id-set-cache-size = 100
  series-file-max-concurrent-snapshot-compactions = 0
  trace-logging-enabled = false
  tsm-use-madv-willneed = false

[coordinator]
  write-timeout = "10s"
  max-concurrent-queries = 0
  query-timeout = "0s"
  log-queries-after = "0s"
  max-select-point = 0
  max-select-series = 0
  max-select-buckets = 0

[retention]
  enabled = true
  check-interval = "30m0s"

[shard-precreation]
  enabled = true
  check-interval = "10m0s"
  advance-period = "30m0s"

[monitor]
  store-enabled = true
  store-database = "_internal"
  store-interval = "10s"

[subscriber]
  enabled = true
  http-timeout = "30s"
  insecure-skip-verify = false
  ca-certs = ""
  write-concurrency = 40
  write-buffer-size = 1000

[http]
  enabled = true
  bind-address = ":8086"
  auth-enabled = false
  log-enabled = true
  suppress-write-log = false
  write-tracing = false
  flux-enabled = false
  flux-log-enabled = false
  pprof-enabled = true
  pprof-auth-enabled = false
  debug-pprof-enabled = false
  ping-auth-enabled = false
  prom-read-auth-enabled = false
  https-enabled = false
  https-certificate = "/etc/ssl/influxdb.pem"
  https-private-key = ""
  max-row-limit = 0
  max-connection-limit = 0
  shared-secret = ""
  realm = "InfluxDB"
  unix-socket-enabled = false
  unix-socket-permissions = "0777"
  bind-socket = "/var/run/influxdb.sock"
  max-body-size = 25000000
  access-log-path = ""
  max-concurrent-write-limit = 0
  max-enqueued-write-limit = 0
  enqueued-write-timeout = 30000000000

[logging]
  format = "auto"
  level = "info"
  suppress-logo = false

[[graphite]]
  enabled = false
  bind-address = ":2003"
  database = "graphite"
  retention-policy = ""
  protocol = "tcp"
  batch-size = 5000
  batch-pending = 10
  batch-timeout = "1s"
  consistency-level = "one"
  separator = "."
  udp-read-buffer = 0

[[collectd]]
  enabled = false
  bind-address = ":25826"
  database = "collectd"
  retention-policy = ""
  batch-size = 5000
  batch-pending = 10
  batch-timeout = "10s"
  read-buffer = 0
  typesdb = "/usr/share/collectd/types.db"
  security-level = "none"
  auth-file = "/etc/collectd/auth_file"
  parse-multivalue-plugin = "split"

[[opentsdb]]
  enabled = false
  bind-address = ":4242"
  database = "opentsdb"
  retention-policy = ""
  consistency-level = "one"
  tls-enabled = false
  certificate = "/etc/ssl/influxdb.pem"
  batch-size = 1000
  batch-pending = 5
  batch-timeout = "1s"
  log-point-errors = true

[[udp]]
  enabled = false
  bind-address = ":8089"
  database = "udp"
  retention-policy = ""
  batch-size = 5000
  batch-pending = 10
  read-buffer = 0
  batch-timeout = "1s"
  precision = ""

[continuous_queries]
  log-enabled = true
  enabled = true
  query-stats-enabled = false
  run-interval = "1s"

[tls]
  min-version = "tls1.2"
  max-version = "tls1.3"
`

var testConfigV1obsoleteArrays = `
reporting-disabled = true

[meta]
  dir = "/var/lib/influxdb/meta"

[data]
  dir = "/var/lib/influxdb/data"
  wal-dir = "/var/lib/influxdb/wal"

[http]
  enabled = true
  bind-address = ":8086"

[[udp]]
  enabled = false
  bind-address = ":8089"
  database = "udp"
  retention-policy = ""
  batch-size = 5000
  batch-pending = 10
  read-buffer = 0
  batch-timeout = "1s"
  precision = ""

[[udp]]
  enabled = false
  bind-address = ":8090"
  database = "udp2"
  retention-policy = ""
  batch-size = 5000
  batch-pending = 10
  read-buffer = 0
  batch-timeout = "1s"
  precision = ""
`

var testConfigV1empty = `
`

var testConfigV1QueryConcurrency = `
[coordinator]
  max-concurrent-queries = 128
`

// 2.x test configs

var testConfigV2minimal = `reporting-disabled = false
bolt-path = "/db/.influxdbv2/influxd.bolt"
engine-path = "/db/.influxdbv2/engine"
http-bind-address = ":8086"
influxql-max-select-point = 0
log-level = "debug"
storage-retention-check-interval = "30m"
storage-shard-precreator-check-interval = "5m"
storage-wal-fsync-delay = "100s"
tls-cert = "/etc/ssl/influxdb.pem"
tls-key = "/etc/ssl/influxdb-key.pem"
pprof-disabled = true
`

var testConfigV2default = `reporting-disabled = false
bolt-path = "/db/.influxdbv2/influxd.bolt"
engine-path = "/db/.influxdbv2/engine"
http-bind-address = ":8086"
influxql-max-select-buckets = 0
influxql-max-select-point = 0
influxql-max-select-series = 0
log-level = "info"
query-concurrency = 0
storage-cache-max-memory-size = 1073741824
storage-cache-snapshot-memory-size = 26214400
storage-cache-snapshot-write-cold-duration = "10m0s"
storage-compact-full-write-cold-duration = "4h0m0s"
storage-compact-throughput-burst = 50331648
storage-max-concurrent-compactions = 0
storage-max-index-log-file-size = 1048576
storage-retention-check-interval = "30m0s"
storage-series-file-max-concurrent-snapshot-compactions = 0
storage-series-id-set-cache-size = 100
storage-shard-precreator-advance-period = "30m0s"
storage-shard-precreator-check-interval = "10m0s"
storage-tsm-use-madv-willneed = false
storage-validate-keys = false
storage-wal-fsync-delay = "0s"
tls-cert = "/etc/ssl/influxdb.pem"
tls-key = ""
pprof-disabled = false
`

var testConfigV2obsoleteArrays = `reporting-disabled = true
bolt-path = "/db/.influxdbv2/influxd.bolt"
engine-path = "/db/.influxdbv2/engine"
http-bind-address = ":8086"
`

var testConfigV2empty = `
bolt-path = "/db/.influxdbv2/influxd.bolt"
engine-path = "/db/.influxdbv2/engine"
`

var testConfigV2QueryConcurrency = `
bolt-path = "/db/.influxdbv2/influxd.bolt"
engine-path = "/db/.influxdbv2/engine"
query-concurrency = 128
query-queue-size = 128
`
