export interface PendingProgress {
  chapterId: string;
  segmentId: string;
  offsetMs: number;
  playbackRate: number;
  updatedAt: string;
}

interface StorageLike {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
  removeItem(key: string): void;
}

export function progressOutboxKey(bookId: string): string {
  return `hearbooks:progress:${encodeURIComponent(bookId)}`;
}

function isPendingProgress(value: unknown): value is PendingProgress {
  return Boolean(
    value && typeof value === "object" &&
    "chapterId" in value && typeof value.chapterId === "string" &&
    "segmentId" in value && typeof value.segmentId === "string" &&
    "offsetMs" in value && typeof value.offsetMs === "number" && value.offsetMs >= 0 &&
    "playbackRate" in value && typeof value.playbackRate === "number" && value.playbackRate >= 0.5 && value.playbackRate <= 3 &&
    "updatedAt" in value && typeof value.updatedAt === "string" && Number.isFinite(Date.parse(value.updatedAt)),
  );
}

export class ProgressOutbox {
  constructor(privateStorage: StorageLike) {
    this.storage = privateStorage;
  }

  private readonly storage: StorageLike;

  get(bookId: string): PendingProgress | null {
    const key = progressOutboxKey(bookId);
    try {
      const raw = this.storage.getItem(key);
      if (!raw) return null;
      const value: unknown = JSON.parse(raw);
      if (isPendingProgress(value)) return value;
    } catch {
      // Invalid or unavailable browser storage is treated as an empty outbox.
    }
    this.storage.removeItem(key);
    return null;
  }

  save(bookId: string, progress: PendingProgress): void {
    if (!isPendingProgress(progress)) throw new Error("待同步播放进度无效。");
    const current = this.get(bookId);
    if (current && Date.parse(current.updatedAt) > Date.parse(progress.updatedAt)) return;
    this.storage.setItem(progressOutboxKey(bookId), JSON.stringify(progress));
  }

  clear(bookId: string, throughUpdatedAt: string): void {
    const current = this.get(bookId);
    if (!current || Date.parse(current.updatedAt) <= Date.parse(throughUpdatedAt)) {
      this.storage.removeItem(progressOutboxKey(bookId));
    }
  }
}
