/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package apoc.export;

import static apoc.ApocConfig.APOC_EXPORT_FILE_ENABLED;
import static apoc.ApocConfig.APOC_IMPORT_FILE_ENABLED;
import static apoc.ApocConfig.apocConfig;
import static apoc.util.TransactionTestUtil.checkTerminationGuard;

import apoc.export.csv.ExportCSV;
import apoc.export.csv.ImportCsv;
import apoc.export.cypher.ExportCypher;
import apoc.export.graphml.ExportGraphML;
import apoc.export.json.ExportJson;
import apoc.export.json.ImportJson;
import apoc.graph.Graphs;
import apoc.meta.Meta;
import apoc.refactor.GraphRefactoring;
import apoc.refactor.rename.Rename;
import apoc.util.TestUtil;
import apoc.util.Util;
import com.neo4j.test.extension.EnterpriseDbmsExtension;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.neo4j.configuration.GraphDatabaseSettings;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.test.TestDatabaseManagementServiceBuilder;
import org.neo4j.test.extension.ExtensionCallback;
import org.neo4j.test.extension.Inject;

@EnterpriseDbmsExtension(configurationCallback = "configure", createDatabasePerTest = false)
public class BigGraphTest {
    private static final File directory = new File("target/import");

    static { //noinspection ResultOfMethodCallIgnored
        directory.mkdirs();
    }

    @Inject
    GraphDatabaseService db;

    @ExtensionCallback
    void configure(TestDatabaseManagementServiceBuilder builder) {
        builder.setConfig(GraphDatabaseSettings.memory_tracking, true)
                .setConfig(
                        GraphDatabaseSettings.load_csv_file_url_root,
                        directory.toPath().toAbsolutePath());
    }

    @BeforeAll
    void setUp() {
        TestUtil.registerProcedure(
                db,
                Rename.class,
                ExportCSV.class,
                ExportJson.class,
                ExportCypher.class,
                ExportGraphML.class,
                Graphs.class,
                Meta.class,
                GraphRefactoring.class,
                ImportCsv.class,
                ImportJson.class);
        apocConfig().setProperty(APOC_IMPORT_FILE_ENABLED, true);
        apocConfig().setProperty(APOC_EXPORT_FILE_ENABLED, true);

        final String query = Util.readResourceFile("moviesMod.cypher");
        IntStream.range(0, 10000).forEach(__ -> db.executeTransactionally(query));
    }

    @Test
    public void testTerminateExportCsv() {
        checkTerminationGuard(db, "CALL apoc.export.csv.all('testTerminate.csv',{bulkImport: true})");
    }

    @Test
    public void testTerminateExportGraphMl() {
        checkTerminationGuard(db, "CALL apoc.export.graphml.all('testTerminate.graphml', {})");
    }

    @Test
    public void testTerminateExportCypher() {
        checkTerminationGuard(db, "CALL apoc.export.cypher.all('testTerminate.cypher',{})");
    }

    @Test
    public void testTerminateExportJson() {
        checkTerminationGuard(db, "CALL apoc.export.json.all('testTerminate.json',{})");
    }

    @Test
    public void testTerminateRenameNodeProp() {
        checkTerminationGuard(db, "CALL apoc.refactor.rename.nodeProperty('name', 'nameTwo')");
    }

    @Test
    public void testTerminateRenameType() {
        checkTerminationGuard(db, 50L, "CALL apoc.refactor.rename.type('DIRECTED', 'DIRECTED_TWO')");
    }

    @Test
    public void testTerminateRefactorProcs() {
        List<Node> nodes = db.executeTransactionally(
                "MATCH (n:Person) RETURN collect(n) as nodes",
                Collections.emptyMap(),
                r -> r.<List<Node>>columnAs("nodes").next());

        checkTerminationGuard(db, "CALL apoc.refactor.cloneNodes($nodes)", Map.of("nodes", nodes));

        checkTerminationGuard(db, "CALL apoc.refactor.cloneSubgraph($nodes)", Map.of("nodes", nodes));
    }
}
