import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Pattern;
import java.util.stream.Stream;

public class JSONloose {

    private static final String SOURCE_DIR = "files";
    private static final String MISMATCH_DIR = "json";
    private static final String INVALID_HASH_DIR = "json_search";
    // Regex for exactly 64 hexadecimal characters
    private static final Pattern SHA256_PATTERN = Pattern.compile("^[a-fA-F0-9]{64}$");

    public static void main(String[] args) {
        Path sourcePath = Paths.get(SOURCE_DIR);

        if (!Files.exists(sourcePath) || !Files.isDirectory(sourcePath)) {
            System.err.println("Error: Directory '" + SOURCE_DIR + "' not found.");
            return;
        }

        try (Stream<Path> paths = Files.list(sourcePath)) {
            paths.filter(path -> path.toString().toLowerCase().endsWith(".json"))
                 .forEach(JSONloose::processFile);
        } catch (IOException e) {
            System.err.println("Error reading directory: " + e.getMessage());
        }
    }

    private static void processFile(Path filePath) {
        String fileNameWithExt = filePath.getFileName().toString();
        // Remove .json extension
        String fileNameNoExt = fileNameWithExt.substring(0, fileNameWithExt.lastIndexOf('.'));

        if (isValidSha256Format(fileNameNoExt)) {
            try {
                String actualHash = calculateSHA256(filePath);
                if (!fileNameNoExt.equalsIgnoreCase(actualHash)) {
                    // Scenario: Name is a hash, but not THE hash of this file
                    moveFile(filePath, MISMATCH_DIR);
                }
            } catch (Exception e) {
                System.err.println("Failed to process hash for " + fileNameWithExt);
            }
        } else {
            // Scenario: Name is not a valid SHA256 string
            moveFile(filePath, INVALID_HASH_DIR);
        }
    }

    private static boolean isValidSha256Format(String name) {
        return SHA256_PATTERN.matcher(name).matches();
    }

    private static String calculateSHA256(Path path) throws IOException, NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        try (InputStream is = Files.newInputStream(path)) {
            byte[] buffer = new byte[8192];
            int n;
            while ((n = is.read(buffer)) != -1) {
                digest.update(buffer, 0, n);
            }
        }
        byte[] hash = digest.digest();
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        return hexString.toString();
    }

    private static void moveFile(Path source, String targetDirName) {
        try {
            Path targetDir = Paths.get(targetDirName);
            // Create directory if it doesn't exist
            if (!Files.exists(targetDir)) {
                Files.createDirectories(targetDir);
            }

            Path targetPath = targetDir.resolve(source.getFileName());

            // Only move if the file doesn't already exist in the destination
            if (Files.exists(targetPath)) {
                System.out.println("Skipped: " + source.getFileName() + " already exists in " + targetDirName);
            } else {
                Files.move(source, targetPath);
                System.out.println("Moved: " + source.getFileName() + " -> " + targetDirName + "/");
            }
        } catch (IOException e) {
            System.err.println("Move failed for " + source.getFileName() + ": " + e.getMessage());
        }
    }
}