class ShardImporter {

    public void importShard(ShardPresentData shardPresentData, List<String> excludedTables) {
        Objects.requireNonNull(shardPresentData, "shardPresentData is NULL");
        Objects.requireNonNull(excludedTables, "excludedTables is NULL");
        // shard archive data has been downloaded at that point and stored (unpacked?) in configured folder
        String genesisTaskId = aplAppStatus.durableTaskStart("Shard data import", "Loading Genesis public accounts", true);
        log.debug("genesisTaskId = {}", genesisTaskId);
        log.debug("Received shardPresentData = '{}', lets map all data to Location(s)...", shardPresentData);

        // try to unzip data and throw ShardArchiveProcessingException in any kind of error or zip inconsistency
        Path zipInFolder = unzipMainOptionalArchives(shardPresentData, genesisTaskId);
        if (zipInFolder == null) {
            // ShardArchiveProcessingException was thrown before due to incorrect Zip processing
            return;
        }

        genesisImporter.importGenesisJson(true); // import genesis public Keys ONLY (NO balances) - 049,842%
        aplAppStatus.durableTaskUpdate(genesisTaskId, 50.0, "Public keys were imported");
        // import additional tables
        List<String> tables = List.of(ShardConstants.SHARD_TABLE_NAME,
            ShardConstants.BLOCK_TABLE_NAME, ShardConstants.TRANSACTION_TABLE_NAME,
            ShardConstants.TRANSACTION_INDEX_TABLE_NAME, ShardConstants.BLOCK_INDEX_TABLE_NAME);
        log.debug("1. Will be imported [{}] tables...", tables.size());
        for (String table : tables) {
            if (excludedTables.contains(table)) {
                log.warn("Skip import {}", table);
                continue;
            }
            try {
                log.debug("start importing '{}'...", table);
                aplAppStatus.durableTaskUpdate(genesisTaskId, "Loading '" + table + "'", 0.6);
                long rowsImported = csvImporter.importCsv(table, ShardConstants.DEFAULT_COMMIT_BATCH_SIZE, true);
                log.debug("Imported '{}' rows = {}", table, rowsImported);
            } catch (Exception e) {
                log.error("CSV import error for '{}', RETURN.......", table, e);
                aplAppStatus.durableTaskFinished(genesisTaskId, true, "Shard data import");
                throw new RuntimeException(e);
            }
        }
        Shard lastShard = shardDao.getLastShard();
        if (lastShard == null) {
            if (!excludedTables.contains(ShardConstants.SHARD_TABLE_NAME)) {
                aplAppStatus.durableTaskFinished(genesisTaskId, true, "Shard data import");
                throw new IllegalStateException("Unable to import shard without records in shard table");
            }
        } else {
            lastShard.setShardState(ShardState.CREATED_BY_ARCHIVE);
            ChunkedFileOps ops = new ChunkedFileOps(zipInFolder.toAbsolutePath().toString());
            lastShard.setCoreZipHash(ops.getFileHash());
            log.debug("Update shard info: coreZipHash={}", Convert.toHexString(lastShard.getCoreZipHash()));

            shardDao.updateShard(lastShard);
        }


        // import derived tables
        Collection<String> tableNames = derivedTablesRegistry.getDerivedTableNames();
        log.debug("2. Will be imported [{}] tables...", tables.size());
        for (String table : tableNames) {
            try {
                log.debug("start importing '{}'...", table);
                aplAppStatus.durableTaskUpdate(genesisTaskId, "Loading '" + table + "'", 0.6);
                long rowsImported;
                if (ShardConstants.ACCOUNT_TABLE_NAME.equalsIgnoreCase(table) || ShardConstants.ACCOUNT_ASSET_TABLE_NAME.equalsIgnoreCase(table) || ShardConstants.ACCOUNT_CURRENCY_TABLE_NAME.equalsIgnoreCase(table)) {
                    rowsImported = csvImporter.importCsvWithDefaultParams(table, 100, true,
                        Map.of("height", blockchain.findFirstBlock().getHeight()));
                } else if (ShardConstants.TAGGED_DATA_TABLE_NAME.equalsIgnoreCase(table)) {
                    rowsImported = csvImporter.importCsvWithRowHook(table, 100, true, (row) -> {
                        Object parsedTags = row.get("parsed_tags");
                        Object height = row.get("height");
                        if (parsedTags != null) {
                            Object[] tagArray = (Object[]) parsedTags;
                            dataTagDao.add(Arrays.copyOf(tagArray, tagArray.length, String[].class), Integer.parseInt((String) height));
                        }
                    });
                } else {
                    rowsImported = csvImporter.importCsv(table, 100, true);
                }
                log.debug("Imported '{}' rows = {}", table, rowsImported);
            } catch (Exception e) {
                log.error("CSV import error for '{}', RETURN.......", table, e);
                aplAppStatus.durableTaskFinished(genesisTaskId, true, "Shard data import");
                throw new RuntimeException(e);
            }
        }
        // remove all extracted *.csv files after successful importing from zip shard archive(s)
        log.debug("Start deleting imported CSV files from folder: {}", csvImporter.getDataExportPath());
        FileUtils.deleteFilesByPattern(csvImporter.getDataExportPath(), new String[]{"csv"}, null);
        aplAppStatus.durableTaskFinished(genesisTaskId, false, "Shard data import");
    }

    @Inject public  ShardImporter(ShardDao shardDao, BlockchainConfig blockchainConfig, GenesisImporter genesisImporter, Blockchain blockchain, DerivedTablesRegistry derivedTablesRegistry, CsvImporter csvImporter, Zip zipComponent, DataTagDao dataTagDao, DownloadableFilesManager downloadableFilesManager, AplAppStatus aplAppStatus);

}