package validator_test

import (
	"fmt"
	"testing"

	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/testhelper"
	"github.com/fiskerinc/cloud-services/pkg/validator"

	v "github.com/go-playground/validator/v10"
)

type TestCase struct {
	Name          string
	Validate      interface{}
	ExpectedError string
}

func TestValidateStruct(t *testing.T) {
	tests := []TestCase{
		{
			Name:          "Empty Car",
			Validate:      common.Car{},
			ExpectedError: "VIN required. Year required. Model required. Trim required",
		},
		{
			Name: "Valid Car VIN",
			Validate: common.Car{
				VIN: "JTKJF5C77E3095776",
			},
			ExpectedError: "Year required. Model required. Trim required",
		},
		{
			Name: "Valid Car",
			Validate: common.Car{
				VIN:   "JTKJF5C77E3095776",
				Year:  2022,
				Model: "Ocean",
				Trim:  "Basic",
			},
			ExpectedError: "",
		},
		{
			Name: "Invalid Car VIN",
			Validate: common.Car{
				VIN: "JTKJF5C77E3095776X",
			},
			ExpectedError: "VIN 'JTKJF5C77E3095776X' invalid. Year required. Model required. Trim required",
		},
	}

	validationTestRunner(t, tests, validator.ValidateStruct)
}

func TestValidateSerialStruct(t *testing.T) {
	tests := []TestCase{
		{
			Name:          "Empty Cert",
			Validate:      common.CertificateRevokeRequest{},
			ExpectedError: "Serial required",
		},
		{
			Name: "Valid Cert Serial",
			Validate: common.CertificateRevokeRequest{
				Serial: "02-89-1f-ec-82-69-8a-ce-59-9c-ab-6a-ad-03-b3-c4-41-bd-0d-26",
			},
			ExpectedError: "",
		},
		{
			Name: "Valid Cert Serial 2",
			Validate: common.CertificateRevokeRequest{
				Serial: "02-89-1f-ec-82-69-8a-ce-59-9c-ab-6a-ad-03-b3-c4-41-bd-0d",
			},
			ExpectedError: "",
		},
		{
			Name: "Wrong Cert Serial",
			Validate: common.CertificateRevokeRequest{
				Serial: "XXXXXXXXXXX",
			},
			ExpectedError: "Serial 'XXXXXXXXXXX' invalid",
		},
	}

	validationTestRunner(t, tests, validator.ValidateStruct)
}

func TestValidateNonRequired(t *testing.T) {
	tests := []TestCase{
		{
			Name: "Invalid Car VIN",
			Validate: common.Car{
				VIN:   "JTKJF5C77E3095776X",
				Model: "Ocean",
				Year:  2021,
			},
			ExpectedError: "VIN 'JTKJF5C77E3095776X' invalid",
		},
		{
			Name:          "Empty Car",
			Validate:      common.Car{},
			ExpectedError: "",
		},
		{
			Name: "Valid Car VIN",
			Validate: common.Car{
				VIN: "JTKJF5C77E3095776",
			},
			ExpectedError: "",
		},
		{
			Name: "Valid Car",
			Validate: common.Car{
				VIN:   "JTKJF5C77E3095776",
				Year:  2022,
				Model: "Ocean",
			},
			ExpectedError: "",
		},
	}

	validationTestRunner(t, tests, validator.ValidateNonRequired)
}

func validationTestRunner(t *testing.T, tests []TestCase, validatorFunc func(interface{}) error) {
	for _, test := range tests {
		err := validatorFunc(test.Validate)
		if err != nil {
			valerrs, ok := err.(v.ValidationErrors)
			if ok {
				str := validator.GetError(&valerrs)
				if str != test.ExpectedError {
					t.Errorf(testhelper.TestErrorTemplate, test.Name, test.ExpectedError, str)
				}
			} else {
				t.Errorf(testhelper.TestErrorTemplate, test.Name, "ValidationErrors", err)
			}
		} else if test.ExpectedError != "" {
			t.Errorf(testhelper.TestErrorTemplate, test.Name, test.ExpectedError, nil)
		}
	}
}

func TestVINValidation(t *testing.T) {
	expected := "VIN '%s' invalid"
	test := common.Car{
		VIN:   "1G1FP87S3GN100062",
		Model: "Ocean",
		Year:  2021,
		Trim:  "Basic",
	}

	err := validator.ValidateStruct(test)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "Good validation", "no errors", err)
	}

	test.VIN = "1G1FP87S3GN100062XXXXX"

	err = validator.ValidateStruct(test)
	if err != nil {
		_, msg := validator.GetValidationErrorMsg(err)
		if msg != fmt.Sprintf(expected, test.VIN) {
			t.Errorf(testhelper.TestErrorTemplate, "Bad VIN", fmt.Sprintf(expected, test.VIN), msg)
		}
	}

	test.VIN = "1G1FP87S3GN10006"

	err = validator.ValidateStruct(test)
	if err != nil {
		_, msg := validator.GetValidationErrorMsg(err)
		if msg != fmt.Sprintf(expected, test.VIN) {
			t.Errorf(testhelper.TestErrorTemplate, "Bad VIN", fmt.Sprintf(expected, test.VIN), msg)
		}
	}
}

func TestUpdatePackageValidation(t *testing.T) {
	up := common.UpdateManifest{}

	err := validator.ValidateStruct(up)
	if err != nil {
		_, msg := validator.GetValidationErrorMsg(err)
		expected := "Name required"
		if msg != expected {
			t.Errorf(testhelper.TestErrorTemplate, "Empty UpdateManifest", expected, msg)
		}
	}

	up.ReleaseNotes = "XXXXX"
	err = validator.ValidateStruct(up)
	if err != nil {
		_, msg := validator.GetValidationErrorMsg(err)
		expected := "Name required. ReleaseNotes invalid url"
		if msg != expected {
			t.Errorf(testhelper.TestErrorTemplate, "Empty UpdateManifest", expected, msg)
		}
	}
}
