import { Op } from 'sequelize';
import { CheckoutSession, Invoice, Meter, Setting, Subscription } from '../store/models';
import type { EventType, NotificationSetting } from '../store/models/types';
import logger from './logger';

const notificationSettingKey = 'notification_settings';
const settingIdKey = 'setting_id';

export const getSettingForNotification = async (settingKey: string | null): Promise<NotificationSetting | null> => {
  if (!settingKey) {
    return null;
  }
  try {
    const setting = await Setting.findOne({
      where: {
        type: 'notification',
        [Op.or]: [
          {
            id: settingKey,
          },
          {
            component_did: settingKey,
          },
          {
            mount_location: settingKey,
          },
        ],
      },
    });
    return (setting?.settings || null) as NotificationSetting | null;
  } catch (error) {
    logger.error('getSettingForNotification error', error);
    return null;
  }
};

export function getDirectNotificationSetting(obj: {
  metadata?: Record<string, any>;
  subscription_data?: Record<string, any>;
}): NotificationSetting | null {
  if (obj?.metadata?.[notificationSettingKey]) {
    return obj.metadata[notificationSettingKey];
  }

  if (obj?.subscription_data?.[notificationSettingKey]) {
    return obj.subscription_data[notificationSettingKey];
  }

  return null;
}

export function getSettingIdFromObject(obj: { metadata?: Record<string, any> }): string | null {
  return obj?.metadata?.[settingIdKey] || null;
}

export async function getNotificationSettingFromSubscription(
  subscription: Subscription
): Promise<NotificationSetting | null> {
  if (!subscription) {
    return null;
  }
  const directSetting = getDirectNotificationSetting(subscription);
  if (directSetting) {
    return directSetting;
  }

  const settingId = getSettingIdFromObject(subscription);
  if (settingId) {
    return getSettingForNotification(settingId);
  }

  const checkoutSession = await CheckoutSession.findBySubscriptionId(subscription.id);
  return checkoutSession ? getNotificationSettingFromCheckoutSession(checkoutSession) : null;
}

export async function getNotificationSettingFromCheckoutSession(
  checkoutSession: CheckoutSession
): Promise<NotificationSetting | null> {
  if (!checkoutSession) {
    return null;
  }
  const directSetting = getDirectNotificationSetting(checkoutSession);
  if (directSetting) {
    return directSetting;
  }
  const settingId = getSettingIdFromObject(checkoutSession);
  if (settingId) {
    const settings = await getSettingForNotification(settingId);
    return settings || null;
  }
  return null;
}

export async function getNotificationSettingFromInvoice(invoice: Invoice): Promise<NotificationSetting | null> {
  if (!invoice) {
    return null;
  }
  const directSetting = getDirectNotificationSetting(invoice);
  if (directSetting) {
    return directSetting;
  }

  const settingId = getSettingIdFromObject(invoice);
  if (settingId) {
    return getSettingForNotification(settingId);
  }

  if (invoice.subscription_id) {
    const subscription = await Subscription.findByPk(invoice.subscription_id);
    if (subscription) {
      return getNotificationSettingFromSubscription(subscription);
    }
  }

  if (invoice.checkout_session_id) {
    const checkoutSession = await CheckoutSession.findByPk(invoice.checkout_session_id);
    if (checkoutSession) {
      return getNotificationSettingFromCheckoutSession(checkoutSession);
    }
  }
  return null;
}

export async function getNotificationSettingFromMeter(meter: Meter): Promise<NotificationSetting | null> {
  if (!meter) {
    return null;
  }
  const settingId = getSettingIdFromObject(meter);
  if (settingId) {
    const settings = await getSettingForNotification(settingId);
    return settings || null;
  }
  return null;
}

export type NotificationSettingsProps = {
  subscription?: Subscription;
  invoice?: Invoice;
  checkoutSession?: CheckoutSession;
  meter?: Meter;
};
export async function getNotificationSettings({
  subscription,
  invoice,
  checkoutSession,
  meter,
}: NotificationSettingsProps): Promise<NotificationSetting | null> {
  if (subscription) {
    const settings = await getNotificationSettingFromSubscription(subscription);
    return settings || null;
  }

  if (checkoutSession) {
    const settings = await getNotificationSettingFromCheckoutSession(checkoutSession);
    return settings || null;
  }

  if (invoice) {
    const settings = await getNotificationSettingFromInvoice(invoice);
    return settings || null;
  }

  if (meter) {
    const settings = await getNotificationSettingFromMeter(meter);
    return settings || null;
  }

  return null;
}

export function shouldSendSystemNotification(eventType: string, settings?: NotificationSetting | null): boolean {
  if (!settings) {
    return true;
  }

  const { include_events: includeEvents, exclude_events: excludeEvents, self_handle: selfHandle } = settings;
  if (!selfHandle) {
    // if self_handle is false, then the notification is sent by the system
    return true;
  }
  if (includeEvents && includeEvents.length > 0) {
    const isIncluded = includeEvents.includes(eventType as EventType);
    return !isIncluded;
  }

  if (excludeEvents && excludeEvents.length > 0) {
    const isExcluded = excludeEvents.includes(eventType as EventType);
    return isExcluded;
  }
  return !selfHandle;
}
