package main

import (
	"net/http"

	"otaupdate/controllers"
	"otaupdate/handlers"
	"otaupdate/services"

	a "github.com/fiskerinc/cloud-services/pkg/adminroles"
	"github.com/fiskerinc/cloud-services/pkg/common/authproviders"
	h "github.com/fiskerinc/cloud-services/pkg/httphandlers"
	"github.com/fiskerinc/cloud-services/pkg/logger"
	"github.com/fiskerinc/cloud-services/pkg/tracer"
	"github.com/fiskerinc/cloud-services/pkg/utils/app"

	"gopkg.in/DataDog/dd-trace-go.v1/contrib/julienschmidt/httprouter"
)

var authChecker h.AuthCheckerInterface

func init() {
	app.Setup("ota update go", cleanup)

	handlers.InitSwaggerDoc()

	authChecker = &h.AuthAPIToken{
		APITokens: services.GetDB().GetAPITokens(),
		APICalls:  services.GetDB().GetAPICalls(),
		JWTAuth:   true,
	}
}

func main() {
	defer cleanup()

	tracer.Start()
	defer tracer.Stop()

	const port string = ":8077"

	// Enables profiling of application. Run `go get github.com/pkg/profile` for profiling package
	// defer profile.Start(profile.MemProfile, profile.MemProfileRate(1), profile.ProfilePath(".")).Stop()

	swagger := h.GetSwaggerHandler()
	router := httprouter.New()
	router.PanicHandler = h.HttpRouterPanicHandler
	readRoles := []a.RoleID{a.RoleCreate, a.RoleReadOnly}
	createRole := []a.RoleID{a.RoleCreate}
	deleteRole := []a.RoleID{a.RoleDelete}
	supplierAdminRole := []a.RoleID{a.RoleSupplierApprover}
	updateDeployRole := []a.RoleID{a.RoleUpdateDeploy}

	// this means only Fisker AD and API Key can access
	permissionRead := a.RoleMap{
		authproviders.FiskerAD:     readRoles,
		authproviders.FiskerAPIKey: readRoles,
	}
	permissionCreate := a.RoleMap{
		authproviders.FiskerAD:     createRole,
		authproviders.FiskerAPIKey: createRole,
	}
	permissionDelete := a.RoleMap{
		authproviders.FiskerAD:     deleteRole,
		authproviders.FiskerAPIKey: deleteRole,
	}
	permissionSupplierAdmin := a.RoleMap{
		authproviders.FiskerAD:     supplierAdminRole,
		authproviders.FiskerAPIKey: supplierAdminRole,
	}
	permissionManufacture := a.RoleMap{
		authproviders.FiskerAPIKey: {a.RoleManufacture},
	}
	permissionMigrate := a.RoleMap{
		authproviders.FiskerAD:     {a.RoleManifestMigration},
		authproviders.FiskerAPIKey: {a.RoleManifestMigration},
	}

	permissionUpdateDeploy := a.RoleMap{
		authproviders.FiskerAD:     updateDeployRole,
		authproviders.FiskerAPIKey: updateDeployRole,
	}

	// Change it to Magna as soon as you get groupIDs.
	magnaAccess := a.RoleMap{
		authproviders.Magna:    []a.RoleID{a.RoleMagna},
		authproviders.FiskerQA: []a.RoleID{a.RoleManufacture},
	}

	carDiagnostic := a.RoleMap{
		authproviders.Default:      []a.RoleID{a.RoleCarDiagnostic},
		authproviders.FiskerAPIKey: []a.RoleID{a.RoleCarDiagnostic},
	}

	addHandler(router, nil, http.MethodGet, "/docs/*path", swagger)

	// ### APICALL ###
	addHandler(router, permissionRead, http.MethodGet, "/apicalls", handlers.HandleAPICallsGet)

	// ### APITOKEN ###
	addHandler(router, permissionRead, http.MethodGet, "/apitokens", handlers.HandleAPITokensGetList)
	addHandler(router, permissionCreate, http.MethodPost, "/apitoken", handlers.HandleAPITokenAdd)
	addHandler(router, permissionDelete, http.MethodDelete, "/apitoken", handlers.HandleAPITokenDelete)
	addHandler(router, permissionCreate, http.MethodPut, "/apitoken", handlers.HandleAPITokenUpdate)

	// ### CANSIGNAL ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/cansignals/:vin", handlers.HandleVehiclesSignals)
	addHandler(router, permissionRead, http.MethodGet, "/can_signals_export", handlers.HandleCanSignalVINGet)
	addHandler(router, permissionRead, http.MethodGet, "/can_signals_list", handlers.HandleCanSignalListGet)
	addHandler(router, permissionRead, http.MethodGet, "/can_signals/:dbc", handlers.HandleDBCSignalsGetList)

	// ### CARCONFIG ###
	addHandler(router, permissionCreate, http.MethodGet, "/car_config/:vin", handlers.GetCarConfiguration)
	addHandler(router, permissionUpdateDeploy, http.MethodPost, "/car_config/:vin", handlers.CarConfigurationUpdate)

	// ### CARSCONNECTED ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodPost, "/carsconnected", handlers.HandleVehicleConnectionStatuses)

	// ### CARSLOCATION ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/carslocations", handlers.HandleVehiclesLocations)

	// ### CARSTATE ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/carstate", handlers.HandleVehicleState)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodPost, "/carstate_multi", handlers.HandleVehicleStateMulti)

	// ### CARUPDATE ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/carupdates", handlers.HandleCarUpdatesGet)
	addHandler(router, permissionUpdateDeploy.CopyAndMerge(magnaAccess), http.MethodPost, "/carupdate", handlers.HandleCarUpdatesAdd)
	addHandler(router, permissionDelete, http.MethodDelete, "/carupdate", handlers.HandleCarUpdateDelete)
	addHandler(router, permissionCreate.CopyAndMerge(magnaAccess), http.MethodPost, "/carupdate/:id/cancel", handlers.HandleCarUpdateCancel)
	addHandler(router, carDiagnostic, http.MethodPost, "/carupdate/:id/vehicle-cancel", handlers.HandleCarUpdateVehicleCancel)
	addHandler(router, permissionUpdateDeploy.CopyAndMerge(magnaAccess), http.MethodPost, "/carupdate/:id/deploy", handlers.HandleCarUpdateDeploy)

	// ### CARUPDATESLOG ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/carupdateslog", handlers.HandleCarUpdatesLog)

	// ### CARUPDATESTATUS ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/carupdatesstatuses", handlers.HandleCarUpdatesStatuses)

	// ### DASHBOARD ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/dashboard/guest-token", handlers.HandleDashboardToken)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/dashboard/embedded-dashboards", handlers.HandleSupersetEmbeddedDashboardsGet)

	// ### DITTO ###
	addHandler(router, permissionRead, http.MethodGet, "/ditto/carstate", handlers.HandleDigitalTwinSignal)

	// ### DTC ###
	addHandler(router, permissionRead, http.MethodGet, "/dtcs/:vin", handlers.HandleECUDTCGet)

	// ### ECUSTATS ###
	addHandler(router, permissionRead, http.MethodGet, "/ecu_stats", handlers.HandleECUStatsGetList)
	addHandler(router, permissionRead, http.MethodGet, "/ecu_stats/:vin/:dbc", handlers.HandleVINECUStatsGetList)

	// ### FLEET ###
	addHandler(router, permissionRead, http.MethodGet, "/fleets", handlers.HandleFleetGetList)
	addHandler(router, permissionCreate, http.MethodPost, "/fleet", handlers.HandleFleetAdd)
	addHandler(router, permissionRead, http.MethodGet, "/fleet/:name", handlers.HandleFleetGet)
	addHandler(router, permissionCreate, http.MethodPut, "/fleet/:name", handlers.HandleFleetUpdate)
	addHandler(router, permissionDelete, http.MethodDelete, "/fleet/:name", handlers.HandleFleetDelete)
	addHandler(router, permissionRead, http.MethodGet, "/fleet/:name/filters", handlers.HandleFleetFilterGetList)
	addHandler(router, permissionCreate, http.MethodPost, "/fleet/:name/filter", handlers.HandleFleetFilterAdd)
	addHandler(router, permissionCreate, http.MethodPut, "/fleet/:name/filter/:id", handlers.HandleFleetFilterUpdate)
	addHandler(router, permissionDelete, http.MethodDelete, "/fleet/:name/filter/:id", handlers.HandleFleetFilterDelete)
	addHandler(router, permissionRead, http.MethodGet, "/fleet/:name/vehicles", handlers.HandleFleetVehicleGetList)
	addHandler(router, permissionRead, http.MethodPost, "/fleet/:name/vehicles/add", handlers.HandleFleetVehicleAdd)
	addHandler(router, permissionRead, http.MethodPost, "/fleet/:name/vehicles/delete", handlers.HandleFleetVehicleDelete)

	// ### FLEETUPDATE ###
	addHandler(router, permissionCreate, http.MethodPost, "/fleetupdate", handlers.HandleFleetUpdatesAdd)

	// ### ISSUE ###
	addHandler(router, permissionRead, http.MethodGet, "/issues", handlers.HandleIssuesGet)
	addHandler(router, permissionRead, http.MethodGet, "/issues/:id", handlers.HandleIssueGet)
	addHandler(router, permissionDelete, http.MethodDelete, "/issues/:id", handlers.HandleIssuesDelete)

	// ### MANIFEST ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/manifests", handlers.HandleUpdateManifestsGet)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/manifest", handlers.HandleUpdateManifestGet)
	addHandler(router, permissionCreate, http.MethodPost, "/manifest", handlers.HandleUpdateManifestAdd)
	addHandler(router, permissionCreate, http.MethodPut, "/manifest", handlers.HandleUpdateManifestUpdate)
	addHandler(router, permissionDelete, http.MethodDelete, "/manifest", handlers.HandleUpdateManifestDelete)
	addHandler(router, permissionCreate.CopyAndMerge(magnaAccess), http.MethodPut, "/manifests/:id/sums", handlers.HandleUpdateManifestSUMSUpdate)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/manifests/:manifest_id/vehicles", handlers.HandleGetCarsByManifest)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/manifest/sums", handlers.HandleUpdateManifestSUMSGet)
	addHandler(router, permissionCreate.CopyAndMerge(magnaAccess), http.MethodPost, "/manifest/sums", handlers.HandleUpdateManifestSUMSAdd)
	addHandler(router, permissionDelete.CopyAndMerge(magnaAccess), http.MethodDelete, "/manifest/sums/:version", handlers.HandleUpdateManifestSUMSDelete)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/manifest/sums/:version/rxswins", handlers.HandleUpdateManifestSUMSRxSwinsGet)
	addHandler(router, permissionCreate.CopyAndMerge(magnaAccess), http.MethodPost, "/manifest/sums/:version/rxswins", handlers.HandleUpdateManifestSUMSRxSwinsAdd)
	addHandler(router, permissionDelete.CopyAndMerge(magnaAccess), http.MethodDelete, "/manifest/sums/:version/rxswins/:rxswin", handlers.HandleUpdateManifestSUMSRxSwinsDelete)

	// ### MANIFESTECU ###
	addHandler(router, permissionCreate, http.MethodPost, "/manifestecu", handlers.HandleUpdateManifestECUAdd)

	// ### MANIFESTFILE ###
	addHandler(router, permissionCreate, http.MethodPost, "/manifestfile", handlers.HandleUpdateManifestFileAdd)

	// ### MANIFESTMIGRATE ###
	// Need a special permission
	// Like HandleUpdateManifestAdd, but receives a manifest itself instead of the CreateUpdateManifest object
	addHandler(router, permissionMigrate, http.MethodPost, "/manifestmigrate", handlers.HandleUpdateManifestMigrateReceive)
	// Sends the manifest to another API
	addHandler(router, permissionMigrate, http.MethodPost, "/manifestmigrate/:manifest_id", handlers.HandleUpdateManifestMigrate)
	addHandler(router, permissionMigrate, http.MethodGet, "/manifestmigrate-version", handlers.HandleUpdateManifestMigrateVersion)

	// ### MANUFACTURECERT ###
	addHandler(router, permissionManufacture.CopyAndMerge(magnaAccess), http.MethodPost, "/manufacture-certs", handlers.HandleGetDLLManufactureCerts)

	// ### SMS ###
	addHandler(router, permissionCreate, http.MethodPost, "/sms", handlers.HandleSMSSend)

	// ### SUBSCRIPTION ###
	addHandler(router, permissionDelete, http.MethodDelete, "/subscription", handlers.HandleSubscriptionDelete)

	// ### SUBSCRIPTIONCONFIG ###
	addHandler(router, permissionRead, http.MethodGet, "/subscriptionconfigs", handlers.HandleSubscriptionConfigsGetList)
	addHandler(router, permissionCreate, http.MethodPost, "/subscriptionconfig", handlers.HandleSubscriptionConfigAdd)
	addHandler(router, permissionDelete, http.MethodDelete, "/subscriptionconfig", handlers.HandleSubscriptionConfigDelete)
	addHandler(router, permissionCreate, http.MethodPut, "/subscriptionconfig", handlers.HandleSubscriptionConfigUpdate)

	// ### SUBSCRIPTIONFEATURE ###
	addHandler(router, permissionRead, http.MethodGet, "/subscriptionfeatures", handlers.HandleSubscriptionFeaturesGet)
	addHandler(router, permissionCreate, http.MethodPost, "/subscriptionfeature", handlers.HandleSubscriptionFeatureAdd)
	addHandler(router, permissionDelete, http.MethodDelete, "/subscriptionfeature", handlers.HandleSubscriptionFeatureDelete)
	addHandler(router, permissionCreate, http.MethodPut, "/subscriptionfeature", handlers.HandleSubscriptionFeatureUpdate)
	addHandler(router, permissionRead, http.MethodGet, "/subscriptionfeature", handlers.HandleSubscriptionFeatureGet)

	// ### SUBSCRIPTIONPACKAGE ###
	addHandler(router, permissionRead, http.MethodGet, "/subscriptionpackages", handlers.HandleSubscriptionPackagesGetList)
	addHandler(router, permissionCreate, http.MethodPost, "/subscriptionpackage", handlers.HandleSubscriptionPackageAdd)
	addHandler(router, permissionDelete, http.MethodDelete, "/subscriptionpackage", handlers.HandleSubscriptionPackageDelete)
	addHandler(router, permissionCreate, http.MethodPut, "/subscriptionpackage", handlers.HandleSubscriptionPackageUpdate)
	addHandler(router, permissionRead, http.MethodGet, "/subscriptionpackage", handlers.HandleSubscriptionPackageGet)

	// ### SUBSCRIPTIONPACKAGEFEATURE ###
	addHandler(router, permissionCreate, http.MethodPost, "/subscriptionpackagefeature", handlers.HandleSubscriptionFeatureAssign)

	// ### SUPPLIER ###
	addHandler(router, permissionSupplierAdmin, http.MethodGet, "/suppliers", handlers.HandleSuppliersGetList)
	addHandler(router, permissionSupplierAdmin, http.MethodPost, "/supplier/activate/:email", handlers.HandleSupplierActivate)
	addHandler(router, permissionSupplierAdmin, http.MethodPut, "/supplier/:email", handlers.HandleSupplierUpdate)
	addHandler(router, permissionSupplierAdmin, http.MethodDelete, "/supplier/:email", handlers.HandleSupplierDelete)

	// ### TAG ###
	addHandler(router, permissionCreate, http.MethodPut, "/tags", handlers.HandleTagsUpdate)
	addHandler(router, permissionCreate, http.MethodPost, "/tags", handlers.HandleTagsAppend)

	// ### VEHICLE ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/vehicles", handlers.HandleVehiclesGet)
	addHandler(router, permissionCreate, http.MethodPut, "/vehicles/archive", handlers.HandleUpdateManifestsArchive)
	addHandler(router, permissionCreate, http.MethodPost, "/vehicle", handlers.HandleVehicleAdd)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/vehicle/:vin", handlers.HandleVehicleGet)
	addHandler(router, permissionCreate.CopyAndMerge(magnaAccess), http.MethodPut, "/vehicle/:vin", handlers.HandleVehicleUpdate)
	addHandler(router, permissionDelete, http.MethodDelete, "/vehicle/:vin", handlers.HandleVehicleDeleteHoneyPot)
	addHandler(router, permissionRead, http.MethodGet, "/vehicle/:vin/filters", handlers.HandleVehicleFilterGetList)
	addHandler(router, permissionCreate, http.MethodPost, "/vehicle/:vin/filter", handlers.HandleVehicleFilterAdd)
	addHandler(router, permissionCreate, http.MethodPut, "/vehicle/:vin/filter/:id", handlers.HandleVehicleFilterUpdate)
	addHandler(router, permissionDelete, http.MethodDelete, "/vehicle/:vin/filter/:id", handlers.HandleVehicleFilterDelete)
	addHandler(router, permissionRead, http.MethodGet, "/vehicle/:vin/fleets", handlers.HandleVehicleFleetGetList)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/vehicle/:vin/trex-logs", handlers.HandleTrexLogsGet)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/vehicle/:vin/trex-logs-link", handlers.HandleTrexLogsLinkGet)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/vehicle/:vin/version", handlers.HandleVersionsGet)
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/vehicle/:vin/version/logs", handlers.HandleVersionLogsGet)

	// ### VEHICLECOMMAND ###
	addHandler(router, permissionCreate.CopyAndMerge(magnaAccess), http.MethodPost, "/vehiclecommand", handlers.HandleVehicleCommand)
	// immobilizer routes
	addHandler(router, permissionCreate, http.MethodGet, "/vehiclecommand/immobilize", handlers.HandleVehicleImmobilizerList)
	addHandler(router, permissionCreate, http.MethodPost, "/vehiclecommand/immobilize", handlers.HandleVehicleImmobilizerAdd)
	addHandler(router, permissionCreate, http.MethodDelete, "/vehiclecommand/immobilize", handlers.HandleVehicleImmobilizerDelete)

	// ### VEHICLEDIAGNOSTICCOMMAND ###
	addHandler(router, carDiagnostic, http.MethodPost, "/vehiclediagnosticcommand", handlers.HandleVehicleDiagnosticCommand)

	// ### VEHICLEECU ###
	addHandler(router, permissionRead.CopyAndMerge(magnaAccess), http.MethodGet, "/vehicleecus", handlers.HandleVehicleECUsGet)

	// ### VEHICLEMODEL ###
	addHandler(router, permissionRead, http.MethodGet, "/vehiclemodels", handlers.HandleVehicleModels)

	// ### FLASHPACK TO ECU VERSION MAPPINGS ###
	addHandler(router, permissionRead, http.MethodGet, "/flashpack_version_ecu_mappings/:model/:trim/:year/:flashpack", handlers.HandleFlashpackVersionECUMappingsGet)
	addHandler(router, permissionRead, http.MethodGet, "/flashpack_versions/:model/:trim/:year", handlers.HandleFlashpackVersionsGetAll)
	addHandler(router, permissionCreate, http.MethodPost, "/flashpack_version", handlers.HandleFlashpackVersionAdd)
	addHandler(router, permissionDelete, http.MethodDelete, "/flashpack_version", handlers.HandleFlashpackVersionDelete)
	addHandler(router, permissionRead, http.MethodGet, "/flashpack_version_info/:vin", handlers.HandleFlashpackVersionInfoGet)

	// ### VEHICLEPATH ###
	addHandler(router, permissionRead, http.MethodPost, "/vehicle_paths", handlers.HandleVehiclePathsPost)

	// ### VEHICLEYEAR ###
	addHandler(router, permissionRead, http.MethodGet, "/vehicleyears", handlers.HandleVehicleYears)

	// ### CUSTOMER EMAILS ###
	addHandler(router, permissionRead, http.MethodPut, "/customer_ota_emails", handlers.HandleCustomerOtaEmails)

	// ### OVLoop Data Sources ###
	addHandler(router, permissionRead, http.MethodGet, "/cars/allowed_access", handlers.HandleCarsAllowedAccess)
	addHandler(router, permissionRead, http.MethodPost, "/cars/allowed_access", handlers.HandleCarAllowedAccess)
	addHandler(router, permissionCreate, http.MethodPost, "/cars/change_access", handlers.HandleCarChangeAccess)
	addHandler(router, permissionCreate, http.MethodGet, "/cars/hmi_key", handlers.HandleGetCarsHMIKey)
	addHandler(router, permissionCreate, http.MethodPost, "/drivers/add_external", handlers.HandleVehicleExternalDriverAdd)
	addHandler(router, permissionCreate, http.MethodDelete, "/drivers/delete_external", handlers.HandleExternalDriverDelete)
	addHandler(router, permissionRead, http.MethodGet, "/car/software_information", handlers.HandleCarSoftwareInformation)
	addHandler(router, permissionRead, http.MethodGet, "/car/software_information/v2", handlers.HandleCarSoftwareInformationV2)
	addHandler(router, permissionRead, http.MethodPost, "/car/wake", handlers.HandleSendWakeSMSToVIN)

	// Experimental text feature
	addHandler(router, nil, http.MethodGet, "/expirment", handlers.HandleExperiment)

	go controllers.HealthCheck()

	logger.Info().Msgf("Listening on http://0.0.0.0%s", port)
	logger.Fatal().Err(http.ListenAndServe(port, router)).Send()
}

func addHandler(router *httprouter.Router, roles a.RoleMap, method string, path string, handler http.HandlerFunc) {
	router.HandlerFunc(method, h.HttpRouterHandleBaseURL(path), authChecker.GetHandler(roles, h.LogRequest(handler)))
}

func cleanup() {
	authChecker.Close()
	services.GetDB().Close()
	logger.Close()
}
