use clap::Subcommand;
use tail_fin_common::TailFinError;
use tail_fin_twitter::{extract_tweet_id, TimelineType, TwitterApi, TwitterClient};

use crate::session::{
    browser_session, no_mode_error, print_json, print_list, require_browser, twitter_http_client,
    Ctx,
};

/// Construct a `TwitterHttpClient` from either `--cookies` or `--connect`, then run `$body`.
macro_rules! twitter_http_cmd {
    ($ctx:expr, $cmd:expr, $client:ident => $body:expr) => {{
        let $client = if let Some(ref cf) = $ctx.cookies {
            twitter_http_client(cf)?
        } else if let Some(ref host) = $ctx.connect {
            tail_fin_twitter::TwitterHttpClient::from_browser(host).await?
        } else {
            return Err(no_mode_error("twitter", $cmd));
        };
        $body
    }};
}

#[derive(Subcommand)]
pub enum TwitterAction {
    /// Fetch your home timeline
    Timeline {
        #[arg(long, default_value = "following")]
        r#type: String,
        #[arg(long, default_value_t = 20)]
        count: usize,
        #[arg(long)]
        cursor: Option<String>,
    },
    /// Search for tweets
    Search {
        query: String,
        #[arg(long, default_value_t = 20)]
        count: usize,
        #[arg(long)]
        cursor: Option<String>,
    },
    /// Fetch a user's profile
    Profile {
        /// Twitter screen name (without @)
        username: String,
    },
    /// Fetch bookmarked tweets
    Bookmarks {
        #[arg(long, default_value_t = 20)]
        count: usize,
        #[arg(long)]
        cursor: Option<String>,
    },
    /// Fetch liked tweets of a user
    Likes {
        /// Twitter screen name (without @)
        username: String,
        #[arg(long, default_value_t = 20)]
        count: usize,
        #[arg(long)]
        cursor: Option<String>,
    },
    /// Get a tweet thread (original + replies)
    Thread {
        /// Tweet ID or URL
        tweet_id: String,
        #[arg(long, default_value_t = 50)]
        count: usize,
    },
    /// Post a new tweet (browser mode only)
    Post {
        /// Tweet text
        text: String,
    },
    /// Like a tweet (browser mode only)
    Like {
        /// Tweet URL
        url: String,
    },
    /// Follow a user (browser mode only)
    Follow {
        /// Twitter screen name (without @)
        username: String,
    },
    /// Unfollow a user (browser mode only)
    Unfollow {
        /// Twitter screen name (without @)
        username: String,
    },
    /// Delete a tweet (browser mode only)
    Delete {
        /// Tweet URL
        url: String,
    },
    /// Block a user (browser mode only)
    Block {
        /// Twitter screen name (without @)
        username: String,
    },
    /// Unblock a user (browser mode only)
    Unblock {
        /// Twitter screen name (without @)
        username: String,
    },
    /// Bookmark a tweet (browser mode only)
    Bookmark {
        /// Tweet URL
        url: String,
    },
    /// Remove a bookmark (browser mode only)
    Unbookmark {
        /// Tweet URL
        url: String,
    },
    /// Reply to a tweet (browser mode only)
    Reply {
        /// Tweet URL
        url: String,
        /// Reply text
        text: String,
    },
    /// Get trending topics (browser mode only)
    Trending {
        #[arg(long, default_value_t = 20)]
        count: usize,
    },
    /// Get followers of a user
    Followers {
        /// Twitter screen name (without @)
        username: String,
        #[arg(long, default_value_t = 50)]
        count: usize,
    },
    /// Get accounts a user is following
    Following {
        /// Twitter screen name (without @)
        username: String,
        #[arg(long, default_value_t = 50)]
        count: usize,
    },
    /// Get notifications
    Notifications {
        #[arg(long, default_value_t = 20)]
        count: usize,
    },
    /// Download media from a user's tweets (browser mode only)
    Download {
        /// Twitter screen name (without @)
        username: String,
        #[arg(long, default_value_t = 10)]
        count: usize,
    },
    /// Hide a reply on your tweet (browser mode only)
    HideReply {
        /// Reply tweet URL
        url: String,
    },
    /// Export cookies from Chrome for offline use
    ExportCookies {
        /// Output path (default: ~/.tail-fin/twitter-cookies.txt)
        #[arg(long, value_name = "PATH")]
        output: Option<String>,
    },
    /// Fetch a Twitter Article (long-form content) as Markdown
    Article {
        /// Tweet ID or URL
        tweet_id: String,
    },
    /// Fetch your Twitter Lists (browser mode only)
    Lists {
        #[arg(long, default_value_t = 20)]
        count: usize,
    },
    /// Reply to DM conversations (browser mode only)
    ReplyDm {
        /// Message text
        text: String,
        #[arg(long, default_value_t = 10)]
        count: usize,
    },
    /// Accept follow requests (browser mode only, for private accounts)
    Accept {
        /// Comma-separated keywords to filter requests
        #[arg(long)]
        filter: Option<String>,
        #[arg(long, default_value_t = 20)]
        count: usize,
    },
}

pub async fn run(action: TwitterAction, ctx: &Ctx) -> Result<(), TailFinError> {
    match action {
        TwitterAction::ExportCookies { output } => {
            let chrome_host = ctx.connect.as_deref().unwrap_or("127.0.0.1:9222");
            let out_path = output
                .map(std::path::PathBuf::from)
                .unwrap_or_else(|| crate::session::default_cookies_path("twitter"));

            let count = tail_fin_twitter::auth::export_cookies(chrome_host, &out_path).await?;
            eprintln!("Saved {} cookies to {}", count, out_path.display());
        }

        // ── Read commands (both cookie + browser mode) ──
        TwitterAction::Timeline {
            r#type,
            count,
            cursor,
        } => {
            let kind = match r#type.as_str() {
                "for-you" => TimelineType::ForYou,
                "following" => TimelineType::Following,
                other => {
                    return Err(TailFinError::Api(format!(
                        "Unknown timeline type '{other}'. Use 'for-you' or 'following'."
                    )));
                }
            };

            if let Some(ref cookies_flag) = ctx.cookies {
                let client = twitter_http_client(cookies_flag)?;
                let resp = client.timeline(kind, count, cursor.as_deref()).await?;
                print_json(&resp)?;
            } else if let Some(ref host) = ctx.connect {
                let session = browser_session(host, ctx.headed).await?;
                let client = TwitterClient::new(session);
                let resp = client.timeline(kind, count, cursor.as_deref()).await?;
                print_json(&resp)?;
            } else {
                return Err(no_mode_error("twitter", "timeline"));
            }
        }

        TwitterAction::Search {
            query,
            count,
            cursor,
        } => {
            if let Some(ref cookies_flag) = ctx.cookies {
                let client = twitter_http_client(cookies_flag)?;
                let resp = client.search(&query, count, cursor.as_deref()).await?;
                print_json(&resp)?;
            } else if let Some(ref host) = ctx.connect {
                let session = browser_session(host, ctx.headed).await?;
                let client = TwitterClient::new(session);
                let resp = client.search(&query, count, cursor.as_deref()).await?;
                print_json(&resp)?;
            } else {
                return Err(no_mode_error("twitter", "search"));
            }
        }

        TwitterAction::Profile { username } => {
            twitter_http_cmd!(ctx, "profile", client => {
                let profile = client.profile(&username).await?;
                print_json(&profile)?;
            });
        }

        TwitterAction::Bookmarks { count, cursor } => {
            twitter_http_cmd!(ctx, "bookmarks", client => {
                let resp = client.bookmarks(count, cursor.as_deref()).await?;
                print_json(&resp)?;
            });
        }

        TwitterAction::Likes {
            username,
            count,
            cursor,
        } => {
            twitter_http_cmd!(ctx, "likes", client => {
                let resp = client.likes(&username, count, cursor.as_deref()).await?;
                print_json(&resp)?;
            });
        }

        TwitterAction::Thread { tweet_id, count } => {
            let id = extract_tweet_id(&tweet_id);
            twitter_http_cmd!(ctx, "thread", client => {
                let tweets = client.thread(&id, count).await?;
                print_list("tweets", &tweets, tweets.len())?;
            });
        }

        // ── Write commands (browser mode only) ──
        TwitterAction::Post { text } => {
            let host = require_browser(&ctx.connect, "twitter", "post")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).post(&text).await?;
            print_json(&result)?;
        }

        TwitterAction::Like { url } => {
            let host = require_browser(&ctx.connect, "twitter", "like")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).like(&url).await?;
            print_json(&result)?;
        }

        TwitterAction::Follow { username } => {
            let host = require_browser(&ctx.connect, "twitter", "follow")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).follow(&username).await?;
            print_json(&result)?;
        }

        TwitterAction::Unfollow { username } => {
            let host = require_browser(&ctx.connect, "twitter", "unfollow")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).unfollow(&username).await?;
            print_json(&result)?;
        }

        TwitterAction::Delete { url } => {
            let host = require_browser(&ctx.connect, "twitter", "delete")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).delete(&url).await?;
            print_json(&result)?;
        }

        TwitterAction::Block { username } => {
            let host = require_browser(&ctx.connect, "twitter", "block")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).block(&username).await?;
            print_json(&result)?;
        }

        TwitterAction::Unblock { username } => {
            let host = require_browser(&ctx.connect, "twitter", "unblock")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).unblock(&username).await?;
            print_json(&result)?;
        }

        TwitterAction::Bookmark { url } => {
            let host = require_browser(&ctx.connect, "twitter", "bookmark")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).bookmark(&url).await?;
            print_json(&result)?;
        }

        TwitterAction::Unbookmark { url } => {
            let host = require_browser(&ctx.connect, "twitter", "unbookmark")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).unbookmark(&url).await?;
            print_json(&result)?;
        }

        TwitterAction::Reply { url, text } => {
            let host = require_browser(&ctx.connect, "twitter", "reply")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).reply(&url, &text).await?;
            print_json(&result)?;
        }

        TwitterAction::Trending { count } => {
            let host = require_browser(&ctx.connect, "twitter", "trending")?;
            let session = browser_session(&host, ctx.headed).await?;
            let trends = TwitterClient::new(session).trending(count).await?;
            print_list("trends", &trends, trends.len())?;
        }

        TwitterAction::Followers { username, count } => {
            twitter_http_cmd!(ctx, "followers", client => {
                let users = client.followers(&username, count).await?;
                print_list("users", &users, users.len())?;
            });
        }

        TwitterAction::Following { username, count } => {
            twitter_http_cmd!(ctx, "following", client => {
                let users = client.following(&username, count).await?;
                print_list("users", &users, users.len())?;
            });
        }

        TwitterAction::Notifications { count } => {
            twitter_http_cmd!(ctx, "notifications", client => {
                let notifs = client.notifications(count).await?;
                print_list("notifications", &notifs, notifs.len())?;
            });
        }

        TwitterAction::Download { username, count } => {
            let host = require_browser(&ctx.connect, "twitter", "download")?;
            let session = browser_session(&host, ctx.headed).await?;
            let media = TwitterClient::new(session)
                .download(&username, count)
                .await?;
            print_list("media", &media, media.len())?;
        }

        TwitterAction::HideReply { url } => {
            let host = require_browser(&ctx.connect, "twitter", "hide-reply")?;
            let session = browser_session(&host, ctx.headed).await?;
            let result = TwitterClient::new(session).hide_reply(&url).await?;
            print_json(&result)?;
        }

        TwitterAction::Article { tweet_id } => {
            let id = extract_tweet_id(&tweet_id);
            twitter_http_cmd!(ctx, "article", client => {
                let article = client.article(&id).await?;
                print_json(&article)?;
            });
        }

        TwitterAction::Lists { count } => {
            let host = require_browser(&ctx.connect, "twitter", "lists")?;
            let session = browser_session(&host, ctx.headed).await?;
            let lists = TwitterClient::new(session).lists(count).await?;
            print_list("lists", &lists, lists.len())?;
        }

        TwitterAction::ReplyDm { text, count } => {
            let host = require_browser(&ctx.connect, "twitter", "reply-dm")?;
            let session = browser_session(&host, ctx.headed).await?;
            let results = TwitterClient::new(session).reply_dm(&text, count).await?;
            print_list("dm_results", &results, results.len())?;
        }

        TwitterAction::Accept { filter, count } => {
            let host = require_browser(&ctx.connect, "twitter", "accept")?;
            let session = browser_session(&host, ctx.headed).await?;
            let results = TwitterClient::new(session)
                .accept(filter.as_deref(), count)
                .await?;
            print_list("accept_results", &results, results.len())?;
        }
    }
    Ok(())
}

pub struct Adapter;

impl crate::adapter::CliAdapter for Adapter {
    fn name(&self) -> &'static str {
        "twitter"
    }

    fn about(&self) -> &'static str {
        "Twitter/X operations"
    }

    fn command(&self) -> clap::Command {
        <TwitterAction as clap::Subcommand>::augment_subcommands(
            clap::Command::new("twitter").about("Twitter/X operations"),
        )
    }

    fn dispatch<'a>(
        &'a self,
        matches: &'a clap::ArgMatches,
        ctx: &'a crate::session::Ctx,
    ) -> std::pin::Pin<
        Box<
            dyn std::future::Future<Output = Result<(), tail_fin_common::TailFinError>> + Send + 'a,
        >,
    > {
        Box::pin(async move {
            let action = <TwitterAction as clap::FromArgMatches>::from_arg_matches(matches)
                .map_err(|e| tail_fin_common::TailFinError::Api(e.to_string()))?;
            run(action, ctx).await
        })
    }
}
