package queries_test

import (
	"fmt"
	"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"
)

const testCarUpdateVIN = "FISKER1234"
const testCarUpdateVIN2 = "FISKER12345"
const testCarUpdatesPackageName = "TEST CARUPDATES"

func TestCarUpdateIntegration(t *testing.T) {
	t.Skip()
	query := queries.CarUpdates{}
	client := query.GetClient()
	vin, manifestID := setupCarUpdates(&query, client, testCarUpdateVIN)
	vin2, manifestID2 := setupCarUpdates(&query, client, testCarUpdateVIN2)
	defer func() {
		if manifestID > 0 {
			manifest := queries.NewUpdateManifest(nil)
			manifest.Delete(&m.UpdateManifest{ID: manifestID})
		}
	}()
	carupdateID := testCarUpdateSelectOrInsert(t, &query, vin, manifestID)
	testCarUpdateSelectByID(t, &query, carupdateID)
	testCarUpdateSelectByVIN(t, &query, carupdateID)
	testCarUpdateSelectByManifestID(t, &query, manifestID, carupdateID)
	testCarUpdateGetManifest(t, &query, carupdateID)
	testCarUpdateSelect(t, &query, vin, carupdateID)
	testStatusHistoryFilter(t, &query)
	carupdateID2 := testCarUpdateSelectOrInsert(t, &query, vin2, manifestID2)
	testCarUpdateSelectMostRecentByVINs(t, &query, []int64{carupdateID, carupdateID2})
	testCarUpdateDelete(t, &query, carupdateID)
	testCarUpdateDelete(t, &query, carupdateID2)
}

func testCarUpdateSelect(t *testing.T, query queries.CarUpdatesInterface, vin string, carupdateID int64) {
	filter := m.CarUpdate{
		VIN: vin,
	}
	options := &queries.PageQueryOptions{
		Limit:  100,
		Offset: 0,
		Order:  "id desc",
	}

	query.UpdateStatusIfNotRepeat(&m.CarUpdate{
		ID:     carupdateID,
		VIN:    vin,
		Status: "manifest_succeeded",
	})

	carupdates, err := query.Select(&filter, options)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Select", nil, err)
	}

	for _, cu := range carupdates {
		if cu.Status == "cleanup_succeeded" {
			t.Errorf(testhelper.TestErrorTemplate, "Select", "manifest_succeeded", cu.Status)
		}
	}
}

func testStatusHistoryFilter(t *testing.T, query queries.CarUpdatesInterface) {
	mock := new(orm.Query)
	queries.StatusHistoryFilter(mock, []string{"cleanup_succeeded"})
	value, _ := mock.AppendQuery(orm.NewFormatter(), nil)
	actual := string(value)

	expected := `SELECT car_update.id, car_update.vin, car_update.created_at, car_update.updated_at, car_update.update_manifest_id, car_update.error_code, car_update.info, car_update.username, cus.status AS status LEFT JOIN (
		SELECT car_update_id, status,
			ROW_NUMBER() OVER (PARTITION BY car_update_id ORDER BY updated_at DESC) AS rn
		FROM public.car_update_statuses
		WHERE status NOT IN ('cleanup_succeeded')
	) cus ON car_update.id = cus.car_update_id AND cus.rn = 1`

	if actual != expected {
		t.Errorf(testhelper.TestErrorTemplate, "StatusHistoryFilter", expected, actual)
	}
}

func testCarUpdateSelectOrInsert(t *testing.T, query queries.CarUpdatesInterface, vin string, manifestID int64) int64 {
	carupdate := m.CarUpdate{
		VIN:              vin,
		UpdateManifestID: manifestID,
	}

	_, err := query.SelectOrInsert(&carupdate)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "SelectOrInsert", nil, err)
	}
	if carupdate.ID == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "ID", "Has ID", carupdate.ID)
	}

	return carupdate.ID
}

func testCarUpdateSelectByID(t *testing.T, query queries.CarUpdatesInterface, carupdateID int64) {
	carupdate, err := query.SelectByID(carupdateID)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "SelectByID", nil, err)
	}
	checkCarUpdate(t, carupdate, carupdateID)
}

func testCarUpdateSelectByVIN(t *testing.T, query queries.CarUpdatesInterface, carupdateID int64) {
	carupdates, err := query.SelectByVIN(testCarUpdateVIN)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "SelectByVIN", nil, err)
	}
	if len(carupdates) == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Count", "More than 0", len(carupdates))
	}
	for _, update := range carupdates {
		if update.ID == carupdateID {
			checkCarUpdate(t, &update, carupdateID)
			break
		}
	}
}

func testCarUpdateSelectMostRecentByVINs(t *testing.T, query queries.CarUpdatesInterface, carupdateIDs []int64) {
	carupdates, err := query.SelectMostRecentByVINs([]string{testCarUpdateVIN, testCarUpdateVIN2})
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "SelectMostRecentByVINs", nil, err)
	}
	if len(carupdates) == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Count", "More than 0", len(carupdates))
	}
	matchCount := 0
	for _, update := range carupdates {
		for _, carupdateID := range carupdateIDs {
			if update.ID == carupdateID {
				matchCount++
				checkCarUpdate(t, &update, carupdateID)
				break
			}
		}
	}
	if matchCount != 2 {
		t.Errorf(testhelper.TestErrorTemplate, "Matches", "2", matchCount)
	}
}

func testCarUpdateSelectByManifestID(t *testing.T, query queries.CarUpdatesInterface, manifestID int64, carupdateID int64) {
	carupdates, err := query.SelectByManifestID(manifestID)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "SelectByVIN", nil, err)
	}
	if len(carupdates) == 0 {
		t.Errorf(testhelper.TestErrorTemplate, "Count", "More than 0", len(carupdates))
	}
	for _, carupdate := range carupdates {
		if carupdate.VIN == "FISKER1234" {
			checkCarUpdate(t, &carupdate, carupdateID)
			return
		}
	}

	t.Errorf(testhelper.TestErrorTemplate, "checkCarUpdate", "FISKER1234", "VIN not found")
}

func testCarUpdateGetManifest(t *testing.T, query queries.CarUpdatesInterface, carupdateID int64) {
	manifest, err := query.GetManifest(carupdateID)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "GetManifest", nil, err)
	}
	if manifest.Name != testCarUpdatesPackageName {
		t.Errorf(testhelper.TestErrorTemplate, "GetManifest Name", testCarUpdatesPackageName, manifest.Name)
	}
	if len(manifest.ECUs) != 1 {
		t.Errorf(testhelper.TestErrorTemplate, "GetManifest ECUs", 1, len(manifest.ECUs))
	}
}

func testCarUpdateDelete(t *testing.T, query queries.CarUpdatesInterface, carupdateID int64) {
	result, err := query.Delete(&m.CarUpdate{ID: carupdateID})
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Delete", nil, err)
	}
	if result.RowsAffected() != 1 {
		t.Errorf(testhelper.TestErrorTemplate, "Delete RowsAffected", 1, result.RowsAffected())
	}
}

func checkCarUpdate(t *testing.T, carupdate *m.CarUpdate, carupdateID int64) {
	if carupdate.ID != carupdateID {
		t.Errorf(testhelper.TestErrorTemplate, "ID", carupdateID, carupdate.ID)
	}
	if carupdate.Status != "pending" {
		t.Errorf(testhelper.TestErrorTemplate, "ID", "pending", carupdate.Status)
	}
	if carupdate.VIN != testCarUpdateVIN && carupdate.VIN != testCarUpdateVIN2 {
		expected := fmt.Sprintf("%s or %s", testCarUpdateVIN, testCarUpdateVIN2)
		t.Errorf(testhelper.TestErrorTemplate, "Car", expected, carupdate.VIN)
	}
}

func setupCarUpdates(query queries.CarUpdatesInterface, client *db.DBClient, vin string) (string, int64) {
	conn := client.GetConn()
	client.InitSchema([]interface{}{
		(*m.UpdateManifest)(nil),
		(*m.UpdateManifestECU)(nil),
		(*m.UpdateManifestFile)(nil),
		(*m.CarUpdate)(nil),
	})

	conn.AddQueryHook(db.SQLLogger{})

	car := m.Car{
		VIN: vin,
	}
	updatemanifest := m.UpdateManifest{
		Name:    testCarUpdatesPackageName,
		Version: "2.0",
	}

	_, err := conn.Model(&car).Where("vin = ?vin").SelectOrInsert()
	if err != nil {
		fmt.Println(err)
	}

	_, err = conn.Model(&updatemanifest).Where("name = ?name AND version = ?version").SelectOrInsert()
	if err != nil {
		fmt.Println(err)
	}

	ecu := m.UpdateManifestECU{
		UpdateManifestID: updatemanifest.ID,
		ECU:              "TEST",
		Version:          "version",
	}
	_, err = conn.Model(&ecu).Insert()
	if err != nil {
		fmt.Println(err)
	}

	_, err = conn.Model(&m.UpdateManifestFile{
		UpdateManifestECUID: ecu.ID,
		FileID:              "f000000000000000",
		Filename:            "TEST.bin",
		URL:                 "http://fiskerinc.com/download",
	}).Insert()
	if err != nil {
		fmt.Println(err)
	}

	return vin, updatemanifest.ID
}

/*
This test was for an integration test
func TestIntegrationInsert(t *testing.T) {
	opts := pg.Options{
		Addr:     "127.0.0.1:5432",
		User:     "postgres",
		Password: "REPLACE_ME",
	}
	con := pg.Connect(&opts)
	cl := db.DBClient{}
	err := cl.SetConn(con)
	if err != nil {
		t.Error(err)
	}

	p := queries.CarUpdates{}
	p.QueryBase.SetClient(&cl)

	update := m.CarUpdate{
		ID:               325,
		VIN:              testCarUpdateVIN,
		UpdateManifestID: 325,
		Status:           "requirements_failed",
		ErrorCode:        0,
	}
	_, err = p.LogStatusIfNotARepeat(&update)
	if err != nil {
		t.Error(err)
	}

	update.Status = "manifest_succeeded"
	_, err = p.LogStatusIfNotARepeat(&update)
	if err != nil {
		t.Error(err)
	}

	_, err = p.LogStatusIfNotARepeat(&update)
	if err == nil {
		t.Error("Repeated status did not produce an error")
	}
	if !errors.Is(err, queries.RepeatedStatus) {
		t.Error(err)
	}
}
*/
