use std::cmp::Ordering;
use htmlentity::types::Byte;
use ntex::http::header;
use ntex::web;
use ntex::web::HttpRequest;
use tokio::{task, time};

use crate::providers::hanime::HanimeProvider;
use crate::providers::perverzija::PerverzijaProvider;
use crate::providers::spankbang::SpankbangProvider;
use crate::util::cache::VideoCache;
use crate::{providers::*, status::*, videos::*, DbPool};

#[derive(Debug)]
struct ClientVersion{
    version: u32,
    subversion: u32,
    name: String,
}


impl ClientVersion {

    pub fn new(version: u32, subversion: u32, name: String) -> ClientVersion{
        ClientVersion{
            version,
            subversion,
            name
        }
    }

    pub fn parse(input: &str) -> Option<Self> {
        // Example input: "Hot%20Tub/22c CFNetwork/1494.0.7 Darwin/23.4.0 0.002478"
        let parts: Vec<&str> = input.split_whitespace().collect();
        if let Some(first) = parts.first() {
            let name_version: Vec<&str> = first.split('/').collect();
            let name = name_version[1];

            // Extract version and optional subversion
            let (version, subversion) = if let Some((v, c)) = name.split_at(name.len().saturating_sub(1)).into() {
                match v.parse::<u32>() {
                    Ok(ver) => (ver, c.chars().next().map(|ch| ch as u32).unwrap_or(0)),
                    Err(_) => {
                        // Try parsing whole string if no subversion exists
                        match name.parse::<u32>() {
                            Ok(ver) => (ver, 0),
                            Err(_) => return None,
                        }
                    }
                }
            } else {
                return None;
            };

            return Some(ClientVersion {
                version: version,
                subversion: subversion,
                name: name.to_string(),
            });
        }
        None
    }
}

// Implement comparisons
impl PartialEq for ClientVersion {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
    }
}

impl Eq for ClientVersion {}

impl PartialOrd for ClientVersion {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for ClientVersion {
    fn cmp(&self, other: &Self) -> Ordering {
        self.version
            .cmp(&other.version)
            .then_with(|| self.subversion.cmp(&other.subversion))
    }
}

pub fn config(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::resource("/status")
            .route(web::post().to(status))
            .route(web::get().to(status)),
    )
    .service(
        web::resource("/videos")
            // .route(web::get().to(videos_get))
            .route(web::post().to(videos_post)),
    );
}

async fn status(req: HttpRequest) -> Result<impl web::Responder, web::Error> {

    let clientversion: ClientVersion = match req.headers().get("User-Agent"){
        Some(v) => match v.to_str(){
            Ok(useragent) => ClientVersion::parse(useragent).unwrap_or_else(|| ClientVersion::new(999, 0, "999".to_string())),
            Err(_) => ClientVersion::new(999, 0, "999".to_string())
        },
        _=> ClientVersion::new(999, 0, "999".to_string())
    };


    let host = req
        .headers()
        .get(header::HOST)
        .and_then(|h| h.to_str().ok())
        .unwrap_or_default()
        .to_string();
    let mut status = Status::new();

    if clientversion >= ClientVersion::new(22,97,"22a".to_string()){
        //add perverzija
        status.add_channel(Channel {
            id: "perverzija".to_string(),
            name: "Perverzija".to_string(),
            description: "Free videos from Perverzija".to_string(),
            premium: false,
            favicon: "https://www.google.com/s2/favicons?sz=64&domain=tube.perverzija.com".to_string(),
            status: "active".to_string(),
            categories: vec![],
            options: vec![
                // ChannelOption {
                //     id: "sort".to_string(),
                //     title: "Sort".to_string(),
                //     description: "Sort the Videos".to_string(), //"Sort the videos by Date or Name.".to_string(),
                //     systemImage: "list.number".to_string(),
                //     colorName: "blue".to_string(),
                //     options: vec![
                //         FilterOption {
                //             id: "date".to_string(),
                //             title: "Date".to_string(),
                //         },
                //         FilterOption {
                //             id: "name".to_string(),
                //             title: "Name".to_string(),
                //         },
                //     ],
                //     multiSelect: false,
                // },
                ChannelOption {
                    id: "featured".to_string(),
                    title: "Featured".to_string(),
                    description: "Filter Featured Videos.".to_string(),
                    systemImage: "star".to_string(),
                    colorName: "red".to_string(),
                    options: vec![
                        FilterOption {
                            id: "all".to_string(),
                            title: "No".to_string(),
                        },
                        FilterOption {
                            id: "featured".to_string(),
                            title: "Yes".to_string(),
                        },
                    ],
                    multiSelect: false,
                },
                // ChannelOption {
                //     id: "duration".to_string(),
                //     title: "Duration".to_string(),
                //     description: "Filter the videos by duration.".to_string(),
                //     systemImage: "timer".to_string(),
                //     colorName: "green".to_string(),
                //     options: vec![
                //         FilterOption {
                //             id: "short".to_string(),
                //             title: "< 1h".to_string(),
                //         },
                //         FilterOption {
                //             id: "long".to_string(),
                //             title: "> 1h".to_string(),
                //         },
                //     ],
                //     multiSelect: true,
                // },
            ],
            nsfw: true,
        });
    }
    status.add_channel(Channel {
        id: "hanime".to_string(),
        name: "Hanime".to_string(),
        description: "Free Hentai from Hanime".to_string(),
        premium: false,
        favicon: "https://www.google.com/s2/favicons?sz=64&domain=hanime.tv".to_string(),
        status: "active".to_string(),
        categories: vec![],
        options: vec![
            ChannelOption{
                id: "sort".to_string(),
                title: "Sort".to_string(),
                description: "Sort the Videos".to_string(), //"Sort the videos by Date or Name.".to_string(),
                systemImage: "list.number".to_string(),
                colorName: "blue".to_string(),
                options: vec![
                    FilterOption {
                        id: "created_at_unix.desc".to_string(),
                        title: "Recent Upload".to_string(),
                    },
                    FilterOption {
                        id: "created_at_unix.asc".to_string(),
                        title: "Old Upload".to_string(),
                    },
                    FilterOption {
                        id: "views.desc".to_string(),
                        title: "Most Views".to_string(),
                    },
                    FilterOption {
                        id: "views.asc".to_string(),
                        title: "Least Views".to_string(),
                    },
                    FilterOption {
                        id: "likes.desc".to_string(),
                        title: "Most Likes".to_string(),
                    },
                    FilterOption {
                        id: "likes.asc".to_string(),
                        title: "Least Likes".to_string(),
                    },
                    FilterOption {
                        id: "released_at_unix.desc".to_string(),
                        title: "New".to_string(),
                    },
                    FilterOption {
                        id: "released_at_unix.asc".to_string(),
                        title: "Old".to_string(),
                    },
                    FilterOption {
                        id: "title_sortable.asc".to_string(),
                        title: "A - Z".to_string(),
                    },
                    FilterOption {
                        id: "title_sortable.desc".to_string(),
                        title: "Z - A".to_string(),
                    },
                ],
                multiSelect: false,
            }
        ],
        nsfw: true,
    });
    status.add_channel(Channel {
        id: "spankbang".to_string(),
        name: "SpankBang".to_string(),
        description: "Popular Porn Videos - SpankBang".to_string(),
        premium: false,
        favicon: "https://www.google.com/s2/favicons?sz=64&domain=spankbang.com".to_string(),
        status: "active".to_string(),
        categories: vec![],
        options: vec![
            ChannelOption{
                id: "sort".to_string(),
                title: "Sort".to_string(),
                description: "Sort the Videos".to_string(), //"Sort the videos by Date or Name.".to_string(),
                systemImage: "list.number".to_string(),
                colorName: "blue".to_string(),
                options: vec![
                    FilterOption {
                        id: "trending_videos".to_string(),
                        title: "Trending".to_string(),
                    },
                    FilterOption {
                        id: "new_videos".to_string(),
                        title: "New".to_string(),
                    },
                    FilterOption {
                        id: "most_popular".to_string(),
                        title: "Popular".to_string(),
                    }],
                multiSelect: false,
            }
        ],
        nsfw: true,
    });
    status.iconUrl = format!("http://{}/favicon.ico", host).to_string();
    Ok(web::HttpResponse::Ok().json(&status))
}

async fn videos_post(
    video_request: web::types::Json<VideosRequest>,
    cache: web::types::State<VideoCache>,
    pool: web::types::State<DbPool>,
) -> Result<impl web::Responder, web::Error> {
    let mut videos = Videos {
        pageInfo: PageInfo {
            hasNextPage: true,
            resultsPerPage: 10,
        },
        items: vec![],
    };
    let channel: String = video_request
        .channel
        .as_deref()
        .unwrap_or("all")
        .to_string();
    let sort: String = video_request.sort.as_deref().unwrap_or("date").to_string();
    let mut query: Option<String> = video_request.query.clone();
    if video_request.query.as_deref() == Some("") {
        query = None;
    }
    let page: u8 = video_request
        .page
        .as_deref()
        .unwrap_or("1")
        .to_string()
        .parse()
        .unwrap();
    let perPage: u8 = video_request
        .perPage
        .as_deref()
        .unwrap_or("10")
        .to_string()
        .parse()
        .unwrap();
    let featured = video_request.featured.as_deref().unwrap_or("all").to_string();
    let provider = get_provider(channel.as_str())
        .ok_or_else(|| web::error::ErrorBadRequest("Invalid channel".to_string()))?;
    let video_items = provider
        .get_videos(cache.get_ref().clone(), pool.get_ref().clone(), channel.clone(), sort.clone(), query.clone(), page.to_string(), perPage.to_string(), featured.clone())
        .await;
    videos.items = video_items.clone();
    if video_items.len() == 0 {
        videos.pageInfo = PageInfo {
            hasNextPage: false,
            resultsPerPage: 10,
        }
    }
//###
    let next_page = page.to_string().parse::<i32>().unwrap_or(1) + 1;
    let provider_clone = provider.clone();
    let cache_clone = cache.get_ref().clone();
    let pool_clone = pool.get_ref().clone();
    let channel_clone = channel.clone();
    let sort_clone = sort.clone();
    let query_clone = query.clone();
    let per_page_clone = perPage.to_string();
    let featured_clone = featured.clone();
    task::spawn_local(async move {
        if let AnyProvider::Spankbang(_) = provider_clone {
            // Spankbang has a delay for the next page
            let delay = time::Duration::from_secs(80);
            time::sleep(delay).await;
        }
        let _ = provider_clone
            .get_videos(
                cache_clone,
                pool_clone,
                channel_clone,
                sort_clone,
                query_clone,
                next_page.to_string(),
                per_page_clone,
                featured_clone,
            )
            .await;
    });
//###
    Ok(web::HttpResponse::Ok().json(&videos))
}

pub fn get_provider(channel: &str) -> Option<AnyProvider> {
    match channel {
        "perverzija" => Some(AnyProvider::Perverzija(PerverzijaProvider::new())),
        "hanime" => Some(AnyProvider::Hanime(HanimeProvider::new())),
        "spankbang" => Some(AnyProvider::Spankbang(SpankbangProvider::new())),
        _ => Some(AnyProvider::Perverzija(PerverzijaProvider::new())),
    }
}