use anyhow::Result;
use std::path::Path;

pub trait Fetcher {
    fn fetch(&self, url: &str) -> Result<String>;
}

pub struct UreqFetcher;

impl Fetcher for UreqFetcher {
    fn fetch(&self, url: &str) -> Result<String> {
        let agent = ureq::AgentBuilder::new()
            .timeout(std::time::Duration::from_secs(30))
            .build();
        let body = agent.get(url).call()?.into_string()?;
        Ok(body)
    }
}

pub fn pull_calendar(fetcher: &dyn Fetcher, url: &str, dest: &Path) -> Result<()> {
    let content = fetcher.fetch(url)?;
    std::fs::write(dest, content)?;
    Ok(())
}

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

    struct MockFetcher {
        response: String,
    }

    impl MockFetcher {
        fn new(response: &str) -> Self {
            MockFetcher {
                response: response.to_string(),
            }
        }
    }

    impl Fetcher for MockFetcher {
        fn fetch(&self, _url: &str) -> Result<String> {
            Ok(self.response.clone())
        }
    }

    struct ErrorFetcher;

    impl Fetcher for ErrorFetcher {
        fn fetch(&self, _url: &str) -> Result<String> {
            Err(anyhow::anyhow!("network error"))
        }
    }

    const SAMPLE_ICS: &str = "BEGIN:VCALENDAR\r\n\
        VERSION:2.0\r\n\
        BEGIN:VEVENT\r\n\
        DTSTART:20260223\r\n\
        SUMMARY:Test event\r\n\
        END:VEVENT\r\n\
        END:VCALENDAR\r\n";

    #[test]
    fn test_pull_saves_content_to_file() {
        let dir = tempfile::tempdir().unwrap();
        let dest = dir.path().join("work.ics");
        let fetcher = MockFetcher::new(SAMPLE_ICS);

        pull_calendar(&fetcher, "https://example.com/work.ics", &dest).unwrap();

        let saved = std::fs::read_to_string(&dest).unwrap();
        assert_eq!(saved, SAMPLE_ICS);
    }

    #[test]
    fn test_pull_overwrites_existing_file() {
        let dir = tempfile::tempdir().unwrap();
        let dest = dir.path().join("work.ics");
        std::fs::write(&dest, "old content").unwrap();

        let fetcher = MockFetcher::new(SAMPLE_ICS);
        pull_calendar(&fetcher, "https://example.com/work.ics", &dest).unwrap();

        let saved = std::fs::read_to_string(&dest).unwrap();
        assert_eq!(saved, SAMPLE_ICS);
    }

    #[test]
    fn test_pull_returns_error_on_fetch_failure() {
        let dir = tempfile::tempdir().unwrap();
        let dest = dir.path().join("work.ics");
        let fetcher = ErrorFetcher;

        let result = pull_calendar(&fetcher, "https://example.com/work.ics", &dest);
        assert!(result.is_err());
    }
}
