package com.tinsae.crawler;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.BufferedWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;
import java.util.Set;

/**
 * CrawlTask represents a single URL crawl operation
 * Runs in a separate thread, fetches content, extracts links,
 * appends URLs to file, and submits new tasks for discovered links.
 */
public class CrawlTask implements Runnable {

    private final String url;
    private final int depth;
    private final Crawler crawler;
    private static final String urlsFilename = "crawled_urls_" + System.currentTimeMillis() + ".txt";

    public CrawlTask(String url, int depth, Crawler crawler) {
        this.url = url;
        this.depth = depth;
        this.crawler = crawler;
    }

    public String getUrl() {
        return url;
    }

    public int getDepth() {
        return depth;
    }

    @Override
    public void run() {
        System.out.println("[" + Thread.currentThread().getName() + "] Crawling: " + url + " (depth: " + depth + ")");

        try {
            // Fetch and parse the page
            Document document = fetchPage(url);
            if (document == null) {
                System.err.println("[" + Thread.currentThread().getName() + "] Failed to fetch: " + url);
                return;
            }

            // Extract links if we haven't reached max depth
            if (depth < crawler.getMaxDepth()) {
                Set<String> links = extractLinks(document, url);
                System.out.println(
                        "[" + Thread.currentThread().getName() + "] Found " + links.size() + " links on " + url);

                // Save URLs to file
                if (!links.isEmpty()) {
                    saveUrlsToFile(links, url, depth);
                }

                // Submit tasks for each discovered link
                for (String link : links) {
                    CrawlTask newTask = new CrawlTask(link, depth + 1, crawler);
                    crawler.submitTask(newTask);
                }
            } else {
                System.out.println("[" + Thread.currentThread().getName() + "] Max depth reached for: " + url);
            }

        } catch (Exception e) {
            System.err.println(
                    "[" + Thread.currentThread().getName() + "] Error crawling " + url + ": " + e.getMessage());
        }
    }

    /**
     * Fetch a web page and return as JSoup Document
     */
    private Document fetchPage(String url) throws IOException {
        // Add timeout and user agent for responsible crawling
        return Jsoup.connect(url)
                .timeout(10000) // 10 second timeout
                .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
                .get();
    }

    /**
     * Extract all links from a page that are within the same domain
     * Parses HTML line by line and extracts any URLs found
     */
    private Set<String> extractLinks(Document document, String pageUrl) throws URISyntaxException {
        Set<String> links = new HashSet<>();
        URI baseUri = new URI(pageUrl);
        String baseDomain = baseUri.getHost();

        // Get the full HTML as text and split by lines
        String htmlContent = document.html();
        String[] lines = htmlContent.split("\n");

        // Pattern to match URLs in HTML
        java.util.regex.Pattern urlPattern = java.util.regex.Pattern.compile(
                "https?://[^\\s\"'<>)]+");

        for (String line : lines) {
            // Look for any URLs in this line
            java.util.regex.Matcher matcher = urlPattern.matcher(line);

            while (matcher.find()) {
                String href = matcher.group();

                if (href.isEmpty() || !href.startsWith("http")) {
                    continue;
                }

                try {
                    URI linkUri = new URI(href);
                    String linkHost = linkUri.getHost();

                    // Only crawl links from the same domain
                    if (linkHost != null && linkHost.equals(baseDomain)) {
                        links.add(href);
                    }
                } catch (URISyntaxException e) {
                    // Skip invalid URLs
                }
            }
        }

        return links;
    }

    /**
     * Save discovered URLs to a file with timestamp
     */
    private synchronized void saveUrlsToFile(Set<String> urls, String sourceUrl, int depth) {
        try {
            String filepath = crawler.getOutputDir() + "/" + urlsFilename;

            // Append URLs to file (create if doesn't exist)
            try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filepath),
                    StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {

                writer.write("=== Found on: " + sourceUrl + " (depth: " + depth + ") ===\n");
                for (String url : urls) {
                    writer.write(url + "\n");
                }
                writer.write("\n");
            }

            System.out.println(
                    "[" + Thread.currentThread().getName() + "] Appended " + urls.size() + " URLs to: " + urlsFilename);
        } catch (IOException e) {
            System.err.println("[" + Thread.currentThread().getName() + "] Failed to save URLs: " + e.getMessage());
        }
    }
}
