use std::process::{Command};
use std::io::{stdin,stdout,Write};
use colored::Colorize;
use regex::Regex;

fn clear_screen() {
    Command::new("clear")
        .status()
        .expect("Failed to clear screen");
    //println!("clearing");
}

fn parse_markdown(page_content: String) -> (String, Vec<String>) {
    let mut parsed_page_content: String = "".to_string();
    let mut hyperlink_number_counter: u64 = 0;
    let mut links: Vec<String> = Vec::new();
    let (screen_width, screen_height) = termion::terminal_size().unwrap(); // So the horizontal line (<hr/>) spans the whole console

    for line in page_content.lines() {
        let mut parsed_line: String = line.to_string();
        // Bold
        let bold_regex = Regex::new(r"((\*\*)|(__))(.*?)((\*\*)|(__))").unwrap();
        parsed_line = bold_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            caps[4].bold().to_string()
        }).to_string();

        // Strikethrough
        let strikethrough_regex = Regex::new(r"~~(.*?)~~").unwrap();
        parsed_line = strikethrough_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            caps[1].strikethrough().to_string()
        }).to_string();

        // Horizontal lines
        let hr_regex = Regex::new(r"^(\*\*\*)|(---)|(___)$").unwrap();
        parsed_line = hr_regex.replace_all(&parsed_line, |_caps: &regex::Captures| {
            let mut result: String = "\n".to_string();
            for _x in 0..screen_width/2 {
                result += "- ";
            }
            result += "\n";
            result
        }).to_string();

        // html br tag support
        let br_regex = Regex::new(r"(.*?)<br/>(.*?)").unwrap();
        parsed_line = br_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            format!("{}{}{}", &caps[1], "\n", &caps[2])
        }).to_string();

        // Italics
        let italic_regex = Regex::new(r"\*(.*?)\*").unwrap();
        parsed_line = italic_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            caps[1].italic().to_string()
        }).to_string();
        let italic_regex = Regex::new(r"_(.*?)_").unwrap();
        parsed_line = italic_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            caps[1].italic().to_string()
        }).to_string();

        // Block quotes
        let block_quotes_regex = Regex::new(r"^>(.*)").unwrap();
        parsed_line = block_quotes_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            format!("   | {}", &caps[1])
        }).to_string();

        // Ordered list
        let ordered_list_regex = Regex::new(r"^([ \t]+|^)([0-9]+)\. (.*)").unwrap();
        parsed_line = ordered_list_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            format!("{} {}. {}", &caps[1], &caps[2], &caps[3])
        }).to_string();

        // Unordered list ([    ]+|^)- (.*)
        let unordered_list_regex = Regex::new(r"^([ \t]+|^)(-|\+|\*).(.*)").unwrap();
        parsed_line = unordered_list_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            format!("{} • {}", &caps[1], &caps[3])
        }).to_string();

        // Inline code
        let inline_code_regex = Regex::new(r"`(.*?)`").unwrap();
        parsed_line = inline_code_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            format!("{}", &caps[1].magenta())
        }).to_string();

        // HyperLink
        let hyperlink_regex = Regex::new(r"(.*?)\[(.*?)\]\((.*?)\)").unwrap();
        parsed_line = hyperlink_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            // Check if the character before the link is not '!'
            if !caps[1].ends_with('!') {  // caps[1] is everything before the link
                let result = format!("{}{}[{}]", &caps[1], &caps[2].blue().underline(), hyperlink_number_counter);
                let url = caps[3].to_string();
                links.push(url);
                hyperlink_number_counter += 1;
                result
            } else {
                // If it's an image (starts with !), return the link as is
                let url = caps[3].to_string();
                links.push(url);
                hyperlink_number_counter += 1;
                format!("({})[{}]", &caps[2].green(), hyperlink_number_counter)
            }
        }).to_string();
        
        let quick_hyperlink_regex = Regex::new(r"<(.*?)>").unwrap();
        parsed_line = quick_hyperlink_regex.replace_all(&parsed_line, |caps: &regex::Captures| {
            hyperlink_number_counter += 1;
            let url = caps[1].to_string();
            links.push(url);
            format!("{}[{}]", &caps[1].blue().underline(), hyperlink_number_counter)
        }).to_string();


        parsed_page_content+=&(parsed_line + "\n");
    }

    // multiline code
    let multiline_code_regex = Regex::new(r"(?ms)%%%((.*?\n)+?)%%%").unwrap();
    parsed_page_content = multiline_code_regex.replace_all(&parsed_page_content, |caps: &regex::Captures| {
        // Capture the code inside the %% blocks
        let code_block = &caps[1];
        
        // Add a tab to each line in the block
        let indented_code = code_block
            .lines()
            .map(|line| format!("\t{}", line)) // Insert tab at the start of each line
            .collect::<Vec<String>>()
            .join("\n");

        // Return the formatted block with magenta color
        format!("{}", indented_code.magenta())
    }).to_string();

    return (parsed_page_content, links);
}

fn fetch_page(host: &String, port: &String, path: &String) -> String {
    let full_url_formatted = format!("{}:{}/{}", host, port, path);

    // Call curl using Com, mand
    let output = Command::new("curl")
        .arg(full_url_formatted)
        .output()
        .expect("Failed to execute curl command");

    // Check if the command was successful
    if output.status.success() {
        let page: String = String::from_utf8_lossy(&output.stdout).to_string();
        return page
    } else {
        eprintln!("Error:\n{}", String::from_utf8_lossy(&output.stderr));
        let result: String = "error".to_string();
        return result
    }
}

fn render_page(host: String, port: String, path: String) -> Vec<String> {
    clear_screen();
    let mut content = fetch_page(&host, &port, &path);
    let mut links = Vec::new();
    let (screen_width, screen_height) = termion::terminal_size().unwrap();

    (content, links) = parse_markdown(content);

    for _i in 0..screen_width {
       print!("—"); 
    }
    print!("{}:{}/{}\n", host, port, path);
    for _i in 0..screen_width {
       print!("—"); 
    }
    println!("\n\n{}", content);
    for _i in 0..screen_width {
       print!("—"); 
    }
    println!();

    // Return links (you can add link parsing logic)
    return links;   
}

fn input() -> String{
    let mut s=String::new();
    let _=stdout().flush();
    stdin().read_line(&mut s).expect("Did not enter a correct string");
    if let Some('\n')=s.chars().next_back() {
        s.pop();
    }
    if let Some('\r')=s.chars().next_back() {
        s.pop();
    }
    return s;
}

fn parse_url(url: String, previous_host: &String) -> (String, String, String) {
    let mut host: String = previous_host.to_string();
    let mut port: String = "3477".to_string();
    let mut path: String = "/".to_string();

    let mttp_regex = Regex::new(r"^mttp:\/\/(.*?)\/(.*?)$").unwrap(); //  mttp://example.com/index.md
    let mttp_regex_no_path = Regex::new(r"^mttp:\/\/(.*?)$").unwrap(); // mttp://example.com
    let accept_this_as_mttp = Regex::new(r"^(.*?)$").unwrap(); //         example.com
    let no_protocol_but_path = Regex::new(r"^(.*?)/(.*?)$").unwrap(); //  example.com/index.md
    let path_change = Regex::new(r"^/(.*?)$").unwrap(); //                /index.md

    if let Some(caps) = mttp_regex.captures(&url) {
        host = caps[1].to_string(); 
        port = "3477".to_string();   
        path = caps[2].to_string();
    }
    else if let Some(caps) = mttp_regex_no_path.captures(&url) {
        host = caps[1].to_string(); 
        port = "3477".to_string();   
        path = "/".to_string();
    }
    else if let Some(caps) = path_change.captures(&url) {
        println!("path change");
        host = previous_host.to_string(); 
        port = "3477".to_string();   
        path = caps[1].to_string();
    }
    else if let Some(caps) = no_protocol_but_path.captures(&url) {
        host = caps[1].to_string(); 
        port = "3477".to_string();
        path = caps[2].to_string();
    }
    else if let Some(caps) = accept_this_as_mttp.captures(&url) {
        host = caps[1].to_string(); 
        port = "3477".to_string();   
        path = "/".to_string();
    }
    println!("{}:{}/{}",host,port,path);

    return (host, port, path);
}

fn main() {
    clear_screen();
    println!("Enter a url: ");
    let url = input();
    
    if url == "q" {
        std::process::exit(0);
    }

    let (mut host, mut port, mut path) = parse_url(url, &"example.com".to_string()); // Must pass
                                                                                    // 'previous
                                                                                    // host'
    let mut load_page: bool = true;
    let mut links: Vec<String> = Vec::new();

    'mainloop: loop {
        if load_page {
            links = Vec::new();
            links = render_page(host.clone(), port.clone(), path.clone());   

            println!("Enter reference number to follow, h for help, or q to quit ");
        }
        load_page = true;

        let link_to_follow = input(); 
        if link_to_follow == "q" {
            break 'mainloop;
        }
        else if link_to_follow == "r" {
            continue;
        }
        else if link_to_follow == "h" {
            println!("Source code: https://git.javalsai.dynv6.net/deadvey/markdown-webbrowser\nq: quit\nh: help\nr: reload\ni: visit root index of this host eg: root index of mttp://deadvey.com/blog/4.md is just deadvey.com\nb: go back in history\nox: print the hyprlink of reference x eg: o5 or o24
            ");
            load_page = false;
        }
        else if link_to_follow.chars().nth(0).unwrap() == 'o' {
            let number_str = &link_to_follow[1..];
            if let Ok(number) =  number_str.parse::<usize>() {
                println!("{}", links[number]);
            } else {
                println!("error");
            }
            load_page = false;
        }
        else {
            if let Ok(number) = link_to_follow.parse::<usize>() {
                if number < links.len() {
                    (host, port, path) = parse_url(links[number].clone(), &host);
                } else {
                    println!("Invalid reference id");
                    load_page = false;
                }
            } else {
                println!("Invalid input");
                load_page = false;
            }
        }
    }
}

