// VulcanizeDB
// Copyright © 2018 Vulcanize

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package plugin_test

import (
	"plugin"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/spf13/viper"

	"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
	"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
	"github.com/vulcanize/vulcanizedb/pkg/config"
	"github.com/vulcanize/vulcanizedb/pkg/core"
	"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
	"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
	p2 "github.com/vulcanize/vulcanizedb/pkg/plugin"
	"github.com/vulcanize/vulcanizedb/pkg/plugin/helpers"
	"github.com/vulcanize/vulcanizedb/pkg/plugin/test_helpers"
)

var genConfig = config.Plugin{
	Initializers: map[string]string{
		"bite": "github.com/vulcanize/mcd_transformers/transformers/bite/initializer",
		"deal": "github.com/vulcanize/mcd_transformers/transformers/deal/initializer",
	},
	Dependencies: map[string]string{
		"mcd_transformers": "github.com/vulcanize/mcd_transformers",
	},
	//Migrations: map[string]string{"mcd_transformers" : "db/migrations"},
	FileName: "externalTestTransformerSet",
	FilePath: "$GOPATH/src/github.com/vulcanize/vulcanizedb/pkg/plugin/test_helpers/test",
	Save:     false,
}

var dbConfig = config.Database{
	Hostname: "localhost",
	Port:     5432,
	Name:     "vulcanize_private",
}

type Exporter interface {
	Export() []transformer.TransformerInitializer
}

var _ = Describe("Generator test", func() {
	var g p2.Generator
	var goPath, soPath string
	var err error
	var bc core.BlockChain
	var db *postgres.DB
	var hr repositories.HeaderRepository
	var headerID int64
	viper.SetConfigName("compose")
	viper.AddConfigPath("$GOPATH/src/github.com/vulcanize/vulcanizedb/environments/")

	BeforeEach(func() {
		goPath, soPath, err = genConfig.GetPluginPaths()
		Expect(err).ToNot(HaveOccurred())
		g, err = p2.NewGenerator(genConfig, dbConfig)
		Expect(err).ToNot(HaveOccurred())
		err = g.GenerateExporterPlugin()
		Expect(err).ToNot(HaveOccurred())
	})

	AfterEach(func() {
		err := helpers.ClearFiles(goPath, soPath)
		Expect(err).ToNot(HaveOccurred())
	})

	Describe("GenerateTransformerPlugin", func() {
		It("It bundles the specified transformer initializers into a Exporter object and creates .so", func() {
			plug, err := plugin.Open(soPath)
			Expect(err).ToNot(HaveOccurred())
			symExporter, err := plug.Lookup("Exporter")
			Expect(err).ToNot(HaveOccurred())
			exporter, ok := symExporter.(Exporter)
			Expect(ok).To(Equal(true))
			initializers := exporter.Export()
			Expect(len(initializers)).To(Equal(2))
		})

		It("Loads our generated Exporter and uses it to import an arbitrary set of TransformerInitializers that we can execute over", func() {
			db, bc = test_helpers.SetupDBandBC()
			defer test_helpers.TearDown(db)

			hr = repositories.NewHeaderRepository(db)
			header1, err := bc.GetHeaderByNumber(9377319)
			Expect(err).ToNot(HaveOccurred())
			headerID, err = hr.CreateOrUpdateHeader(header1)
			Expect(err).ToNot(HaveOccurred())

			plug, err := plugin.Open(soPath)
			Expect(err).ToNot(HaveOccurred())
			symExporter, err := plug.Lookup("Exporter")
			Expect(err).ToNot(HaveOccurred())
			exporter, ok := symExporter.(Exporter)
			Expect(ok).To(Equal(true))
			initializers := exporter.Export()

			w := watcher.NewWatcher(db, bc)
			w.AddTransformers(initializers)
			err = w.Execute()
			Expect(err).ToNot(HaveOccurred())

			type model struct {
				Ilk              string
				Urn              string
				Ink              string
				Art              string
				IArt             string
				Tab              string
				NFlip            string
				LogIndex         uint   `db:"log_idx"`
				TransactionIndex uint   `db:"tx_idx"`
				Raw              []byte `db:"raw_log"`
				Id               int64  `db:"id"`
				HeaderId         int64  `db:"header_id"`
			}

			returned := model{}

			err = db.Get(&returned, `SELECT * FROM maker.bite WHERE header_id = $1`, headerID)
			Expect(err).ToNot(HaveOccurred())
			Expect(returned.Ilk).To(Equal("ETH"))
			Expect(returned.Urn).To(Equal("0x0000d8b4147eDa80Fec7122AE16DA2479Cbd7ffB"))
			Expect(returned.Ink).To(Equal("80000000000000000000"))
			Expect(returned.Art).To(Equal("11000000000000000000000"))
			Expect(returned.IArt).To(Equal("12496609999999999999992"))
			Expect(returned.Tab).To(Equal("11000000000000000000000"))
			Expect(returned.NFlip).To(Equal("7"))
			Expect(returned.TransactionIndex).To(Equal(uint(1)))
			Expect(returned.LogIndex).To(Equal(uint(4)))
		})
	})
})
