use crate::registry::{ServiceRegistration, Heartbeat, ServiceStatus};
use reqwest::Client;
use std::time::Duration;
use tokio::time;
use log::{info, warn, error};
use std::sync::Arc;

pub struct ServiceRegistrar {
    gateway_url: String,
    registration: ServiceRegistration,
    client: Client,
}

impl ServiceRegistrar {
    pub fn new(gateway_url: String, registration: ServiceRegistration) -> Self {
        Self {
            gateway_url,
            registration,
            client: Client::new(),
        }
    }

    /// Registers the service with the gateway.
    /// It will attempt to register multiple times before giving up or returning.
    /// In a real production scenario, you might want this to block until success 
    /// or allow the application to start and register in the background.
    pub async fn register(&self) -> Result<(), reqwest::Error> {
        let url = format!("{}/api/v1/registry/register", self.gateway_url);
        let mut attempt = 0;
        let max_retries = 5;
        let mut delay = Duration::from_secs(2);

        loop {
            attempt += 1;
            info!("Registering service (attempt {}/{})...", attempt, max_retries);
            
            match self.client.post(&url)
                .json(&self.registration)
                .send()
                .await {
                Ok(resp) => {
                    if resp.status().is_success() {
                        info!("Successfully registered service: {}", self.registration.service_id);
                        return Ok(());
                    } else {
                         warn!("Registration failed with status: {}. Attempt: {}", resp.status(), attempt);
                    }
                },
                Err(e) => {
                    warn!("Registration request failed: {}. Attempt: {}", e, attempt);
                }
            }
            
            if attempt >= max_retries {
                 // We stop blocking here. The heartbeat loop will try to recover registration if needed.
                 warn!("Max registration retries reached. Continuing, but service might not be discoverable yet.");
                 // We return Ok to allow the service to start up. 
                 // Returning an error might cause the whole pod to crash loop which might be desired or not.
                 // Given the self-healing design, we can proceed.
                 return Ok(()); 
            }
            
            time::sleep(delay).await;
            delay = std::cmp::min(delay * 2, Duration::from_secs(30));
        }
    }

    /// Helper to register a single time without retries (used by recovery mechanism)
    async fn register_once(&self) -> Result<(), reqwest::Error> {
        let url = format!("{}/api/v1/registry/register", self.gateway_url);
        let resp = self.client.post(&url)
            .json(&self.registration)
            .send()
            .await?;
            
        if !resp.status().is_success() {
            return Err(resp.error_for_status().unwrap_err());
        }
        Ok(())
    }

    /// Starts the background heartbeat loop.
    /// Requires `Arc<Self>` because it will be spawned into a static task.
    pub async fn start_heartbeat_loop(self: Arc<Self>) {
        let mut interval = time::interval(Duration::from_secs(10));
        let heartbeat_url = format!("{}/api/v1/registry/heartbeat", self.gateway_url);
        
        info!("Starting heartbeat loop for service: {}", self.registration.service_id);

        loop {
            interval.tick().await;
            
            let heartbeat = Heartbeat {
                service_id: self.registration.service_id.clone(),
                status: ServiceStatus::Active,
            };

            match self.client.post(&heartbeat_url)
                .json(&heartbeat)
                .send()
                .await {
                Ok(resp) => {
                    // If the Gateway says "I don't know you" (404) or Unauthorized, we re-register.
                    if resp.status() == reqwest::StatusCode::NOT_FOUND || resp.status() == reqwest::StatusCode::UNAUTHORIZED {
                        warn!("Gateway returned {}, indicating registration loss. Re-registering...", resp.status());
                        if let Err(e) = self.register_once().await {
                             error!("Re-registration failed: {}", e);
                        } else {
                             info!("Re-registration successful.");
                        }
                    } else if !resp.status().is_success() {
                        warn!("Heartbeat failed with status: {}", resp.status());
                    }
                },
                Err(e) => {
                    error!("Heartbeat request failed: {}", e);
                }
            }
        }
    }

    pub async fn deregister(&self) -> Result<(), reqwest::Error> {
        let url = format!("{}/api/v1/registry/deregister", self.gateway_url);
        info!("Deregistering service: {}", self.registration.service_id);
        
        let payload = serde_json::json!({
            "service_id": self.registration.service_id
        });

        let _ = self.client.post(&url)
            .json(&payload)
            .send()
            .await?;
            
        Ok(())
    }
}

