use core::panic;
use iced::widget::image::Handle;
use reqwest::{self, blocking::Client, header::USER_AGENT, Error, Proxy};
use rss::Channel;
use scraper::{Html, Selector};
use url::Url;

const DEFAULT_TOR_PROXY: &str = "socks5h://127.0.0.1:9050";
const DEFAULT_I2P_PROXY: &str = "socks5h://127.0.0.1:4447";

#[derive(PartialEq)]
pub enum Network {
    Clearnet,
    Tor,
    I2P,
    Unknown,
}

pub fn url_network(url: &str) -> Network {
    match Url::parse(url) {
        Ok(u) => match u.host() {
            Some(host) => {
                let h = host.to_string();
                if h.find(".onion").is_some() {
                    return Network::Tor;
                } else if h.find(".i2p").is_some() {
                    return Network::I2P;
                } else {
                    return Network::Clearnet;
                }
            }
            None => {return Network::Unknown;}
        },
        Err(e) => {
            return Network::Clearnet;
        }
    }
    Network::Clearnet
}

#[test]
fn network_testing(){
    assert!(url_network("http://gabe.onion/rss") == Network::Tor);
    assert!(url_network("http://gabe.i2p/rss") == Network::I2P);
    assert!(url_network("https://gabe.rocks/rss") == Network::Clearnet);
}

fn get_client(network: Network) -> Result<Client, Error> {
    //in the long run, support for fancy things like arti & user-configurable proxies will be important, nevermind other networks like reticulum!
    match network {
        Network::Clearnet => {
            return Ok(Client::new());
        }
        Network::Tor => {
            match Client::builder()
                .proxy(Proxy::http(DEFAULT_TOR_PROXY).unwrap())
                .build()
            {
                Ok(client) => {
                    return Ok(client);
                }
                Err(e) => {
                    return Err(e);
                }
            }
        }
        Network::I2P => {
            match Client::builder()
                .proxy(Proxy::http(DEFAULT_I2P_PROXY).unwrap())
                .build()
            {
                Ok(client) => {
                    return Ok(client);
                }
                Err(e) => {
                    return Err(e);
                }
            }
        }
        Network::Unknown => {
            println!("Fetching client for unknown network...");
            return Ok(Client::new());
        }
    }
}

fn get_content(url: &str) -> Option<String> {
    let client = get_client(url_network(url)).unwrap();
    let res = client.get(url).header(USER_AGENT, "RSS Reader").send();
    match res {
        Ok(resp) => match resp.text() {
            Ok(body) => return Some(body),
            Err(_) => return None,
        },
        Err(_) => return None,
    }
}

#[test]
fn get_onion_content() {
    get_content("http://gabriel262me3lgv3w7xohtesg3laoojmtye644pwirhdm73qmedmsqd.onion/rss");
}

pub fn get_feeds(url: &str) -> Option<Vec<Channel>> {
    let page = get_content(url).unwrap();
    let doc = Html::parse_document(&page);
    let link_selector = Selector::parse("head link").unwrap();
    let mut feeds: Vec<Channel> = Vec::new();
    for e in doc.select(&link_selector) {
        if let Some(rel) = e.value().attr("rel") {
            if rel == "alternate" {
                if let Some(href) = e.value().attr("href") {
                    match load_rss(href) {
                        Some(c) => feeds.push(c),
                        None => {}
                    }
                }
            }
        }
    }
    return match feeds.len() {
        0 => None,
        _ => Some(feeds),
    };
}

pub fn is_feed(url: &str) -> bool {
    match get_feeds(url) {
        Some(_) => true,
        None => false,
    }
}

pub fn load_rss(url: &str) -> Option<Channel> {
    let client = get_client(url_network(url)).unwrap();
    let res = client.get(url).header(USER_AGENT, "RSS Reader").send();
    match res {
        Ok(resp) => match resp.bytes() {
            Ok(body) => match Channel::read_from(&*body) {
                Ok(channel) => Some(channel),
                Err(e) => {
                    panic!("Error parsing feed:\n{}", e);
                }
            },
            Err(_) => {
                panic!("Empty response")
            }
        },
        Err(err) => {
            panic!("Error loading feed.:{}", err)
        }
    }
}
pub fn get_item_image(item: &rss::Item) -> Option<&str> {
    // Only bother with itunes:image
    print!("{} \n", item.title().unwrap());
    match item.itunes_ext() {
        Some(e) => match e.image() {
            Some(img) => {
                println!("Image found: {}", img);
                Some(img)
            }
            None => {
                println!("Itunes extension found, but image was not..");
                None
            }
        },
        None => {
            println!("found no extensions");
            None
        }
    }
}
pub fn download_image(url: &str) -> Option<iced::widget::image::Handle> {
    match reqwest::blocking::get(url) {
        Ok(r) => {
            let img: Handle = Handle::from_bytes(r.bytes().unwrap());
            Some(img)
        }
        Err(_) => {
            println!("Failed to download image.");
            None
        }
    }
}

const MEDIA_FORMATS: [&str; 15] = [
    ".mp3", ".wav", ".ogg", ".flac", ".ogv", ".opus", ".webm", ".mp4", ".mkv", ".mov", ".webp",
    ".gif", ".png", ".jpg", ".pdf",
];

fn is_media(link: &str) -> bool {
    for f in MEDIA_FORMATS {
        match link.find(f) {
            Some(_) => {
                return true;
            }
            None => {}
        }
    }

    false
}


pub fn parse_links(content: &str) -> Vec<String> {
    let mut result = Vec::new();
    let frag = Html::parse_fragment(content);
    let link_selector = Selector::parse("a").unwrap();
    for link in frag.select(&link_selector) {
        if let Some(href) = link.value().attr("href") {
            if is_media(href) == false {
                result.push(href.to_owned());
            }
            /*if is_feed(href) {
                result.push(href.to_owned());
            }*/
        }
    }
    result
}