use anyhow::{Result, bail};
use std::collections::BTreeSet;

use crate::app::App;
use crate::cli::SyncArgs;
use crate::helpers::{normalize_email, received_email_matches_account, send_desktop_notification};
use crate::models::{AccountRecord, SyncSummary};
use crate::output::{Format, print_success_or};
use crate::resend::ResendClient;

impl App {
    pub fn sync(&self, args: SyncArgs) -> Result<()> {
        let account_filter = args.account.clone();
        let limit = args.limit;
        let watch = args.watch;
        let notify = args.notify;
        let interval = args.interval.unwrap_or(60);

        self.sync_once(account_filter.as_deref(), limit, notify)?;

        if watch {
            loop {
                std::thread::sleep(std::time::Duration::from_secs(interval));
                if matches!(self.format, Format::Human) {
                    eprintln!("polling...");
                }
                self.sync_once(account_filter.as_deref(), limit, notify)?;
            }
        }

        Ok(())
    }

    fn sync_once(&self, account_filter: Option<&str>, limit: usize, notify: bool) -> Result<()> {
        let accounts = if let Some(account) = account_filter {
            vec![self.get_account(&normalize_email(account))?]
        } else {
            self.list_accounts()?
        };
        if accounts.is_empty() {
            bail!("no accounts configured");
        }

        let unique_profiles = accounts
            .iter()
            .map(|account| account.profile_name.clone())
            .collect::<BTreeSet<_>>();

        let mut summary = SyncSummary {
            profiles: unique_profiles.len(),
            sent_messages: 0,
            received_messages: 0,
        };

        for account in accounts {
            let client = self.client_for_profile(&account.profile_name)?;
            summary.sent_messages += self.sync_sent_account(&client, &account, limit)?;
            let (received, new_messages) =
                self.sync_received_account_with_details(&client, &account, limit)?;
            summary.received_messages += received;

            if notify && !new_messages.is_empty() {
                for (from, subject) in &new_messages {
                    send_desktop_notification(
                        &format!("New email to {}", account.email),
                        &format!("From: {}\n{}", from, subject),
                    );
                }
            }
        }

        print_success_or(self.format, &summary, |summary| {
            println!(
                "synced profiles={} sent={} received={}",
                summary.profiles, summary.sent_messages, summary.received_messages
            );
        });

        Ok(())
    }

    pub fn sync_sent_account(
        &self,
        client: &ResendClient,
        account: &AccountRecord,
        page_size: usize,
    ) -> Result<usize> {
        let cursor = self.get_sync_cursor(&account.email, "sent")?;
        let mut after = None;
        let mut newest_cursor = None;
        let mut total = 0usize;

        loop {
            let page = client.list_sent_emails_page(page_size, after.as_deref())?;
            if newest_cursor.is_none() {
                newest_cursor = page.data.first().map(|item| item.id.clone());
            }
            let mut stop = false;
            let mut last_id = None;

            for item in page.data {
                last_id = Some(item.id.clone());
                if cursor.as_deref() == Some(item.id.as_str()) {
                    stop = true;
                    break;
                }
                let from_email = item
                    .from
                    .as_deref()
                    .map(normalize_email)
                    .unwrap_or_default();
                if from_email == account.email {
                    let detail = client.get_sent_email(&item.id)?;
                    self.store_sent_message(account, detail, None, None)?;
                    total += 1;
                }
            }

            if stop || !page.has_more.unwrap_or(false) || last_id.is_none() {
                break;
            }
            after = last_id;
        }

        if let Some(cursor_id) = newest_cursor {
            self.set_sync_cursor(&account.email, "sent", &cursor_id)?;
        }

        Ok(total)
    }

    /// Sync received messages and return (count, Vec<(from, subject)>) for notifications.
    pub fn sync_received_account_with_details(
        &self,
        client: &ResendClient,
        account: &AccountRecord,
        page_size: usize,
    ) -> Result<(usize, Vec<(String, String)>)> {
        let cursor = self.get_sync_cursor(&account.email, "received")?;
        let mut after = None;
        let mut newest_cursor = None;
        let mut total = 0usize;
        let mut new_messages = Vec::new();

        loop {
            let page = client.list_received_emails_page(page_size, after.as_deref())?;
            if newest_cursor.is_none() {
                newest_cursor = page.data.first().map(|item| item.id.clone());
            }
            let mut stop = false;
            let mut last_id = None;

            for item in page.data {
                last_id = Some(item.id.clone());
                if cursor.as_deref() == Some(item.id.as_str()) {
                    stop = true;
                    break;
                }
                let detail = client.get_received_email(&item.id)?;
                if !received_email_matches_account(&detail, &account.email) {
                    continue;
                }
                let from = detail.from.clone().unwrap_or_default();
                let subject = detail.subject.clone().unwrap_or_default();
                let message_id = self.store_received_message(account, detail.clone())?;
                self.store_received_attachments(message_id, &detail.attachments)?;
                new_messages.push((from, subject));
                total += 1;
            }

            if stop || !page.has_more.unwrap_or(false) || last_id.is_none() {
                break;
            }
            after = last_id;
        }

        if let Some(cursor_id) = newest_cursor {
            self.set_sync_cursor(&account.email, "received", &cursor_id)?;
        }

        Ok((total, new_messages))
    }
}
