import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.*;

public class MediaCrawler {

    private static final String DOWNLOAD_DIR = "files";
    private static final String LOG_DIR = "log";
    private static final Set<String> VISITED_URLS = new HashSet<>();
    private static final Set<String> MEDIA_EXTENSIONS = new HashSet<>(Arrays.asList(
            "jpg", "jpeg", "png", "gif", "bmp", "webp", "mp4"
    ));
    
    private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36";
    private static final int MAX_DEPTH = 2; // Increased complexity of relative links warrants a lower depth for safety

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("Enter the starting URL (include http://): ");
        String startUrl = scanner.nextLine();
        
        System.out.print("Enter your signature for the logs: ");
        String signature = scanner.nextLine();

        try {
            // Create directories
            Files.createDirectories(Paths.get(DOWNLOAD_DIR));
            Files.createDirectories(Paths.get(LOG_DIR));
            
            crawl(startUrl, 0, signature);
        } catch (IOException e) {
            System.err.println("Setup error: " + e.getMessage());
        }
        
        System.out.println("\nProcess complete.");
    }

    private static void crawl(String urlString, int depth, String signature) {
        if (depth > MAX_DEPTH || VISITED_URLS.contains(urlString) || urlString.isEmpty()) {
            return;
        }

        VISITED_URLS.add(urlString);
        System.out.println("Exploring: " + urlString);

        String extension = getFileExtension(urlString);

        if (MEDIA_EXTENSIONS.contains(extension)) {
            downloadAndLog(urlString, extension, signature);
        } else if (isPotentiallyWebPage(urlString)) {
            processWebPage(urlString, depth, signature);
        }
    }

    private static void processWebPage(String urlString, int depth, String signature) {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(urlString).openConnection();
            connection.setRequestProperty("User-Agent", USER_AGENT);
            connection.setConnectTimeout(5000);
            
            String contentType = connection.getContentType();
            if (contentType == null || !contentType.contains("text/html")) return;

            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            // Regex captures both href (links) and src (media sources)
            Pattern pattern = Pattern.compile("(href|src)=\"([^\"]*)\"", Pattern.CASE_INSENSITIVE);

            while ((line = reader.readLine()) != null) {
                Matcher matcher = pattern.matcher(line);
                while (matcher.find()) {
                    String foundUrl = matcher.group(2);
                    
                    // Resolve Relative URL to Absolute URL
                    String absoluteUrl = resolveRelative(urlString, foundUrl);
                    if (absoluteUrl != null) {
                        crawl(absoluteUrl, depth + 1, signature);
                    }
                }
            }
            reader.close();
        } catch (Exception e) {
            System.err.println("Skipping " + urlString + " (Reason: " + e.getMessage() + ")");
        }
    }

    private static void downloadAndLog(String urlString, String extension, String signature) {
        String hash = generateHash(urlString);
        String fileName = hash + "." + extension;
        Path targetPath = Paths.get(DOWNLOAD_DIR, fileName);

        if (Files.exists(targetPath)) {
            System.out.println("   [!] Already exists: " + fileName);
            return;
        }

        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(urlString).openConnection();
            connection.setRequestProperty("User-Agent", USER_AGENT);

            try (InputStream in = connection.getInputStream()) {
                Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
                System.out.println("   [+] Downloaded: " + fileName);
                writeLog(hash, urlString, signature);
            }
        } catch (IOException e) {
            System.err.println("   [-] Error downloading " + urlString);
        }
    }

    private static void writeLog(String hash, String url, String signature) {
        File logFile = new File(LOG_DIR, hash + ".txt");
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        
        try (PrintWriter writer = new PrintWriter(new FileWriter(logFile))) {
            writer.println("URL: " + url);
            writer.println("Date Accessed: " + timestamp);
            writer.println("Signature: " + signature);
            writer.println("File Hash: " + hash);
        } catch (IOException e) {
            System.err.println("   [-] Failed to write log for " + hash);
        }
    }

    private static String resolveRelative(String base, String relative) {
        try {
            // Skips mailto, javascript, and anchor links
            if (relative.startsWith("mailto:") || relative.startsWith("javascript:") || relative.startsWith("#")) {
                return null;
            }
            URL baseUrl = new URL(base);
            URL resolvedUrl = new URL(baseUrl, relative);
            return resolvedUrl.toString();
        } catch (MalformedURLException e) {
            return null;
        }
    }

    private static String getFileExtension(String urlString) {
        try {
            String path = new URL(urlString).getPath();
            int lastDot = path.lastIndexOf('.');
            if (lastDot > 0) {
                return path.substring(lastDot + 1).toLowerCase();
            }
        } catch (MalformedURLException e) { /* Ignore */ }
        return "";
    }

    private static boolean isPotentiallyWebPage(String urlString) {
        String ext = getFileExtension(urlString);
        // If extension is 3-4 chars and NOT media, it's skipped by requirements
        if (!ext.isEmpty() && ext.length() >= 3 && ext.length() <= 4) {
            return Arrays.asList("html", "htm", "php", "asp", "jsp").contains(ext);
        }
        return ext.isEmpty(); // No extension usually means a directory or main page
    }

    private static String generateHash(String input) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(input.getBytes());
            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();
        } catch (NoSuchAlgorithmException e) {
            return String.valueOf(input.hashCode());
        }
    }
}