// Concorde relationships with the same platformVersion - if any
func TestConcordeOrgsWithRelationshipPlatformVersionTransfer(t *testing.T) {
	assert := assert.New(t)

	db := getDatabaseConnectionAndCheckClean(t, assert, concordedUUIDs)
	cypherDriver := getCypherDriver(db)

	annotationsRW := annotations.NewCypherAnnotationsService(cypherDriver.conn, "v1")

	defer cleanDB(db, t, assert, concordedUUIDs)
	defer deleteAllViaService(db, assert, annotationsRW)

	assert.NoError(cypherDriver.Write(org2))

	relOrg2L, relOrg2R, err := getNodeRelationshipNames(cypherDriver.conn, org2UUID)
	assert.Nil(err)
	relOrg1L, relOrg1R, err := getNodeRelationshipNames(cypherDriver.conn, org1UUID)
	assert.Empty(relOrg1L)
	assert.Empty(relOrg1R)
	assert.Nil(err)

	updatedOrg1 := organisation{
		UUID: org1UUID,
		Type: Organisation,
		AlternativeIdentifiers: alternativeIdentifiers{
			FactsetIdentifier: fsOrg8Identifier,
			UUIDS:             []string{org1UUID, org2UUID},
			TME:               []string{},
		},
		ProperName:         "Updated Name",
		ParentOrganisation: org8UUID,
	}

	writeJSONToService(annotationsRW, "./test-resources/annotationBodyForOrg2.json", contentUUID, assert)
	assert.NoError(cypherDriver.Write(updatedOrg1))

	relUpdatedOrg1L, relUpdatedOrg1R, err := getNodeRelationshipNames(cypherDriver.conn, org1UUID)
	assert.Nil(err)
	for _, rel := range relOrg2L {
		contains(relUpdatedOrg1L, rel.RelationshipType)
	}
	for _, rel := range relOrg2R {
		contains(relUpdatedOrg1R, rel.RelationshipType)
	}

	storedOrg2, _, _ := cypherDriver.Read(org2UUID)
	storedOrg1, _, _ := cypherDriver.Read(org1UUID)

	assert.Equal(organisation{}, storedOrg2, "org should have been deleted")
	assert.Equal(updatedOrg1, storedOrg1, "org should have been updated")

	transferredPropertyLR, transferredPropertyRL, err := readRelationshipDetails(cypherDriver.conn, "Thing", org1UUID)
	assert.Nil(err)
	assert.Equal(2, len(transferredPropertyLR))
	assert.Contains(transferredPropertyLR, property{Type: "MENTIONS", PlatformVersion: "v1"})
	assert.Contains(transferredPropertyLR, property{Type: "ABOUT", PlatformVersion: "v1"})
	assert.Equal(1, len(transferredPropertyRL))
	assert.Contains(transferredPropertyRL, property{Type: "SUB_ORGANISATION_OF", PlatformVersion: ""})
}
func main() {

	app := cli.App("annotations-rw-neo4j", "A RESTful API for managing Annotations in neo4j")
	neoURL := app.String(cli.StringOpt{
		Name:   "neo-url",
		Value:  "http://localhost:7474/db/data",
		Desc:   "neo4j endpoint URL",
		EnvVar: "NEO_URL",
	})
	graphiteTCPAddress := app.String(cli.StringOpt{
		Name:   "graphiteTCPAddress",
		Value:  "",
		Desc:   "Graphite TCP address, e.g. graphite.ft.com:2003. Leave as default if you do NOT want to output to graphite (e.g. if running locally",
		EnvVar: "GRAPHITE_ADDRESS",
	})
	graphitePrefix := app.String(cli.StringOpt{
		Name:   "graphitePrefix",
		Value:  "",
		Desc:   "Prefix to use. Should start with content, include the environment, and the host name. e.g. coco.pre-prod.roles-rw-neo4j.1 or content.test.people.rw.neo4j.ftaps58938-law1a-eu-t",
		EnvVar: "GRAPHITE_PREFIX",
	})
	port := app.Int(cli.IntOpt{
		Name:   "port",
		Value:  8080,
		Desc:   "Port to listen on",
		EnvVar: "APP_PORT",
	})
	batchSize := app.Int(cli.IntOpt{
		Name:   "batchSize",
		Value:  1024,
		Desc:   "Maximum number of statements to execute per batch",
		EnvVar: "BATCH_SIZE",
	})
	logMetrics := app.Bool(cli.BoolOpt{
		Name:   "logMetrics",
		Value:  false,
		Desc:   "Whether to log metrics. Set to true if running locally and you want metrics output",
		EnvVar: "LOG_METRICS",
	})
	logLevel := app.String(cli.StringOpt{
		Name:   "log-level",
		Value:  "INFO",
		Desc:   "Logging level (DEBUG, INFO, WARN, ERROR)",
		EnvVar: "LOG_LEVEL",
	})
	platformVersion := app.String(cli.StringOpt{
		Name:   "platformVersion",
		Value:  "",
		Desc:   "Annotation source platform. Possible values are: v1 or v2.",
		EnvVar: "PLATFORM_VERSION",
	})

	app.Action = func() {
		parsedLogLevel, err := log.ParseLevel(*logLevel)
		if err != nil {
			log.WithFields(log.Fields{"logLevel": logLevel, "err": err}).Fatal("Incorrect log level")
		}
		log.SetLevel(parsedLogLevel)

		log.Infof("annotations-rw-neo4j will listen on port: %d, connecting to: %s", *port, *neoURL)

		conf := neoutils.DefaultConnectionConfig()
		conf.BatchSize = *batchSize
		db, err := neoutils.Connect(*neoURL, conf)

		if err != nil {
			log.Fatalf("Error connecting to neo4j %s", err)
		}

		annotationsService := annotations.NewCypherAnnotationsService(db, *platformVersion)
		httpHandlers := httpHandlers{annotationsService}

		// Healthchecks and standards first
		http.HandleFunc("/__health", v1a.Handler("Annotations RW Healthchecks",
			"Checks for accessing neo4j", httpHandlers.HealthCheck()))
		http.HandleFunc(status.PingPath, status.PingHandler)
		http.HandleFunc(status.PingPathDW, status.PingHandler)
		http.HandleFunc(status.BuildInfoPath, status.BuildInfoHandler)
		http.HandleFunc(status.BuildInfoPathDW, status.BuildInfoHandler)

		gtgChecker := make([]gtg.StatusChecker, 0)
		gtgChecker = append(gtgChecker, func() gtg.Status {
			if err := httpHandlers.AnnotationsService.Check(); err != nil {
				return gtg.Status{GoodToGo: false, Message: err.Error()}
			}

			return gtg.Status{GoodToGo: true}
		})
		http.HandleFunc(status.GTGPath, status.NewGoodToGoHandler(gtg.FailFastParallelCheck(gtgChecker)))

		r := router(httpHandlers)
		http.Handle("/", r)
		baseftrwapp.OutputMetricsIfRequired(*graphiteTCPAddress, *graphitePrefix, *logMetrics)

		if err := http.ListenAndServe(fmt.Sprintf(":%d", *port), nil); err != nil {
			log.Fatalf("Unable to start server: %v", err)
		}

	}
	log.Infof("Application started with args %s", os.Args)
	app.Run(os.Args)
}
// concorde node with multiple major mentions (mentions with platformVersion v1)
func TestConcordeOrganisationsWithRelationships(t *testing.T) {
	assert := assert.New(t)
	db := getDatabaseConnectionAndCheckClean(t, assert, concordedUUIDs)
	cypherDriver := getCypherDriver(db)

	defer cleanDB(db, t, assert, concordedUUIDs)

	// STEP 1: write nodes
	assert.NoError(cypherDriver.Write(org1))
	assert.NoError(cypherDriver.Write(org2))
	assert.NoError(cypherDriver.Write(org9))
	assert.NoError(cypherDriver.Write(org8))

	_, found, _ := cypherDriver.Read(org1UUID)
	assert.True(found, "Didn't find organisation for uuid %s", org1UUID)
	_, found, _ = cypherDriver.Read(org2UUID)
	assert.True(found, "Didn't find organisation for uuid %s", org2UUID)
	_, found, _ = cypherDriver.Read(org9UUID)
	assert.True(found, "Didn't find organisation for uuid %s", org9UUID)
	_, found, _ = cypherDriver.Read(org8UUID)
	assert.True(found, "Didn't find organisation for uuid %s", org8UUID)

	//STEP 2: write relationships

	//write V2 mentions, and about annotation for org2UUID, write V2 mentions annotation for org8UUID
	v2AnnotationsRW := annotations.NewCypherAnnotationsService(cypherDriver.conn, "v2")
	writeJSONToService(v2AnnotationsRW, "./test-resources/annotationBodyForOrg2AndOrg8.json", contentUUID, assert)

	//write V1 mentions annotation for org1UUID and org9UUID - considered as major mentions
	v1AnnotationsRW := annotations.NewCypherAnnotationsService(cypherDriver.conn, "v1")
	writeJSONToService(v1AnnotationsRW, "./test-resources/annotationBodyForOrg1AndOrg9.json", contentUUID, assert)

	//STEP3: concorde org1, with org2 and org9
	updatedOrg1 := organisation{
		UUID: org1UUID,
		Type: Organisation,
		AlternativeIdentifiers: alternativeIdentifiers{
			FactsetIdentifier: fsOrg1Identifier,
			UUIDS:             []string{org1UUID, org2UUID, org9UUID},
			LeiCode:           leiCodeOrgxIdentifier,
			TME:               []string{tmeOrg2Identifier, tmeOrg9Identifier},
		},
		// should come out from the transformer like this, otherwise won't be merged
		ProperName:         "Updated Name",
		ParentOrganisation: org8UUID, // should come out from the transformer - otherwise won't be transferred
	}

	assert.NoError(cypherDriver.Write(updatedOrg1))

	//RESULTS concording should result in:

	// - the presence of node 1 and 8, absence of node 2, 9
	_, found, _ = cypherDriver.Read(org1UUID)
	assert.True(found, "Didn't find organisation for uuid %s", org1UUID)
	_, found, _ = cypherDriver.Read(org8UUID)
	assert.True(found, "Didn't find organisation for uuid %s", org8UUID)
	_, found, _ = cypherDriver.Read(org2UUID)
	assert.False(found, "Didn't find organisation for uuid %s", org2UUID)
	_, found, _ = cypherDriver.Read(org9UUID)
	assert.False(found, "Didn't find organisation for uuid %s", org9UUID)

	//- for org 8:
	//	 - one v2 mentions from content - which existed
	//	 - one SUB_ORGANISATION_OF to org8 from org2
	//	 - 4 IDENTIFIES relationships from identifiers to nodes
	transferredPropertyLR, transferredPropertyRL, err := readRelationshipDetails(cypherDriver.conn, "Thing", org8UUID)
	assert.Nil(err)
	assert.Equal(0, len(transferredPropertyRL))
	assert.Equal(2, len(transferredPropertyLR))
	assert.Contains(transferredPropertyLR, property{Type: "MENTIONS", PlatformVersion: "v2"})
	assert.Contains(transferredPropertyLR, property{Type: "SUB_ORGANISATION_OF", PlatformVersion: ""})

	transferredPropertyLR, transferredPropertyRL, err = readRelationshipDetails(cypherDriver.conn, "Identifier", org8UUID)
	assert.Nil(err)
	assert.Equal(0, len(transferredPropertyRL))
	assert.Equal(4, len(transferredPropertyLR))
	for _, rel := range transferredPropertyLR {
		assert.Equal("IDENTIFIES", rel.Type)
		assert.Equal("", rel.PlatformVersion)
	}

	// - for org 1:
	//	 - one v2 mentions from content
	//	 - one v1 mentions from content (two merged in one, with properties from the randomly selected relationship)
	//	 - one v2 about from content
	//	 - one SUB_ORGANISATION_OF to org8
	//	 - 7 IDENTIFIES relationships from identifiers to node
	transferredPropertyLR, transferredPropertyRL, err = readRelationshipDetails(cypherDriver.conn, "Thing", org1UUID)
	assert.Nil(err)
	assert.Equal(3, len(transferredPropertyLR))
	assert.Contains(transferredPropertyLR, property{Type: "MENTIONS", PlatformVersion: "v2"})
	assert.Contains(transferredPropertyLR, property{Type: "MENTIONS", PlatformVersion: "v1"})
	assert.Contains(transferredPropertyLR, property{Type: "ABOUT", PlatformVersion: "v2"})
	assert.Equal(1, len(transferredPropertyRL))
	assert.Contains(transferredPropertyRL, property{Type: "SUB_ORGANISATION_OF", PlatformVersion: ""})

	transferredPropertyLR, transferredPropertyRL, err = readRelationshipDetails(cypherDriver.conn, "Identifier", org1UUID)
	assert.Nil(err)
	assert.Equal(0, len(transferredPropertyRL))
	assert.Equal(7, len(transferredPropertyLR))
	assert.Contains(transferredPropertyLR, property{Type: "IDENTIFIES", PlatformVersion: ""})
	for _, rel := range transferredPropertyLR {
		assert.Equal("IDENTIFIES", rel.Type)
		assert.Equal("", rel.PlatformVersion)
	}
}