// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { z } from 'zod';
import type { Simplify } from 'type-fest';
import { safeParsePartial } from '../util/schemas.std.ts';
import { DAY } from '../util/durations/index.std.ts';

const SNOOZE_DEFAULT_DURATION_DAYS = 3;
const SNOOZE_DEFAULT_CTA_DATA: RemoteMegaphoneSnoozeCtaType = {
  snoozeDurationDays: [SNOOZE_DEFAULT_DURATION_DAYS],
};
export const SNOOZE_DEFAULT_DURATION = SNOOZE_DEFAULT_DURATION_DAYS * DAY;

export enum MegaphoneType {
  UsernameOnboarding = 'UsernameOnboarding',
  Remote = 'Remote',
}

export type UsernameOnboardingMegaphoneType = {
  type: MegaphoneType.UsernameOnboarding;
};

export type UsernameOnboardingActionableMegaphoneType =
  UsernameOnboardingMegaphoneType & {
    onLearnMore: () => void;
    onDismiss: () => void;
  };

export type VisibleRemoteMegaphoneType = Simplify<
  Omit<RemoteMegaphoneType, 'primaryCtaId' | 'secondaryCtaId'> & {
    primaryCtaId: MegaphoneCtaId | null;
    secondaryCtaId: MegaphoneCtaId | null;
  }
>;

export type RemoteMegaphoneDisplayType = Simplify<
  Readonly<
    Pick<
      VisibleRemoteMegaphoneType,
      | 'title'
      | 'body'
      | 'imagePath'
      | 'primaryCtaId'
      | 'secondaryCtaId'
      | 'primaryCtaText'
      | 'secondaryCtaText'
    > & {
      type: MegaphoneType.Remote;
      remoteMegaphoneId: RemoteMegaphoneId;
    }
  >
>;

export type RemoteActionableMegaphoneType = RemoteMegaphoneDisplayType & {
  onInteractWithMegaphone: (
    megaphoneId: RemoteMegaphoneId,
    ctaId: MegaphoneCtaId
  ) => void;
};

export type AnyActionableMegaphone =
  | UsernameOnboardingActionableMegaphoneType
  | RemoteActionableMegaphoneType;

export type RemoteMegaphoneId = string & { RemoteMegaphoneId: never }; // uuid

export enum MegaphoneCtaId {
  Donate = 'donate',
  Finish = 'finish',
  Snooze = 'snooze',
}

export const RemoteMegaphoneSnoozeCtaSchema = z.object({
  snoozeDurationDays: z.array(z.number()),
});

export type RemoteMegaphoneSnoozeCtaType = z.infer<
  typeof RemoteMegaphoneSnoozeCtaSchema
>;

const RemoteMegaphoneUnknownCtaDataSchema = z.record(z.string(), z.any());

export const RemoteMegaphoneCtaDataSchema = z.union([
  RemoteMegaphoneSnoozeCtaSchema,
  RemoteMegaphoneUnknownCtaDataSchema,
]);

export const RemoteMegaphoneSchema = z.object({
  // Base remote note
  id: z.intersection(z.string(), z.custom<RemoteMegaphoneId>()),
  desktopMinVersion: z.string().nullable(),
  priority: z.number(),
  dontShowBeforeEpochMs: z.number().int().positive(),
  dontShowAfterEpochMs: z.number().int().positive(),
  showForNumberOfDays: z.number().int().positive(),
  primaryCtaId: z.string().nullable(),
  secondaryCtaId: z.string().nullable(),
  primaryCtaData: RemoteMegaphoneCtaDataSchema.nullable(),
  secondaryCtaData: RemoteMegaphoneCtaDataSchema.nullable(),
  conditionalId: z.string().nullable(),
  // Locale specific remote note
  title: z.string(),
  body: z.string(),
  primaryCtaText: z.string().nullable(),
  secondaryCtaText: z.string().nullable(),
  imagePath: z.string(),
  // Local state
  localeFetched: z.string(),
  shownAt: z.number().int().positive().nullable(),
  snoozedAt: z.number().int().positive().nullable(),
  snoozeCount: z.number().int().positive(),
  isFinished: z.boolean(),
});

export type RemoteMegaphoneType = z.infer<typeof RemoteMegaphoneSchema>;

function getMegaphoneSnoozeConfig(
  megaphone: RemoteMegaphoneType
): RemoteMegaphoneSnoozeCtaType {
  let parseableCtaData;

  if (megaphone.primaryCtaId === 'snooze') {
    parseableCtaData = megaphone.primaryCtaData;
  } else if (megaphone.secondaryCtaId === 'snooze') {
    parseableCtaData = megaphone.secondaryCtaData;
  } else {
    throw new Error('primaryCtaId or secondaryCtaId must be snooze');
  }

  const result = safeParsePartial(
    RemoteMegaphoneSnoozeCtaSchema,
    parseableCtaData ?? {}
  );
  return result.success ? result.data : SNOOZE_DEFAULT_CTA_DATA;
}

export function getMegaphoneLastSnoozeDurationMs(
  megaphone: RemoteMegaphoneType
): number {
  const { snoozeDurationDays } = getMegaphoneSnoozeConfig(megaphone);
  const lastSnoozeCount = Math.max(megaphone.snoozeCount - 1, 0);
  const snoozeIndex = Math.min(lastSnoozeCount, snoozeDurationDays.length - 1);
  // oxlint-disable-next-line typescript/no-non-null-assertion
  return snoozeDurationDays[snoozeIndex]! * DAY;
}
