import os from "os";
import path from "path";
import { redirect } from "@remix-run/node";
import fsExtra from "fs-extra";
import md5 from "md5-hex";
import { z } from "zod";
const DiscordMemberSchema = z.object({
  avatarURL: z.string().nullable().optional(),
  displayName: z.string(),
  id: z.string()
});
const TokenSetSchema = z.object({
  access_token: z.string(),
  token_type: z.string(),
  scope: z.string()
});
const PlayerPreferencesSchema = z.object({
  volumeRate: z.number().optional(),
  playbackRate: z.number().optional(),
  autoplay: z.boolean().optional(),
  subtitle: z.object({
    id: z.string().nullable().default(null),
    mode: z.literal("disabled").or(z.literal("hidden")).or(z.literal("showing")).nullable().default("disabled")
  }).optional().default({}),
  muted: z.boolean().optional(),
  theater: z.boolean().optional(),
  defaultView: z.string().optional(),
  activeSidebarTab: z.number().optional()
}).optional().default({});
const PresencePreferencesSchema = z.object({
  optOut: z.boolean()
}).optional().default({ optOut: false });
const AuthInfoSchema = z.object({
  tokenSet: TokenSetSchema,
  email: z.string(),
  name: z.string().nullable().optional()
}).transform((d) => ({ ...d, id: md5(d.email) }));
const DataSchema = z.object({
  onboarding: z.object({ finishedTourVideo: z.boolean() }).optional().default({ finishedTourVideo: false }),
  preferences: z.object({
    player: PlayerPreferencesSchema,
    presence: PresencePreferencesSchema
  }).optional().default({}),
  authInfo: AuthInfoSchema.optional(),
  discordMember: DiscordMemberSchema.optional()
});
const appDir = path.join(os.homedir(), ".kcdshop");
const dbPath = path.join(appDir, "data.json");
async function deleteDb() {
  if (process.env.KCDSHOP_DEPLOYED)
    return null;
  try {
    if (await fsExtra.exists(dbPath)) {
      await fsExtra.remove(dbPath);
    }
  } catch (error) {
    console.error(`Error deleting the database in ${dbPath}`, error);
  }
}
async function readDb() {
  if (process.env.KCDSHOP_DEPLOYED)
    return null;
  try {
    if (await fsExtra.exists(dbPath)) {
      const data = await fsExtra.readJSON(dbPath);
      const db = DataSchema.parse(data);
      return db;
    }
  } catch (error) {
    console.error(
      `Error reading the database in ${dbPath}, moving it to a .bkp file to avoid parsing errors in the future`,
      error
    );
    void fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => {
    });
  }
  return null;
}
async function getAuthInfo() {
  const data = await readDb();
  return data?.authInfo ?? null;
}
async function getUserInfo() {
  const db = await readDb();
  if (!db?.authInfo)
    return null;
  return {
    id: db.authInfo.id,
    name: db?.discordMember?.displayName ?? db.authInfo.name,
    email: db.authInfo.email,
    avatarUrl: db?.discordMember?.avatarURL ?? getGravatar({ email: db.authInfo.email, size: 288 })
  };
}
function getGravatar({ email, size }) {
  const gravatarOptions = new URLSearchParams({
    size: size.toString(),
    default: "identicon"
  });
  const gravatarUrl = `https://www.gravatar.com/avatar/${md5(
    email
  )}?${gravatarOptions.toString()}`;
  return gravatarUrl;
}
async function requireAuthInfo({
  request,
  redirectTo
}) {
  const authInfo = await getAuthInfo();
  if (!authInfo) {
    const requestUrl = new URL(request.url);
    redirectTo = redirectTo === null ? null : redirectTo ?? `${requestUrl.pathname}${requestUrl.search}`;
    const loginParams = redirectTo ? new URLSearchParams({ redirectTo }) : null;
    const loginRedirect = ["/login", loginParams?.toString()].filter(Boolean).join("?");
    throw redirect(loginRedirect);
  }
  return authInfo;
}
async function setAuthInfo({
  tokenSet,
  email = "unknown@example.com",
  name
}) {
  const data = await readDb();
  const authInfo = AuthInfoSchema.parse({ tokenSet, email, name });
  await fsExtra.ensureDir(appDir);
  await fsExtra.writeJSON(dbPath, { ...data, authInfo });
  return authInfo;
}
async function getPreferences() {
  const data = await readDb();
  return data?.preferences ?? null;
}
async function setPlayerPreferences(playerPreferences) {
  const data = await readDb();
  const updatedData = {
    ...data,
    preferences: { ...data?.preferences, player: playerPreferences }
  };
  await fsExtra.ensureDir(appDir);
  await fsExtra.writeJSON(dbPath, updatedData);
  return updatedData.preferences.player;
}
async function setPresencePreferences(presnecePreferences) {
  const data = await readDb();
  const updatedData = {
    ...data,
    preferences: { ...data?.preferences, presence: presnecePreferences }
  };
  await fsExtra.ensureDir(appDir);
  await fsExtra.writeJSON(dbPath, updatedData);
  return updatedData.preferences.presence;
}
async function getDiscordMember() {
  const data = await readDb();
  return data?.discordMember ?? null;
}
async function setDiscordMember(discordMember) {
  const data = await readDb();
  const updatedData = {
    ...data,
    discordMember
  };
  await fsExtra.ensureDir(appDir);
  await fsExtra.writeJSON(dbPath, updatedData);
  return updatedData.discordMember;
}
async function deleteDiscordInfo() {
  const data = await readDb();
  delete data?.discordMember;
  await fsExtra.ensureDir(appDir);
  await fsExtra.writeJSON(dbPath, data);
}
async function readOnboardingData() {
  const data = await readDb();
  return data?.onboarding ?? null;
}
async function updateOnboardingData(onboardingData) {
  const data = await readDb();
  const updatedData = {
    ...data,
    onboarding: { ...data?.onboarding, ...onboardingData }
  };
  await fsExtra.ensureDir(appDir);
  await fsExtra.writeJSON(dbPath, updatedData);
  return updatedData.onboarding;
}
export {
  DiscordMemberSchema,
  PlayerPreferencesSchema,
  deleteDb,
  deleteDiscordInfo,
  getAuthInfo,
  getDiscordMember,
  getPreferences,
  getUserInfo,
  readOnboardingData,
  requireAuthInfo,
  setAuthInfo,
  setDiscordMember,
  setPlayerPreferences,
  setPresencePreferences,
  updateOnboardingData
};
