package pl.polgrabia.demos.crackingcodeinterview.t17x7;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

public class Program {
    private static final Logger logger = LoggerFactory.getLogger(Program.class);

    public static void main(String[] args) {
        try (BufferedReader br = getBufferedReaderFromArgs(args)) {
            Map<String, WordSet> nameSetMapping = new HashMap<>();
            logger.info("Processing for word frequencies");
            processWordFrequencies(nameSetMapping, br);
            logger.info("Processing for word relations");
            processWordRelations(nameSetMapping, br);

            int setNr = 0;
            Map<Integer, Set<String>> nrSetMapping = new HashMap<>();
            for (var entry : nameSetMapping.entrySet()) {
                if (entry.getValue().getSetNr() < 1) {
                    entry.getValue().setSetNr(++setNr);
                    nrSetMapping.put(setNr, entry.getValue().getSynonyms());
                    for (var synonym : entry.getValue().getSynonyms()) {
                        nameSetMapping.get(synonym).setSetNr(setNr);
                    }
                }
            }

            logger.info("Count of set is {}. Frequencies: {}", setNr, nrSetMapping
                    .entrySet()
                    .stream()
                    .map(it -> "%d(%s) %s".formatted(
                            it.getKey(),
                            it.getValue().stream().findFirst().orElse(null),
                            getSumForSet(nameSetMapping, nrSetMapping.get(it.getKey()))))
                    .collect(Collectors.joining(",")));

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static int getSumForSet(Map<String, WordSet> nameSetMapping, Set<String> wordsList) {
        int sum = 0;
        for (var word : wordsList) {
            sum += nameSetMapping.computeIfAbsent(word, (s) -> getWordSetForCount(0)).getCount();
        }
        return sum;
    }

    private static void processWordRelations(Map<String, WordSet> nameSetMapping, BufferedReader br) throws IOException {
        int newlineCount = 0;
        String line;
        while ((line = br.readLine()) != null) {
            if (line.isEmpty()) {
                if (++newlineCount >= 2) {
                    return;
                }
                continue;
            }

            Scanner scanner = new Scanner(line);
            var word1 = scanner.next();
            var word2 = scanner.next();
            WordSet ws1 = nameSetMapping.computeIfAbsent(word1, (String s) -> getWordSetForCount(0));
            WordSet ws2 = nameSetMapping.computeIfAbsent(word2, (String s) -> getWordSetForCount(0));
            logger.info("Processing word1: {} and word2: {}", word1, word2);

            var set1 = ws1.getSynonyms();
            var set2 = ws2.getSynonyms();
            Set<String> synonyms;
            if (set1 != null && set2 != null) {
                set1.addAll(set2);
                synonyms = set1;
            } else if (set1 != null) {
                ws2.setSynonyms(set1);
                synonyms = set1;
            } else if (set2 != null) {
                ws1.setSynonyms(set2);
                synonyms = set2;
            } else {
                synonyms = new HashSet<>();
            }
            ws1.setSynonyms(synonyms);
            ws2.setSynonyms(synonyms);
            synonyms.add(word1);
            synonyms.add(word2);
        }
    }

    private static void processWordFrequencies(Map<String, WordSet> nameSetMapping, BufferedReader br) throws IOException {
        int newlineCount = 0;
        String line;
        while ((line = br.readLine()) != null) {
            if (line.isEmpty()) {
                if (++newlineCount >= 2) {
                    return;
                }
                continue;
            }

            logger.debug("Processing line {}", line);
            Scanner scanner = new Scanner(line);
            var name = scanner.next();
            var count = scanner.nextInt();
            nameSetMapping.computeIfAbsent(name, (String s) -> getWordSetForCount(count));
        }
    }

    private static WordSet getWordSetForCount(int count) {
        var ws = new WordSet();
        ws.setCount(count);
        return ws;
    }

    private static BufferedReader getBufferedReaderFromArgs(String[] args) throws FileNotFoundException {
        if (args.length == 0) {
            return new BufferedReader(new InputStreamReader(System.in));
        } else {
            return new BufferedReader(
                    new InputStreamReader(
                            new FileInputStream(Paths.get(args[0]).toFile())));
        }
    }
}
