package queries_test

import (
	"fmt"
	"strings"
	"testing"

	"github.com/go-pg/pg/v10/orm"

	m "github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/db"
	"github.com/fiskerinc/cloud-services/pkg/db/queries"
	"github.com/fiskerinc/cloud-services/pkg/testhelper"

	"github.com/go-pg/pg/v10"
	"github.com/google/uuid"
	"github.com/fiskerinc/cloud-services/pkg/utils/elptr"
)

const testCarVIN = "1GNGC26R1XJ407649"
const testCarVIN2 = "1GNGC26R1XJ407648"

var qc queries.CarsInterface
var conn *pg.DB
var testCarID string
var testDriverID string

func TestCarsIntegration(t *testing.T) {
	t.Skip()

	defer testCarDelete(t, testCarVIN)
	defer testCarDelete(t, testCarVIN2)
	setupCarsTests()
	clearTestCar()
	testCarInsert(t, testCarVIN)
	testCarInsert(t, testCarVIN2)
	testCarECU(t)
	testCarAddDriver(t)
	testCarSelectByVIN(t)
	testCarSelect(t)
	testCarUpdate(t)
	testSetSetting(t)
	testCarSearch(t)
	testGetModels(t)
	testGetYears(t)
	testSelectCarToDriver(t)
	testGetCarsForDriver(t)
	testGetCount(t)
	testCarVINsSearch(t)
}

func setupCarsTests() {
	instance := &queries.Cars{}
	conn = instance.GetDBConn()
	conn.AddQueryHook(db.SQLLogger{})
	qc = instance
	client := instance.GetClient()
	client.InitSchema([]interface{}{
		(*m.CarECU)(nil),
		(*m.Car)(nil),
		(*m.Driver)(nil),
		(*m.CarToDriver)(nil),
		(*m.CarSetting)(nil),
	})
}

func testCarECU(t *testing.T) {
	ecu := m.CarECU{
		VIN:     testCarVIN,
		ECU:     "ECU1",
		Version: "1002",
	}

	err := qc.UpdateCarECU(&ecu)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "UpdateCarECU insert", "No error", err)
	}

	ecu.Version = "1000"
	err = qc.UpdateCarECU(&ecu)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "UpdateCarECU2 update", "No error", err)
	}

	car := m.Car{
		VIN: testCarVIN,
	}
	err = qc.Load(&car)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "UpdateCarECU2 update", "No error", err)
	}
	if !strings.Contains(car.ECUList, "ECU1 1000") {
		t.Errorf(testhelper.TestErrorTemplate, "UpdateCarECU2 update", "ECU1 1001", car.ECUList)
	}
}

func testCarSearch(t *testing.T) {
	search := m.CarSearch{
		Search: testCarVIN,
	}
	options := queries.PageQueryOptions{
		Offset: 0,
		Limit:  0,
		Order:  "vin DESC",
	}

	result, err := qc.Search(&search, &options)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Car Search", "No error", err)
	}
	if len(result) != 1 {
		t.Errorf(testhelper.TestErrorTemplate, "Car Search result", 1, len(result))
	}
}

func testCarVINsSearch(t *testing.T) {
	search := m.CarSearch{
		VINs: testCarVIN + "," + testCarVIN2,
	}

	options := queries.PageQueryOptions{
		Offset: 0,
		Limit:  0,
		Order:  "vin DESC",
	}

	result, err := qc.Search(&search, &options)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Car Search", "No error", err)
	}
	if len(result) != 2 {
		t.Errorf(testhelper.TestErrorTemplate, "Car Search result", 2, len(result))
	}
}

func testSetSetting(t *testing.T) {
	setting := m.CarSetting{
		VIN:      testCarVIN,
		DriverID: testDriverID,
		Name:     "TestSetting",
		Value:    "TestValue",
	}
	result, err := qc.SetSetting(&setting)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "testSetSetting", result, err)
	}
}

func testCarInsert(t *testing.T, vin string) {
	car := m.Car{
		VIN:   vin,
		Model: "Ocean",
		Year:  2022,
		Trim:  "Base",
	}

	_, err := qc.Insert(&car)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "SelectOrInsert", "nil", err)
	}

	testCarID = car.VIN
}

func testCarAddDriver(t *testing.T) {
	car := m.Car{
		VIN: testCarVIN,
	}

	drivers := []m.Driver{
		{
			ID: "TEST-001",
		},
		{
			ID: "TEST-002",
		},
	}
	_, err := qc.AddDriver(&car, &m.Driver{}, "driver")
	if err == nil {
		t.Errorf(testhelper.TestErrorTemplate, "AddDriver errored", "Error", err)
	}

	qc.Load(&car)
	for _, driver := range drivers {
		_, err = conn.Model(&driver).Where("id = ?id").SelectOrInsert()
		if err != nil {
			t.Errorf(testhelper.TestErrorTemplate, "Insert driver", "nil", err)
		}

		_, err := qc.AddDriver(&car, &driver, "driver")
		if err != nil {
			t.Errorf(testhelper.TestErrorTemplate, "AddDriver", "nil", err)
		}

		testDriverID = driver.ID

		_, err = qc.AddDriver(&car, &driver, "driverX")
		if err != nil {
			t.Errorf(testhelper.TestErrorTemplate, "AddDriver", "nil", err)
		}
	}

	qc.Load(&car)
	if len(car.Drivers) != 2 {
		t.Errorf(testhelper.TestErrorTemplate, "Added drivers", 2, len(car.Drivers))
	}
}

func testCarSelectByVIN(t *testing.T) {
	car, err := qc.SelectByVIN(testCarVIN)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "SelectByID error", "nil", err)
	}
	if car.VIN != testCarVIN {
		t.Errorf(testhelper.TestErrorTemplate, "VIN", testCarVIN, car.VIN)
	}

	err = qc.Load(car)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Load error", "nil", err)
	}

	if len(car.Drivers) != 2 {
		t.Errorf(testhelper.TestErrorTemplate, "Drivers", 2, len(car.Drivers))
	}
}

func testCarSelect(t *testing.T) {
	car := m.Car{
		Year: 2022,
	}
	options := queries.PageQueryOptions{
		Offset: 0,
		Limit:  0,
		Order:  "vin DESC",
	}

	cars, err := qc.Select(&car, &options)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Select error", "nil", err)
	}
	count := len(cars)
	if count == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Count", "more than 0", len(cars))
	}

	options.Offset = count
	options.Limit = 10
	cars, err = qc.Select(&car, &options)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Select error", "nil", err)
	}
	if len(cars) != 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Count", 0, len(cars))
	}
}

func testCarUpdate(t *testing.T) {
	car := m.Car{
		VIN: testCarVIN,
	}

	err := qc.Load(&car)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Load", "nil", err)
	}

	car.Year = 2020
	car.Model = "Ocean S"
	car.Trim = "Sport"
	result, err := qc.Update(&car)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Update", "nil", err)
	}
	if result.RowsAffected() != 1 {
		t.Errorf(testhelper.TestErrorTemplate, "Update RowsAffected", 1, result.RowsAffected())
	}

	car = m.Car{
		VIN: testCarVIN,
	}
	err = qc.Load(&car)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Reload", "nil", err)
	}
	if car.Year != 2020 {
		t.Errorf(testhelper.TestErrorTemplate, "Update Year", 2020, car.Year)
	}
	if car.Model != "Ocean S" {
		t.Errorf(testhelper.TestErrorTemplate, "Update Model", "Ocean S", car.Model)
	}
	if !car.CreatedAt.Before(*car.UpdatedAt) {
		t.Errorf(testhelper.TestErrorTemplate, "UpdatedAt", car.CreatedAt, car.UpdatedAt)
	}
}

func testCarDelete(t *testing.T, vin string) {
	car := m.Car{
		Year: 2022,
	}

	_, err := qc.Delete(&car)
	if err == nil {
		t.Errorf(testhelper.TestErrorTemplate, "No ID", "No ID error", err)
	}

	car = m.Car{
		VIN: vin,
	}
	carVIN := car.VIN

	_, err = qc.Delete(&car)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Delete", "nil", err)
	}

	cardrivers := []m.CarToDriver{}

	count, err := conn.Model(&cardrivers).Where("vin = ?", carVIN).SelectAndCount()
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Count", "nil", err)
	}
	if count > 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Count", 0, count)
	}
}

func testGetModels(t *testing.T) {
	models, err := qc.GetModels()
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "GetModels", "nil", err)
	}
	if len(models) == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Models Count", "More than 0", len(models))
	}
}

func testGetYears(t *testing.T) {
	years, err := qc.GetYears()
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "GetYears", "nil", err)
	}
	if len(years) == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Years Count", "More than 0", len(years))
	}
}

func testSelectCarToDriver(t *testing.T) {
	filter := m.CarToDriver{
		DriverID: "TEST-001",
	}

	drivers, err := qc.SelectCarToDriver(&filter)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "SelectCarToDriver", "nil", err)
	} else if len(drivers) == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "SelectCarToDriver", "nil", err)
	}
}

func testGetCarsForDriver(t *testing.T) {
	carToDrivers, err := qc.GetCarsForDriver("TEST-001")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "GetCarsForDriver", nil, err)
	}
	if len(carToDrivers) == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Car to Drivers Count", "More than 0", len(carToDrivers))
	}
}

func testGetCount(t *testing.T) {
	count, err := qc.Count(&m.Car{VIN: testCarVIN})
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Count", nil, err)
	}
	if count != 1 {
		t.Errorf(testhelper.TestErrorTemplate, "Count", 1, count)
	}
}

func clearTestCar() {
	car := m.Car{
		VIN: testCarVIN,
	}
	result, err := conn.Model(&car).Where("vin = ?vin").Delete()
	fmt.Println(result, err)
}

func TestSelectCarToDriver(t *testing.T) {
	t.Skip()
	cars := queries.Cars{}
	rel, err := cars.AddDriver(&m.Car{VIN: "1G1FP87S3GN100062"}, &m.Driver{ID: "ddf34966-9677-46de-b2eb-ddf501968ea5"}, "role")
	if err != nil {
		t.Error(err)
		return
	}
	defer cars.RemoveDriver(rel.VIN, rel.DriverID)

	subtypes := queries.SubscriptionTypes{}
	subtype := m.SubscriptionType{
		Name:          fmt.Sprintf("test %v", uuid.New()),
		Description:   "this is a test",
		Destination:   "ICC",
		Currency:      "USD",
		Price:         0,
		DurationValue: 1,
		DurationUnit:  "Hours",
	}
	_, err = subtypes.Insert(&subtype)
	if err != nil {
		t.Error(err)
		return
	}
	defer subtypes.Delete(&subtype)

	subs := queries.Subscriptions{}
	sub, err := subs.Create(&subtype, rel)
	if err != nil {
		t.Error(err)
		return
	}
	defer subs.Delete(&queries.SubscriptionDeleteRequest{ID: sub.ID})

	setting := m.CarSetting{VIN: testCarVIN, DriverID: testDriverID, Name: "test123", Value: "XXXX"}
	_, err = cars.SetSetting(&setting)
	if err != nil {
		t.Error(err)
		return
	}
	defer cars.DeleteSetting(&setting)

	carToDrivers, err := cars.SelectCarToDriver(&m.CarToDriver{VIN: rel.VIN, DriverID: rel.DriverID})
	if err != nil {
		t.Error(err)
		return
	}
	if len(carToDrivers) != 1 {
		t.Errorf(testhelper.TestErrorTemplate, "SelectCarToDriver count", 1, len(carToDrivers))
		return
	}
	if len(carToDrivers[0].Settings) != 1 {
		t.Errorf(testhelper.TestErrorTemplate, "SelectCarToDriver settings", 1, len(carToDrivers))
	}
	if len(carToDrivers[0].Subscriptions) != 1 {
		t.Errorf(testhelper.TestErrorTemplate, "SelectCarToDriver settings", 1, len(carToDrivers))
	}
}

func queryToString(q *orm.Query) string {
	value, _ := q.AppendQuery(orm.NewFormatter(), nil)

	return string(value)
}

func Test_addOnlineFilter(t *testing.T) {
	type args struct {
		query  *orm.Query
		filter *m.CarSearch
	}
	tests := []struct {
		name   string
		args   args
		expect string
	}{
		{
			name: "empty filter",
			args: args{
				query:  new(orm.Query),
				filter: &m.CarSearch{},
			},
			expect: "SELECT *",
		},
		{
			name: "filter with online true but no online cars",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{Online: &m.CarOnlineFilter{
					Online:     elptr.ElPtr(true),
					VINsOnline: []string{},
				}},
			},
			expect: "SELECT * WHERE (vin IN (''))",
		},
		{
			name: "filter with hmi online true but no online cars",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{Online: &m.CarOnlineFilter{
					HMI:        elptr.ElPtr(true),
					VINsOnline: []string{},
				}},
			},
			expect: "SELECT * WHERE (vin IN (''))",
		},

		{
			name: "filter with online false",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{Online: &m.CarOnlineFilter{
					Online: elptr.ElPtr(false),
				}},
			},
			expect: "SELECT * WHERE (vin NOT IN (''))",
		},
		{
			name: "filter with hmi online false",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{Online: &m.CarOnlineFilter{
					Online: elptr.ElPtr(false),
				}},
			},
			expect: "SELECT * WHERE (vin NOT IN (''))",
		},
		{
			name: "filter with one of them being true 1",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{
					Online: &m.CarOnlineFilter{
						Online: elptr.ElPtr(true),
						HMI:    elptr.ElPtr(false),
					},
				},
			},
			expect: "SELECT * WHERE (vin IN (''))",
		},
		{
			name: "filter with one of them being true 2",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{
					Online: &m.CarOnlineFilter{
						Online: elptr.ElPtr(false),
						HMI:    elptr.ElPtr(true),
					},
				},
			},
			expect: "SELECT * WHERE (vin IN (''))",
		},
		{
			name: "filter with online true",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{Online: &m.CarOnlineFilter{
					Online:     elptr.ElPtr(true),
					VINsOnline: []string{"1G1FP87S3GN100062", "1G1FP87S3GN100063"},
				}},
			},
			expect: "SELECT * WHERE (vin IN ('1G1FP87S3GN100062','1G1FP87S3GN100063'))",
		},
		{
			name: "filter with hmi online true",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{Online: &m.CarOnlineFilter{
					Online:     elptr.ElPtr(true),
					VINsOnline: []string{"1G1FP87S3GN100064", "1G1FP87S3GN100065"},
				}},
			},
			expect: "SELECT * WHERE (vin IN ('1G1FP87S3GN100064','1G1FP87S3GN100065'))",
		},
		{
			name: "filter with both being online true",
			args: args{
				query: new(orm.Query),
				filter: &m.CarSearch{Online: &m.CarOnlineFilter{
					Online: elptr.ElPtr(true),
					HMI:    elptr.ElPtr(true),
					VINsOnline: []string{
						"1G1FP87S3GN100064",
						"1G1FP87S3GN100065",
						"1G1FP87S3GN100062",
						"1G1FP87S3GN100063"},
				}}},
			expect: "SELECT * WHERE (vin IN ('1G1FP87S3GN100064','1G1FP87S3GN100065','1G1FP87S3GN100062','1G1FP87S3GN100063'))",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			queries.AddOnlineFilter(tt.args.query, tt.args.filter)

			if got := queryToString(tt.args.query); got != tt.expect {
				t.Errorf("addOnlineFilter() = %v, want %v", got, tt.expect)
			}
		})
	}
}

func TestMeLocal(t *testing.T) {
	instance := &queries.Cars{}
	conn = instance.GetDBConn()

	certificat := &queries.Certificates{}
	//certificat.SetClient(conn)
	cert, err := certificat.SelectMostRecent("p", "j")
	t.Log(err)
	t.Log(cert)
}
