import storageAvailable from 'storage-available';

export const ONE_DAY_MS = 24 * 60 * 60 * 1000; // 1 day
export const ONE_HOUR_MS = 60 * 60 * 1000; // 1 hour
export const THIRTY_DAYS_MS = 30 * ONE_DAY_MS; // 30 days
export const loadFromLocalStorage = (key: string) => {
  return storageAvailable('localStorage')
    ? localStorage.getItem(key)
    : undefined;
};

export const setInLocalStorage = (key: string, value: string) => {
  if (storageAvailable('localStorage')) {
    localStorage.setItem(key, value);
    return true;
  }
  return false;
};

export const loadJsonFromLocalStorage = ({
  key,
  validateTtl = true,
}: {
  key: string;
  validateTtl?: boolean;
}) => {
  if (!storageAvailable('localStorage')) {
    return undefined;
  }
  const data = JSON.parse(localStorage.getItem(key) || '{}');
  if (
    validateTtl &&
    data.expireTimestamp &&
    // TODO: consider returning a different state if the data is expired
    Date.now() >= data.expireTimestamp
  ) {
    return undefined;
  }
  // TODO: do some sort of validation based on schema?
  return data;
};

export const setJsonInLocalStorage = ({
  key,
  value,
  // TODO: perhaps use versioning to invalidate old data instead of timestamp?
  expireTimestamp,
}: {
  key: string;
  value: object;
  expireTimestamp: number;
}) => {
  if (storageAvailable('localStorage')) {
    localStorage.setItem(key, JSON.stringify({ ...value, expireTimestamp }));
    return true;
  }
  return false;
};

/**
 * Stores the TTL information in localStorage
 * @param key - Storage key to use for the timestamp
 * @param ttlMs - Time in milliseconds before key is considered expired
 */
export function setTimedStorageKey(
  key: string,
  ttlMs: number = ONE_DAY_MS
): void {
  if (!storageAvailable('localStorage')) {
    return;
  }

  // Store both the current timestamp and TTL
  const timestamp = Date.now();
  const data = JSON.stringify({ timestamp, ttlMs });
  localStorage.setItem(key, data);
}

/**
 * Checks if the timed storage key has expired
 * @param key - Storage key to check for the timestamp
 * @returns boolean - true if the timestamp has expired
 */
export function hasTimedStorageKeyExpired(key: string): boolean {
  if (!storageAvailable('localStorage')) {
    return true;
  }

  const data = localStorage.getItem(key);
  if (!data) {
    return true;
  }

  const { timestamp, ttlMs } = JSON.parse(data);
  return Date.now() - timestamp > ttlMs;
}

/**
 * Sets a localStorage key with a 30-day expiration
 * @param key - Storage key to set
 * @param _value - Value to store (unused in this implementation)
 * @returns boolean - true if successfully stored
 */
export function setLocalStorageWith30DayExpiry(
  key: string,
  _value: string
): boolean {
  setTimedStorageKey(key, THIRTY_DAYS_MS);
  return storageAvailable('localStorage');
}

/**
 * Sets a localStorage key with a 30-day expiration and stores a value
 * @param key - Storage key to set
 * @param value - Value to store
 * @returns boolean - true if successfully stored
 */
export function setLocalStorageValueWith30DayExpiry(
  key: string,
  value: string
): boolean {
  if (!storageAvailable('localStorage')) {
    return false;
  }

  const timestamp = Date.now();
  const data = JSON.stringify({
    value,
    timestamp,
    ttlMs: THIRTY_DAYS_MS,
  });
  localStorage.setItem(key, data);
  return true;
}

/**
 * Gets a value from localStorage with 30-day expiration check
 * @param key - Storage key to get
 * @returns string | null - The stored value or null if expired/not found
 */
export function getLocalStorageValueWith30DayExpiry(
  key: string
): string | null {
  if (!storageAvailable('localStorage')) {
    return null;
  }

  const data = localStorage.getItem(key);
  if (!data) {
    return null;
  }

  try {
    const { value, timestamp, ttlMs } = JSON.parse(data);

    // Check if expired
    if (Date.now() - timestamp > ttlMs) {
      localStorage.removeItem(key);
      return null;
    }

    return value;
  } catch (error) {
    // If parsing fails, remove the invalid data
    localStorage.removeItem(key);
    return null;
  }
}
