use std::cell::RefCell;
use std::time::{Duration, Instant};

use crate::api::ApiClient;

pub trait RemoteEndpoint {
    fn get_remote_url(&self) -> crate::Result<String>;
}

pub struct RemoteJobStorageEndpoint {
    job_uuid: String,
    job_auth_token: String,
    storage_type: String,
    presigned_url: RefCell<Option<String>>,
    fetched_at: RefCell<Option<Instant>>,
}

// 8-minute TTL matching Python client's RemoteJobStorageEndpoint
const PRESIGNED_URL_TTL: Duration = Duration::from_secs(8 * 60);

impl RemoteJobStorageEndpoint {
    pub fn new(job_uuid: String, job_auth_token: String, storage_type: String) -> Self {
        Self {
            job_uuid,
            job_auth_token,
            storage_type,
            presigned_url: RefCell::new(None),
            fetched_at: RefCell::new(None),
        }
    }
}

impl RemoteEndpoint for RemoteJobStorageEndpoint {
    fn get_remote_url(&self) -> crate::Result<String> {
        let needs_refresh = {
            let url = self.presigned_url.borrow();
            let fetched = self.fetched_at.borrow();
            url.is_none() || fetched.is_none_or(|t| t.elapsed() > PRESIGNED_URL_TTL)
        };

        if needs_refresh {
            let config = crate::Config::load();
            let mut client = ApiClient::new(&config)?;

            let mut last_err = None;
            for attempt in 0..3 {
                match client.get_job_storage_download_url(
                    &self.job_uuid,
                    &self.job_auth_token,
                    &self.storage_type,
                ) {
                    Ok(url) => {
                        crate::logging::debug(&format!(
                            "Job \"{}\" fetched presigned URL (TTL {}s)",
                            self.job_uuid,
                            PRESIGNED_URL_TTL.as_secs()
                        ));
                        *self.presigned_url.borrow_mut() = Some(url);
                        *self.fetched_at.borrow_mut() = Some(Instant::now());
                        last_err = None;
                        break;
                    }
                    Err(err) => {
                        crate::logging::warning(&format!(
                            "Failed to fetch presigned URL for job \"{}\" (attempt {}): {}",
                            self.job_uuid,
                            attempt + 1,
                            err
                        ));
                        last_err = Some(err);
                        std::thread::sleep(Duration::from_secs(3 * (attempt + 1)));
                    }
                }
            }
            if let Some(err) = last_err {
                return Err(err);
            }
        }

        Ok(self.presigned_url.borrow().clone().unwrap())
    }
}
