"use client";

import { z } from "zod";

import { StorageKeys } from "@/data/storage/storage_keys";
import { Result, SessionAsyncUtil } from "@/utils";

const MAX_AGE_MS = 30 * 60 * 1000;

export type PendingChatUnlockKind = "private" | "voice" | "image";
export type PendingChatUnlockStage = "auth" | "payment";

const PendingChatUnlockSchema = z.object({
  reason: z.literal("single_message_unlock"),
  messageId: z.string().min(1),
  kind: z.enum(["private", "voice", "image"]),
  returnUrl: z
    .string()
    .min(1)
    .refine(
      (value) => value.startsWith("/") && !value.startsWith("//"),
      "returnUrl must be an internal route",
    ),
  stage: z.enum(["auth", "payment"]),
  createdAt: z.number(),
});

const PendingChatImageReturnSchema = z.object({
  reason: z.literal("image_paywall"),
  messageId: z.string().min(1),
  returnUrl: z
    .string()
    .min(1)
    .refine(
      (value) => value.startsWith("/") && !value.startsWith("//"),
      "returnUrl must be an internal route",
    ),
  createdAt: z.number(),
});

export type PendingChatUnlock = z.output<typeof PendingChatUnlockSchema>;
export type PendingChatImageReturn = z.output<
  typeof PendingChatImageReturnSchema
>;

/**
 * NavigationStorage owns short-lived cross-route sessions.
 *
 * UI / router code should not touch the raw session storage utility directly.
 * Keeping these payloads here makes auth, payment, and chat return flows
 * testable without spreading storage keys across screens.
 */
export class NavigationStorage {
  private constructor() {}

  static async savePendingChatUnlock(input: {
    messageId: string;
    kind: PendingChatUnlockKind;
    returnUrl: string;
    stage: PendingChatUnlockStage;
  }): Promise<void> {
    const payload: PendingChatUnlock = {
      reason: "single_message_unlock",
      messageId: input.messageId,
      kind: input.kind,
      returnUrl: input.returnUrl,
      stage: input.stage,
      createdAt: Date.now(),
    };
    await SessionAsyncUtil.setJson(
      StorageKeys.pendingChatUnlock,
      payload,
      PendingChatUnlockSchema,
    );
  }

  static async consumePendingChatUnlock(): Promise<PendingChatUnlock | null> {
    const result = await SessionAsyncUtil.getJson(
      StorageKeys.pendingChatUnlock,
      PendingChatUnlockSchema,
    );
    await SessionAsyncUtil.remove(StorageKeys.pendingChatUnlock);
    if (Result.isErr(result)) return null;
    return NavigationStorage.parsePendingChatUnlock(result.data);
  }

  static async peekPendingChatUnlock(): Promise<PendingChatUnlock | null> {
    const result = await SessionAsyncUtil.getJson(
      StorageKeys.pendingChatUnlock,
      PendingChatUnlockSchema,
    );
    if (Result.isErr(result)) return null;
    const value = NavigationStorage.parsePendingChatUnlock(result.data);
    if (!value) {
      await SessionAsyncUtil.remove(StorageKeys.pendingChatUnlock);
    }
    return value;
  }

  static async hasPendingChatUnlock(): Promise<boolean> {
    return (await NavigationStorage.peekPendingChatUnlock()) !== null;
  }

  static async clearPendingChatUnlock(): Promise<void> {
    await SessionAsyncUtil.remove(StorageKeys.pendingChatUnlock);
  }

  static async savePendingChatImageReturn(input: {
    messageId: string;
    returnUrl: string;
  }): Promise<void> {
    const payload: PendingChatImageReturn = {
      reason: "image_paywall",
      messageId: input.messageId,
      returnUrl: input.returnUrl,
      createdAt: Date.now(),
    };
    await SessionAsyncUtil.setJson(
      StorageKeys.pendingChatImageReturn,
      payload,
      PendingChatImageReturnSchema,
    );
  }

  static async consumePendingChatImageReturn(): Promise<PendingChatImageReturn | null> {
    const result = await SessionAsyncUtil.getJson(
      StorageKeys.pendingChatImageReturn,
      PendingChatImageReturnSchema,
    );
    await SessionAsyncUtil.remove(StorageKeys.pendingChatImageReturn);
    if (Result.isErr(result)) return null;
    return NavigationStorage.parsePendingChatImageReturn(result.data);
  }

  private static parsePendingChatUnlock(
    value: PendingChatUnlock | null,
  ): PendingChatUnlock | null {
    if (!value) return null;
    if (Date.now() - value.createdAt > MAX_AGE_MS) return null;
    return value;
  }

  private static parsePendingChatImageReturn(
    value: PendingChatImageReturn | null,
  ): PendingChatImageReturn | null {
    if (!value) return null;
    if (Date.now() - value.createdAt > MAX_AGE_MS) return null;
    return value;
  }
}
