"use client";

import { Result, type Result as ResultT } from "@/utils/result";
import { SpAsyncUtil } from "@/utils/storage";

import { StorageKeys } from "./storage_keys";

const CURRENT_STORAGE_MIGRATION_VERSION = 1;

const LEGACY_FACEBOOK_ID_KEY = "facebook_id";
const LEGACY_PWA_DIALOG_SHOWN_KEYS = [
  "last_pwa_dialog_shown",
  "cozsweet:lastPwaDialogShown",
];

/**
 * Runs app startup migrations for persistent key-value storage.
 *
 * Migrations are intentionally idempotent: copying only fills missing new keys,
 * and legacy keys are removed after each attempt.
 */
export async function runStorageMigrations(): Promise<ResultT<void>> {
  const versionResult = await SpAsyncUtil.getInt(
    StorageKeys.storageMigrationVersion,
  );
  if (!versionResult.success) return versionResult;
  if ((versionResult.data ?? 0) >= CURRENT_STORAGE_MIGRATION_VERSION) {
    return Result.ok(undefined);
  }

  const migrationResult = await migrateToV1();
  if (!migrationResult.success) return migrationResult;

  return SpAsyncUtil.setInt(
    StorageKeys.storageMigrationVersion,
    CURRENT_STORAGE_MIGRATION_VERSION,
  );
}

async function migrateToV1(): Promise<ResultT<void>> {
  const asidResult = await migrateStringKey({
    legacyKey: LEGACY_FACEBOOK_ID_KEY,
    newKey: StorageKeys.asid,
  });
  if (!asidResult.success) return asidResult;

  return migratePwaDialogShown();
}

async function migrateStringKey(input: {
  legacyKey: string;
  newKey: string;
}): Promise<ResultT<void>> {
  const current = await SpAsyncUtil.getString(input.newKey);
  if (!current.success) return current;

  const legacy = await readLegacyString(input.legacyKey);
  if (!legacy.success) return legacy;

  if (!current.data && legacy.data) {
    const writeResult = await SpAsyncUtil.setString(input.newKey, legacy.data);
    if (!writeResult.success) return writeResult;
  }

  return removeLegacyKey(input.legacyKey);
}

async function migratePwaDialogShown(): Promise<ResultT<void>> {
  const current = await SpAsyncUtil.getBool(StorageKeys.pwaDialogShown);
  if (!current.success) return current;

  let wasShown = current.data === true;
  for (const legacyKey of LEGACY_PWA_DIALOG_SHOWN_KEYS) {
    const legacy = await readLegacyString(legacyKey);
    if (!legacy.success) return legacy;
    wasShown = wasShown || legacy.data !== null;
  }

  if (wasShown && current.data !== true) {
    const writeResult = await SpAsyncUtil.setBool(
      StorageKeys.pwaDialogShown,
      true,
    );
    if (!writeResult.success) return writeResult;
  }

  for (const legacyKey of LEGACY_PWA_DIALOG_SHOWN_KEYS) {
    const removeResult = await removeLegacyKey(legacyKey);
    if (!removeResult.success) return removeResult;
  }

  return Result.ok(undefined);
}

async function readLegacyString(key: string): Promise<ResultT<string | null>> {
  const storageResult = await SpAsyncUtil.getString(key);
  if (!storageResult.success) return storageResult;
  if (storageResult.data) return storageResult;

  const browserValue = readBrowserLocalStorage(key);
  if (browserValue !== null && browserValue.length > 0) {
    return Result.ok(browserValue);
  }
  return Result.ok(null);
}

async function removeLegacyKey(key: string): Promise<ResultT<void>> {
  const removeResult = await SpAsyncUtil.remove(key);
  if (!removeResult.success) return removeResult;
  removeBrowserLocalStorage(key);
  return Result.ok(undefined);
}

function readBrowserLocalStorage(key: string): string | null {
  if (typeof window === "undefined") return null;
  try {
    return window.localStorage.getItem(key);
  } catch {
    return null;
  }
}

function removeBrowserLocalStorage(key: string): void {
  if (typeof window === "undefined") return;
  try {
    window.localStorage.removeItem(key);
  } catch {
    // Best-effort cleanup only.
  }
}
