/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package main

import (
	"fmt"
	htrace "htrace/client"
	"htrace/common"
	"htrace/conf"
	"reflect"
	"testing"
	"time"
)

func compareTotals(a, b common.SpanMetricsMap) bool {
	for k, v := range a {
		if !reflect.DeepEqual(v, b[k]) {
			return false
		}
	}
	for k, v := range b {
		if !reflect.DeepEqual(v, a[k]) {
			return false
		}
	}
	return true
}

type Fatalfer interface {
	Fatalf(format string, args ...interface{})
}

func assertNumWrittenEquals(t Fatalfer, msink *MetricsSink,
	expectedNumWritten int) {
	var sstats common.ServerStats
	msink.PopulateServerStats(&sstats)
	if sstats.WrittenSpans != uint64(expectedNumWritten) {
		t.Fatalf("sstats.WrittenSpans = %d, but expected %d\n",
			sstats.WrittenSpans, len(SIMPLE_TEST_SPANS))
	}
	if sstats.HostSpanMetrics["127.0.0.1"] == nil {
		t.Fatalf("no entry for sstats.HostSpanMetrics[127.0.0.1] found.")
	}
	if sstats.HostSpanMetrics["127.0.0.1"].Written !=
		uint64(expectedNumWritten) {
		t.Fatalf("sstats.HostSpanMetrics[127.0.0.1].Written = %d, but "+
			"expected %d\n", sstats.HostSpanMetrics["127.0.0.1"].Written,
			len(SIMPLE_TEST_SPANS))
	}
}

func TestMetricsSinkPerHostEviction(t *testing.T) {
	cnfBld := conf.Builder{
		Values:   conf.TEST_VALUES(),
		Defaults: conf.DEFAULTS,
	}
	cnfBld.Values[conf.HTRACE_METRICS_MAX_ADDR_ENTRIES] = "2"
	cnf, err := cnfBld.Build()
	if err != nil {
		t.Fatalf("failed to create conf: %s", err.Error())
	}
	msink := NewMetricsSink(cnf)
	msink.UpdatePersisted("192.168.0.100", 20, 10)
	msink.UpdatePersisted("192.168.0.101", 20, 10)
	msink.UpdatePersisted("192.168.0.102", 20, 10)
	msink.lock.Lock()
	defer msink.lock.Unlock()
	if len(msink.HostSpanMetrics) != 2 {
		for k, v := range msink.HostSpanMetrics {
			fmt.Printf("WATERMELON: [%s] = [%s]\n", k, v)
		}
		t.Fatalf("Expected len(msink.HostSpanMetrics) to be 2, but got %d\n",
			len(msink.HostSpanMetrics))
	}
}

func TestIngestedSpansMetricsRest(t *testing.T) {
	testIngestedSpansMetricsImpl(t, false)
}

func TestIngestedSpansMetricsPacked(t *testing.T) {
	testIngestedSpansMetricsImpl(t, true)
}

func testIngestedSpansMetricsImpl(t *testing.T, usePacked bool) {
	htraceBld := &MiniHTracedBuilder{Name: "TestIngestedSpansMetrics",
		DataDirs: make([]string, 2),
	}
	ht, err := htraceBld.Build()
	if err != nil {
		t.Fatalf("failed to create datastore: %s", err.Error())
	}
	defer ht.Close()
	var hcl *htrace.Client
	hcl, err = htrace.NewClient(ht.ClientConf(), &htrace.TestHooks{
		HrpcDisabled: !usePacked,
	})
	if err != nil {
		t.Fatalf("failed to create client: %s", err.Error())
	}

	NUM_TEST_SPANS := 12
	allSpans := createRandomTestSpans(NUM_TEST_SPANS)
	err = hcl.WriteSpans(allSpans)
	if err != nil {
		t.Fatalf("WriteSpans failed: %s\n", err.Error())
	}
	for {
		var stats *common.ServerStats
		stats, err = hcl.GetServerStats()
		if err != nil {
			t.Fatalf("GetServerStats failed: %s\n", err.Error())
		}
		if stats.IngestedSpans == uint64(NUM_TEST_SPANS) {
			break
		}
		time.Sleep(1 * time.Millisecond)
	}
}

func TestCircBuf32(t *testing.T) {
	cbuf := NewCircBufU32(3)
	// We arbitrarily define that empty circular buffers have an average of 0.
	if cbuf.Average() != 0 {
		t.Fatalf("expected empty CircBufU32 to have an average of 0.\n")
	}
	if cbuf.Max() != 0 {
		t.Fatalf("expected empty CircBufU32 to have a max of 0.\n")
	}
	cbuf.Append(2)
	if cbuf.Average() != 2 {
		t.Fatalf("expected one-element CircBufU32 to have an average of 2.\n")
	}
	cbuf.Append(10)
	if cbuf.Average() != 6 {
		t.Fatalf("expected two-element CircBufU32 to have an average of 6.\n")
	}
	cbuf.Append(12)
	if cbuf.Average() != 8 {
		t.Fatalf("expected three-element CircBufU32 to have an average of 8.\n")
	}
	cbuf.Append(14)
	// The 14 overwrites the original 2 element.
	if cbuf.Average() != 12 {
		t.Fatalf("expected three-element CircBufU32 to have an average of 12.\n")
	}
	cbuf.Append(1)
	// The 1 overwrites the original 10 element.
	if cbuf.Average() != 9 {
		t.Fatalf("expected three-element CircBufU32 to have an average of 12.\n")
	}
	if cbuf.Max() != 14 {
		t.Fatalf("expected three-element CircBufU32 to have a max of 14.\n")
	}
}
