use chrono::{DateTime, Duration, Utc};
use uuid::Uuid;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LeaseRecord {
    pub owner_token: Uuid,
    pub expires_at: DateTime<Utc>,
}

pub fn can_claim(existing: Option<&LeaseRecord>, now: DateTime<Utc>) -> bool {
    match existing {
        Some(lease) => lease.expires_at <= now,
        None => true,
    }
}

pub fn lease_owned_by(
    existing: Option<&LeaseRecord>,
    owner_token: Uuid,
    now: DateTime<Utc>,
) -> bool {
    existing.is_some_and(|lease| lease.owner_token == owner_token && lease.expires_at > now)
}

pub fn renew_lease(
    existing: &LeaseRecord,
    owner_token: Uuid,
    now: DateTime<Utc>,
    ttl: Duration,
) -> Option<LeaseRecord> {
    if existing.owner_token != owner_token || existing.expires_at <= now {
        return None;
    }

    Some(LeaseRecord {
        owner_token,
        expires_at: now + ttl,
    })
}

pub fn can_finalize(existing: Option<&LeaseRecord>, owner_token: Uuid, now: DateTime<Utc>) -> bool {
    lease_owned_by(existing, owner_token, now)
}

#[cfg(test)]
mod tests {
    use super::{LeaseRecord, can_claim, can_finalize, lease_owned_by, renew_lease};
    use chrono::{Duration, Utc};
    use uuid::Uuid;

    #[test]
    fn claim_is_allowed_when_lease_missing_or_expired() {
        let now = Utc::now();
        assert!(can_claim(None, now));

        let old = LeaseRecord {
            owner_token: Uuid::new_v4(),
            expires_at: now - Duration::seconds(1),
        };
        assert!(can_claim(Some(&old), now));

        let active = LeaseRecord {
            owner_token: Uuid::new_v4(),
            expires_at: now + Duration::seconds(30),
        };
        assert!(!can_claim(Some(&active), now));
    }

    #[test]
    fn lease_ownership_requires_same_owner_and_unexpired_lease() {
        let now = Utc::now();
        let owner = Uuid::new_v4();
        let lease = LeaseRecord {
            owner_token: owner,
            expires_at: now + Duration::seconds(10),
        };
        assert!(lease_owned_by(Some(&lease), owner, now));
        assert!(!lease_owned_by(Some(&lease), Uuid::new_v4(), now));
        assert!(!lease_owned_by(
            Some(&lease),
            owner,
            now + Duration::seconds(11)
        ));
    }

    #[test]
    fn renew_and_finalize_are_owner_guarded() {
        let now = Utc::now();
        let owner = Uuid::new_v4();
        let lease = LeaseRecord {
            owner_token: owner,
            expires_at: now + Duration::seconds(3),
        };

        let renewed = renew_lease(&lease, owner, now, Duration::seconds(20)).expect("renewed");
        assert!(renewed.expires_at > lease.expires_at);
        assert!(can_finalize(
            Some(&renewed),
            owner,
            now + Duration::seconds(1)
        ));
        assert!(!can_finalize(
            Some(&renewed),
            Uuid::new_v4(),
            now + Duration::seconds(1)
        ));
    }
}
