Загрузка данных


package com.efs.generator;

import java.util;

import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.efs.generator.model.ProfileEntry;
import com.efs.generator.model.LastProfileData;
import com.efs.generator.model.CsvRow;
import com.efs.generator.util.UriNormalizer;
import com.efs.generator.util.MethodExtractor;


@Service
public class CsvGeneratorService {

    private static final Logger log = LoggerFactory.getLogger(CsvGeneratorService.class);
    private static final int DEFAULT_SLA = 0;

    private final UriNormalizer uriNormalizer;
    private final MethodExtractor methodExtractor;

    public CsvGeneratorService(UriNormalizer uriNormalizer, MethodExtractor methodExtractor) {
        this.uriNormalizer = uriNormalizer;
        this.methodExtractor = methodExtractor;
    }

    public void generateProfile(List<ProfileEntry> scriptEntries,
                                List<ProfileEntry> profileEntries,
                                String projectType,
                                String urlPrefix,
                                String requirement,
                                Map<String, LastProfileData> lastProfileData,
                                List<CsvRow> allRowsForExcel) {
        Map<String, List<String>> normalizedUrlToOpIds = new HashMap<>();
        Set<String> matchedOpIds = new HashSet<>();
        Set<String> usedOpIds = new HashSet<>();
        int matchedCount = 0;
        int newCount = 0;
        int nextUcNum = 1;
        int validProfileEntriesCount = 0;

        // Build normalized URL -> operation IDs map from script entries
        for (ProfileEntry entry : scriptEntries) {
            String normalizedUri = uriNormalizer.normalizeUri(entry.getUri(), projectType);
            List<String> opIds = normalizedUrlToOpIds.computeIfAbsent(normalizedUri, k -> new ArrayList<>());
            String opId = generateNewOperationId(entry, usedOpIds, nextUcNum);
            opIds.add(opId);
            usedOpIds.add(opId);
            nextUcNum = extractUcNumber(opId) + 1;
        }

        log.debug("--- Обработка операций из профиля аналитика ---");

        for (ProfileEntry entry : profileEntries) {
            log.debug("  Обработка entry: метод={}, uri={}, интенсивность={}", entry.getMethod(), entry.getUri(), entry.getIntensity());

            if (entry.getIntensity() > 0) {
                validProfileEntriesCount++;
            } else {
                log.info("   Операция с нулевой интенсивностью: {} {} - будет покрашена красным в Excel",
                        entry.getMethod(), entry.getUri());
            }

            String operationId = null;
            String profileMethod = entry.getMethod();

            String normalizedUri = uriNormalizer.normalizeUri(entry.getUri(), projectType);

            if (normalizedUrlToOpIds.containsKey(normalizedUri)) {
                List<String> opIds = normalizedUrlToOpIds.get(normalizedUri);

                for (String opId : opIds) {
                    if (!matchedOpIds.contains(opId)) {
                        String opMethod = extractMethodFromOperationId(opId);
                        if (opMethod.equals(profileMethod)) {
                            operationId = opId;
                            matchedOpIds.add(opId);
                            break;
                        }
                    }
                }

                if (operationId == null) {
                    for (String opId : opIds) {
                        if (!matchedOpIds.contains(opId)) {
                            operationId = opId;
                            matchedOpIds.add(opId);
                            if (entry.getIntensity() > 0) {
                                log.warn("   Метод не совпадает! Профиль: {}, operationId: {} (URI: {})",
                                        profileMethod, opId, entry.getUri());
                            }
                            break;
                        }
                    }
                }

                if (operationId != null) {
                    if (entry.getIntensity() > 0) {
                        if (opIds.size() > 1) {
                            log.debug("   Найден в скрипте (один из {}): {} {} → {} (инт: {})",
                                    opIds.size(), profileMethod, entry.getUri(), operationId, entry.getIntensity());
                        } else {
                            log.debug("  Найден в скрипте: {} {} → {} (инт: {})",
                                    profileMethod, entry.getUri(), operationId, entry.getIntensity());
                        }
                        matchedCount++;
                    }
                } else {
                    if (entry.getIntensity() > 0) {
                        log.warn("   Все operation_id для URI {} уже использованы. Создаём новый.", entry.getUri());
                    }
                    operationId = generateNewOperationId(entry, usedOpIds, nextUcNum);
                    nextUcNum = extractUcNumber(operationId) + 1;
                    usedOpIds.add(operationId);
                    if (entry.getIntensity() > 0) {
                        newCount++;
                    }
                }
            } else {
                operationId = generateNewOperationId(entry, usedOpIds, nextUcNum);
                nextUcNum = extractUcNumber(operationId) + 1;
                usedOpIds.add(operationId);
                if (entry.getIntensity() > 0) {
                    log.info("   Создан новый: {} {} → {} (инт: {})",
                            profileMethod, entry.getUri(), operationId, entry.getIntensity());
                    newCount++;
                } else {
                    log.info("   Создан ID для нулевой операции: {} {} → {} (инт: 0)",
                            profileMethod, entry.getUri(), operationId);
                }
            }

            int sla = DEFAULT_SLA;
            if (lastProfileData.containsKey(operationId)) {
                sla = lastProfileData.get(operationId).getSla();
            }

            String fullUri;
            String uriFromProfile = entry.getUri();

            if ("PPRB".equals(projectType)) {
                // Для PPRB не добавляем префикс и не меняем URI
                fullUri = uriFromProfile;
            } else if (uriFromProfile.startsWith("/ic/ufs/corp-retail/")) {
                // Для WEB: уже содержит префикс, не добавляем urlPrefix
                fullUri = uriFromProfile;
            } else if (uriFromProfile.startsWith("/ic/ufs/mobile/")) {
                // Убираем ведущий "/ic" для мобильного пути
                String withoutIC = uriFromProfile.substring(3); // remove leading "/ic"
                // После удаления, строка начинается с "/ufs/mobile/..."
                if (withoutIC.startsWith("/ufs/mobile/")) {
                    // Уже содержит базовый путь, не добавляем urlPrefix
                    fullUri = withoutIC;
                } else {
                    // На всякий случай, если после удаления не совпадает (не должно случиться)
                    fullUri = urlPrefix + withoutIC;
                }
            } else if (uriFromProfile.startsWith("/ufs/mobile/")) {
                // Для мобильного: уже содержит базовый путь
                fullUri = uriFromProfile;
            } else if (uriFromProfile.startsWith("/")) {
                // Относительный путь к корню сайта
                fullUri = urlPrefix + uriFromProfile;
            } else {
                // Относительный путь без ведущего слеша
                fullUri = urlPrefix + "/" + uriFromProfile;
            }

            CsvRow row = new CsvRow(
                    operationId,
                    fullUri,
                    entry.getMethod(),
                    entry.getIntensity(),  // может быть 0
                    sla,
                    operationId,
                    requirement,
                    ""
            );

            // ИЗМЕНЕНО: добавляем ВСЕ операции в список для Excel
            allRowsForExcel.add(row);
        }

        log.debug("--- Итого ---");
        log.debug("  Скрипт операций: {}", scriptEntries.size());
        log.debug("  Профиль аналитика: {}", profileEntries.size());
        log.debug("  Сопоставлено: {}", matchedCount);
        log.debug("  Новых: {}", newCount);
        log.debug("  Всего в результирующем профиле: {}", (matchedCount + newCount));
    }

    private String generateNewOperationId(ProfileEntry entry, Set<String> usedOpIds, int nextUcNum) {
        String base = "UC" + String.format("%03d", nextUcNum) + "_" + entry.getMethod() + "_" + entry.getUri().replaceAll("[^a-zA-Z0-9]", "_").toUpperCase();
        String opId = base;
        int counter = 0;
        while (usedOpIds.contains(opId)) {
            counter++;
            opId = base + "_" + counter;
        }
        return opId;
    }

    private String extractMethodFromOperationId(String opId) {
        // Example: UC114_POST_business_analytics_industry_top_avgcheck_WEB
        // We need to extract the method part between first and second underscore after UCxxx_
        // Format: UCxxx_METHOD_REST
        // We'll split by '_'
        String[] parts = opId.split("_");
        if (parts.length >= 3) {
            // parts[0] = UCxxx, parts[1] = method, rest = rest
            return parts[1];
        }
        // fallback: return empty
        return "";
    }

    private int extractUcNumber(String opId) {
        // Extract the numeric part after UC
        // opId format: UCxxx_...
        if (opId.startsWith("UC") && opId.length() >= 5) {
            String numPart = opId.substring(2, 5); // assuming three digits
            try {
                return Integer.parseInt(numPart);
            } catch (NumberFormatException e) {
                return 0;
            }
        }
        return 0;
    }
}