use clap::Parser;
use std::fmt;

#[derive(Debug, PartialEq)]
enum MediaType {
    Post,
    Reel,
    Reels,
    IGTV,
    Story,
    Highlight,
    UserProfile,
    Hashtag,
    IosMedia,
}

#[derive(Debug, PartialEq)]
struct ParsedUrl {
    media_type: MediaType,
    id: Option<String>,
    username: Option<String>,
}

impl fmt::Display for MediaType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MediaType::Post => write!(f, "Post"),
            MediaType::Reel => write!(f, "Reel"),
            MediaType::Reels => write!(f, "Reels"),
            MediaType::IGTV => write!(f, "IGTV"),
            MediaType::Story => write!(f, "Story"),
            MediaType::Highlight => write!(f, "Highlight"),
            MediaType::UserProfile => write!(f, "User Profile"),
            MediaType::Hashtag => write!(f, "Hashtag"),
            MediaType::IosMedia => write!(f, "iOS Media"),
        }
    }
}

fn strip_url_scheme(url: &str) -> &str {
    let url = url.trim_start_matches("https://");
    let url = url.trim_start_matches("http://");
    let url = url.trim_start_matches("instagram://");
    url.trim_start_matches("www.")
}

fn parse_instagram_url(url: &str) -> Option<ParsedUrl> {
    let trimmed = url.trim();

    // instagram://media?id=<id>
    if url.starts_with("instagram://") {
        if let Some(id) = trimmed
            .strip_prefix("instagram://media?id=")
            .or_else(|| {
                let start = trimmed.find("id=")?;
                Some(&trimmed[start + 3..])
            })
        {
            let id = id.split('&').next().unwrap_or(id);
            if !id.is_empty() {
                return Some(ParsedUrl {
                    media_type: MediaType::IosMedia,
                    id: Some(id.to_string()),
                    username: None,
                });
            }
        }
    }

    let host = strip_url_scheme(trimmed);
    let host = host.split('?').next().unwrap_or(host); // strip query
    let host = host.split('#').next().unwrap_or(host); // strip fragment
    let host = host.trim_end_matches('/');

    // Must be an instagram.com URL
    if !host.starts_with("instagram.com") && !host.starts_with("www.instagram.com") {
        return None;
    }

    let path = host.trim_start_matches("instagram.com").trim_start_matches("www.instagram.com");
    let path = path.trim_start_matches('/');

    if path.is_empty() {
        return None;
    }

    let segments: Vec<&str> = path.split('/').collect();

    // /explore/tags/<tag>
    if segments.len() >= 3 && segments[0] == "explore" && segments[1] == "tags" {
        return Some(ParsedUrl {
            media_type: MediaType::Hashtag,
            id: Some(segments[2].to_string()),
            username: None,
        });
    }

    // /stories/highlights/<id>
    if segments.len() >= 3 && segments[0] == "stories" && segments[1] == "highlights" {
        return Some(ParsedUrl {
            media_type: MediaType::Highlight,
            id: Some(segments[2].to_string()),
            username: None,
        });
    }

    // /stories/<username>[/<story_id>]
    if segments.len() >= 2 && segments[0] == "stories" {
        return Some(ParsedUrl {
            media_type: MediaType::Story,
            username: Some(segments[1].to_string()),
            id: segments.get(2).map(|s| s.to_string()),
        });
    }

    // Check for /p/, /tv/, /reel/, /reels/ at any position
    // (username can be prefixed, e.g. instagram.com/username/reel/xxx)
    let media_keyword_idx = segments.iter().position(|&s| matches!(s, "p" | "tv" | "reel" | "reels"));

    if let Some(idx) = media_keyword_idx {
        let shortcode = segments.get(idx + 1).map(|s| s.to_string());
        let username = if idx > 0 { Some(segments[0].to_string()) } else { None };

        let media_type = match segments[idx] {
            "p" => MediaType::Post,
            "tv" => MediaType::IGTV,
            "reel" => MediaType::Reel,
            "reels" => MediaType::Reels,
            _ => unreachable!(),
        };

        return Some(ParsedUrl { media_type, id: shortcode, username });
    }

    // /<username> - catch-all for user profiles
    let known_paths = ["accounts", "session", "direct", "accounts", "web", "api", "graphql", "explore"];
    let first = segments[0];
    if first.len() >= 2 && !known_paths.contains(&first) {
        return Some(ParsedUrl {
            media_type: MediaType::UserProfile,
            username: Some(first.to_string()),
            id: None,
        });
    }

    None
}

#[derive(Parser)]
#[command(name = "insta-saver", about = "Detect Instagram URL types")]
struct Cli {
    url: String,
}

fn main() {
    let cli = Cli::parse();

    match parse_instagram_url(&cli.url) {
        Some(parsed) => {
            println!("URL Type: {}", parsed.media_type);
            if let Some(ref id) = parsed.id {
                println!("       ID: {id}");
            }
            if let Some(ref username) = parsed.username {
                println!(" Username: {username}");
            }
        }
        None => {
            println!("Unknown or invalid Instagram URL");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_post() {
        let r = parse_instagram_url("https://instagram.com/p/aye83DjauH/").unwrap();
        assert_eq!(r.media_type, MediaType::Post);
        assert_eq!(r.id.unwrap(), "aye83DjauH");
    }

    #[test]
    fn test_post_with_www() {
        let r = parse_instagram_url("https://www.instagram.com/p/aye83DjauH/?foo=bar#abc").unwrap();
        assert_eq!(r.media_type, MediaType::Post);
        assert_eq!(r.id.unwrap(), "aye83DjauH");
    }

    #[test]
    fn test_post_embed() {
        let r = parse_instagram_url("http://instagram.com/p/9o6LshA7zy/embed/").unwrap();
        assert_eq!(r.media_type, MediaType::Post);
        assert_eq!(r.id.unwrap(), "9o6LshA7zy");
    }

    #[test]
    fn test_reel() {
        let r = parse_instagram_url("https://www.instagram.com/reel/Chunk8-jurw/").unwrap();
        assert_eq!(r.media_type, MediaType::Reel);
        assert_eq!(r.id.unwrap(), "Chunk8-jurw");
    }

    #[test]
    fn test_reels_plural() {
        let r = parse_instagram_url("https://www.instagram.com/reels/Cop84x6u7CP/").unwrap();
        assert_eq!(r.media_type, MediaType::Reels);
        assert_eq!(r.id.unwrap(), "Cop84x6u7CP");
    }

    #[test]
    fn test_igtv() {
        let r = parse_instagram_url("https://www.instagram.com/tv/BkfuX9UB-eK/").unwrap();
        assert_eq!(r.media_type, MediaType::IGTV);
        assert_eq!(r.id.unwrap(), "BkfuX9UB-eK");
    }

    #[test]
    fn test_story() {
        let r = parse_instagram_url("https://www.instagram.com/stories/fruits_zipper/").unwrap();
        assert_eq!(r.media_type, MediaType::Story);
        assert_eq!(r.username.unwrap(), "fruits_zipper");
    }

    #[test]
    fn test_story_with_id() {
        let r = parse_instagram_url("https://www.instagram.com/stories/fruits_zipper/3570766765028588805/").unwrap();
        assert_eq!(r.media_type, MediaType::Story);
        assert_eq!(r.username.unwrap(), "fruits_zipper");
        assert_eq!(r.id.unwrap(), "3570766765028588805");
    }

    #[test]
    fn test_highlight() {
        let r = parse_instagram_url("https://www.instagram.com/stories/highlights/18090946048123978/").unwrap();
        assert_eq!(r.media_type, MediaType::Highlight);
        assert_eq!(r.id.unwrap(), "18090946048123978");
    }

    #[test]
    fn test_hashtag() {
        let r = parse_instagram_url("https://instagram.com/explore/tags/lolcats").unwrap();
        assert_eq!(r.media_type, MediaType::Hashtag);
        assert_eq!(r.id.unwrap(), "lolcats");
    }

    #[test]
    fn test_user_profile() {
        let r = parse_instagram_url("https://instagram.com/porsche").unwrap();
        assert_eq!(r.media_type, MediaType::UserProfile);
        assert_eq!(r.username.unwrap(), "porsche");
    }

    #[test]
    fn test_ios_media() {
        let r = parse_instagram_url("instagram://media?id=482584233761418119").unwrap();
        assert_eq!(r.media_type, MediaType::IosMedia);
        assert_eq!(r.id.unwrap(), "482584233761418119");
    }

    #[test]
    fn test_username_with_reel_subpath() {
        let r = parse_instagram_url("https://www.instagram.com/marvelskies.fc/reel/CWqAgUZgCku/").unwrap();
        assert_eq!(r.media_type, MediaType::Reel);
        assert_eq!(r.id.unwrap(), "CWqAgUZgCku");
    }

    #[test]
    fn test_invalid_url() {
        assert!(parse_instagram_url("https://example.com").is_none());
        assert!(parse_instagram_url("not a url").is_none());
    }
}
