package recorder

import (
	"context"
	"io/ioutil"
	"os"

	"github.com/pingcap-inc/alioth/pkg/util"
	logpattern_go_proto "github.com/pingcap-inc/alioth/proto"
	"github.com/pingcap-inc/alioth/storage"
	. "github.com/pingcap/check"
)

var _ = Suite(&testCoverageSuite{})

type testCoverageSuite struct {
}

func (t *testCoverageSuite) TestCoverage(c *C) {
	tmpdir, err := ioutil.TempDir("./", "logpattern_test")
	c.Assert(err, IsNil)
	defer os.RemoveAll(tmpdir)

	store, err := storage.NewStore(tmpdir)
	c.Assert(err, IsNil)

	cov := NewCoverager(store)
	exptectedRes := t.testRecordCoverage(c, cov)

	c.Assert(cov.coverage, HasLen, 3)
	cov.Forach(context.Background(), func(ctx context.Context, lg *logpattern_go_proto.Coverage) error {
		expect := exptectedRes[util.PosToStr(lg.Pos)]
		c.Assert(lg, DeepEquals, expect)
		return nil
	})

	c.Assert(cov.Flush(context.Background()), IsNil)

	cov2 := NewCoverager(store)
	err = cov2.LoadCoverage(context.Background())
	c.Assert(err, IsNil)
	c.Assert(cov2.coverage, HasLen, 3)
	cov2.Forach(context.Background(), func(ctx context.Context, lg *logpattern_go_proto.Coverage) error {
		expect := exptectedRes[util.PosToStr(lg.Pos)]
		c.Assert(lg, DeepEquals, expect)
		cov.Merge(context.Background(), lg)
		return nil
	})
	c.Assert(cov.Flush(context.Background()), IsNil)

	c.Assert(cov.coverage, HasLen, 3)
	cov.Forach(context.Background(), func(ctx context.Context, lg *logpattern_go_proto.Coverage) error {
		expect := exptectedRes[util.PosToStr(lg.Pos)]
		c.Assert(lg, DeepEquals, expect)
		return nil
	})

	err = cov2.LoadCoverage(context.Background())
	c.Assert(err, IsNil)
	c.Assert(cov2.coverage, HasLen, 3)
	cov2.Forach(context.Background(), func(ctx context.Context, lg *logpattern_go_proto.Coverage) error {
		expect := exptectedRes[util.PosToStr(lg.Pos)]
		c.Assert(lg, DeepEquals, expect)
		return nil
	})
}

func (t *testCoverageSuite) testRecordCoverage(c *C, cov *Coverage) map[string]*logpattern_go_proto.Coverage {
	logs := make(map[string]*logpattern_go_proto.Coverage)

	fun := &logpattern_go_proto.FuncInfo{
		Pos: &logpattern_go_proto.Position{
			FilePath: "task.go",
			Pos: &logpattern_go_proto.CodePosition{
				Line:   110,
				Column: 1,
			},
			EndPos: &logpattern_go_proto.CodePosition{
				Line:   200,
				Column: 2,
			},
		},
		Name: "test",
	}

	pkgPath := &logpattern_go_proto.Package{
		Repo: "github.com/pingcap/pingcap-inc/alioth",
		Path: "recorder",
	}

	pattern1 := &logpattern_go_proto.LogPattern{
		Pos: &logpattern_go_proto.Position{
			FilePath: "task.go",
			Pos: &logpattern_go_proto.CodePosition{
				Line:   138,
				Column: 2,
			},
			EndPos: &logpattern_go_proto.CodePosition{
				Line:   138,
				Column: 20,
			},
			PackageInfo: pkgPath,
		},
		Func:      fun,
		Level:     "warn",
		Signature: []string{"test log"},
	}
	log1 := &logpattern_go_proto.Coverage{
		Pos: &logpattern_go_proto.Position{
			FilePath: "task.go",
			Pos: &logpattern_go_proto.CodePosition{
				Line:   138,
				Column: 2,
			},
			EndPos: &logpattern_go_proto.CodePosition{
				Line:   138,
				Column: 20,
			},
			PackageInfo: pkgPath,
		},
		Message:  "test log",
		CovCount: 2,
		CovCountByLog: map[string]int32{
			"./dm_worker1.log": 1,
			"./dm_worker2.log": 1,
		},
	}
	cov.Record("./dm_worker1.log", "test log", pattern1)
	cov.Record("./dm_worker2.log", "test log", pattern1)
	logs[util.PosToStr(log1.Pos)] = log1

	pattern2 := &logpattern_go_proto.LogPattern{
		Pos: &logpattern_go_proto.Position{
			FilePath: "task.go",
			Pos: &logpattern_go_proto.CodePosition{
				Line:   139,
				Column: 2,
			},
			EndPos: &logpattern_go_proto.CodePosition{
				Line:   138,
				Column: 20,
			},
			PackageInfo: pkgPath,
		},
		Func:      fun,
		Level:     "error",
		Signature: []string{"test log"},
	}
	log2 := &logpattern_go_proto.Coverage{
		Pos: &logpattern_go_proto.Position{
			FilePath: "task.go",
			Pos: &logpattern_go_proto.CodePosition{
				Line:   139,
				Column: 2,
			},
			EndPos: &logpattern_go_proto.CodePosition{
				Line:   138,
				Column: 20,
			},
			PackageInfo: pkgPath,
		},
		Message:  "test log",
		CovCount: 1,
		CovCountByLog: map[string]int32{
			"./dm_worker1.log": 1,
		},
	}
	cov.Record("./dm_worker1.log", "test log", pattern2)
	logs[util.PosToStr(log2.Pos)] = log2

	log3 := &logpattern_go_proto.Coverage{
		Pos: &logpattern_go_proto.Position{
			FilePath: "task.go",
			Pos: &logpattern_go_proto.CodePosition{
				Line:   140,
				Column: 2,
			},
			EndPos: &logpattern_go_proto.CodePosition{
				Line:   140,
				Column: 20,
			},
			PackageInfo: pkgPath,
		},
		Message:  "test log",
		CovCount: 1,
		CovCountByLog: map[string]int32{
			"./dm_worker3.log": 1,
		},
	}
	pattern3 := &logpattern_go_proto.LogPattern{
		Pos: &logpattern_go_proto.Position{
			FilePath: "task.go",
			Pos: &logpattern_go_proto.CodePosition{
				Line:   140,
				Column: 2,
			},
			EndPos: &logpattern_go_proto.CodePosition{
				Line:   140,
				Column: 20,
			},
			PackageInfo: pkgPath,
		},
		Func:      fun,
		Level:     "error",
		Signature: []string{"test log"},
	}
	cov.Record("./dm_worker3.log", "test log", pattern3)
	logs[util.PosToStr(log3.Pos)] = log3

	return logs
}
