package ru.nbch.indicsearchservice.exporter;

import jakarta.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import ru.nbch.indicsearchservice.config.settings.ApplicationSettings;
import ru.nbch.indicsearchservice.model.TableData;
import ru.nbch.indicsearchservice.model.TableMetaData;
import ru.nbch.indicsearchservice.model.enums.SearchParameter;
import tools.jackson.core.JsonGenerator;
import tools.jackson.databind.json.JsonMapper;

@Component
public class JsonExporter implements Exporter {
    private static final Logger log = LoggerFactory.getLogger(JsonExporter.class);

    private final ApplicationSettings settings;
    private final JsonMapper jsonMapper;

    public JsonExporter(ApplicationSettings settings,
                        JsonMapper jsonMapper) {
        this.settings = settings;
        this.jsonMapper = jsonMapper;
    }

    public void writeJsonFile(@Nullable Path aggregatedFile,
                              UUID taskId,
                              SearchParameter searchParameter,
                              Map<String, TableMetaData> tableMetaDataMap) {
        if (aggregatedFile == null) {
            return;
        }

        boolean isPassportOgrnFid = searchParameter == SearchParameter.PASSPORT ||
            searchParameter == SearchParameter.OGRN ||
            searchParameter == SearchParameter.FID;

        Path jsonFile = settings.getStorageDirectory().toPath().resolve(taskId.toString() + ".json");

        try (BufferedReader reader = Files.newBufferedReader(aggregatedFile);
             JsonGenerator jsonGenerator = jsonMapper.createGenerator(Files.newOutputStream(jsonFile))) {
            jsonGenerator.writeStartObject();
            jsonGenerator.writeName("datasheets");
            jsonGenerator.writeStartArray();

            Iterator<String> iterator = reader.lines().iterator();
            List<String> rows = new ArrayList<>();
            boolean isFirst = true;
            boolean isFirstArray = true;
            boolean isFileEmpty = true;

            AtomicReference<Integer> previousFid = new AtomicReference<>();
            while (iterator.hasNext()) {
                String line = iterator.next();

                if (line == null || line.trim().isEmpty()) {
                    continue;
                }

                List<String> lineList = Arrays.asList(line.split("%%%", -1));
                String tableName = lineList.getFirst();

                if (!isFirst && (FIRST_LEVEL_TABLES.contains(tableName) || line.equals("EOF"))) {
                    TableData parentData = buildTableDataHierarchy(rows, tableMetaDataMap);
                    if (parentData != null) {
                        if (isPassportOgrnFid) {
                            Object currentFidField = parentData.getFieldData().stream()
                                .filter(fd -> fd.columnName().equals("FID") && fd.columnValue() != null)
                                .findFirst()
                                .orElse(new TableData.FieldData("FID", null))
                                .columnValue();

                            if (previousFid.get() == null || (currentFidField != null && Integer.parseInt(currentFidField.toString()) != previousFid.get())) {
                                previousFid.set(Integer.parseInt(currentFidField.toString()));
                                if (!isFirstArray) {
                                    jsonGenerator.writeEndArray();
                                }
                                jsonGenerator.writeStartArray();
                                jsonMapper.writeValue(jsonGenerator, parentData);
                                isFirstArray = false;
                                isFileEmpty = false;
                            } else {
                                jsonMapper.writeValue(jsonGenerator, parentData);
                            }
                        } else {
                            jsonGenerator.writeStartArray();
                            jsonMapper.writeValue(jsonGenerator, parentData);
                            jsonGenerator.writeEndArray();
                        }
                    }
                    rows.clear();
                }

                rows.add(line);
                isFirst = false;
            }

            if (!isPassportOgrnFid) {
                jsonGenerator.writeEndArray(); // Закрываем массив объекта
            } else if (!isFileEmpty) {
                jsonGenerator.writeEndArray(); // Закрываем массивы объектов с одним FID для PASSPORT, OGRN, FID
                jsonGenerator.writeEndArray(); // Закрываем общий массив
            } else {
                jsonGenerator.writeEndArray(); // Если файл пуст, закрываем только общий массив
            }
            jsonGenerator.writeEndObject();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    private TableData buildTableDataHierarchy(List<String> rows, Map<String, TableMetaData> tableMetaDataMap) {
        if (rows == null || rows.isEmpty()) {
            return null;
        }

        TableData parentTableData = parseSingleRow(rows.getFirst(), tableMetaDataMap);
        if (parentTableData == null) {
            return null;
        }

        for (int i = 1; i < rows.size(); i++) {
            TableData childTableData = parseSingleRow(rows.get(i), tableMetaDataMap);
            if (childTableData != null) {
                parentTableData.getRelatedTableData().add(childTableData);
            }
        }

        return parentTableData;
    }

    private TableData parseSingleRow(String row, Map<String, TableMetaData> tableMetaDataMap) {
        String[] parts = row.split("%%%", -1);

        if (parts.length == 0 || parts[0].isEmpty()) {
            return null;
        }

        String tableName = parts[0];
        TableMetaData metaData = tableMetaDataMap.get(tableName);

        Set<String> allowedFields = metaData != null ?
            metaData.getFieldMetaData().keySet().stream().map(String::toUpperCase).collect(Collectors.toSet()) :
            new HashSet<>();

        List<TableData.FieldData> fieldData = new ArrayList<>();

        for (int i = 1; i + 1 < parts.length; i += 2) {
            String columnName = parts[i];
            String columnValue = parts[i + 1];

            if (columnName != null && !columnName.isEmpty()) {
                if (allowedFields.contains(columnName.toUpperCase())) {
                    fieldData.add(new TableData.FieldData(columnName, columnValue));
                }
            }
        }

        return new TableData(tableName, fieldData);
    }
}
