use std::time::{SystemTime, UNIX_EPOCH};

use crate::*;

#[uniffi::export(async_runtime = "tokio")]
impl GotchaCore {
    pub async fn notifications(
        &self,
        status: NotificationStatus,
        page: u32,
    ) -> Result<NotificationListPage, GotchaError> {
        let server = self.server()?;
        let result = load_notifications(
            &server,
            matches!(status, NotificationStatus::Open),
            valid_page(page)?,
        )
        .await?;
        Ok(NotificationListPage {
            rows: notification_rows(&server.credential_account, result.items),
            has_more: result.has_more,
        })
    }

    pub async fn mark_notification_read(
        &self,
        server_id: String,
        id: i64,
    ) -> Result<(), GotchaError> {
        let server = self.server_by_id(&server_id)?;
        mark_notification_read(&server, id)
            .await
            .map_err(Into::into)
    }

    pub async fn poll_notifications(&self) -> Result<Vec<NotificationRow>, GotchaError> {
        let servers = {
            let state = self.state.lock().unwrap();
            if !state.preferences.notifications_enabled {
                return Ok(Vec::new());
            }
            state
                .preferences
                .servers
                .iter()
                .cloned()
                .map(|server| {
                    let cursor = state
                        .preferences
                        .notification_cursors
                        .get(&server.credential_account)
                        .cloned();
                    (server, cursor)
                })
                .collect::<Vec<_>>()
        };
        let polled_at = gotcha_gitea::api_timestamp(
            SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_default()
                .as_secs() as i64,
        );
        let mut rows = Vec::new();
        let mut cursors = Vec::new();
        let mut first_error = None;
        for (server, cursor) in servers {
            let notifications = match load_notification_updates(&server, cursor.as_deref()).await {
                Ok(notifications) => notifications,
                Err(error) => {
                    first_error.get_or_insert(error);
                    continue;
                }
            };
            let next_cursor = notification_cursor(&polled_at, &notifications);
            if cursor.is_some() {
                rows.extend(notification_rows(&server.credential_account, notifications));
            }
            cursors.push((server.credential_account, next_cursor));
        }

        if cursors.is_empty()
            && let Some(error) = first_error
        {
            return Err(error.into());
        }

        let mut state = self.state.lock().unwrap();
        if !state.preferences.notifications_enabled {
            return Ok(Vec::new());
        }
        for (server_id, cursor) in cursors {
            if state
                .preferences
                .servers
                .iter()
                .any(|server| server.credential_account == server_id)
            {
                state
                    .preferences
                    .notification_cursors
                    .insert(server_id, cursor);
            }
        }
        save_preferences(&state.preferences)?;
        Ok(rows)
    }
}

fn notification_cursor(
    polled_at: &str,
    notifications: &[gotcha_gitea::models::NotificationThread],
) -> String {
    notifications
        .iter()
        .filter_map(|notification| notification.updated_at.as_deref())
        .fold(polled_at.into(), |cursor, updated| {
            cursor.max(updated.into())
        })
}

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

    #[test]
    fn cursor_does_not_skip_an_update_that_arrived_during_polling() {
        let notifications = vec![gotcha_gitea::models::NotificationThread {
            updated_at: Some("2026-08-15T10:00:01Z".into()),
            ..Default::default()
        }];

        assert_eq!(
            notification_cursor("2026-08-15T10:00:00Z", &notifications),
            "2026-08-15T10:00:01Z"
        );
    }
}
