import merge from 'lodash/merge';
import pick from 'lodash/pick';

import { blocklet } from '../../libs/auth';
import logger from '../../libs/logger';
import {
  CheckoutSession,
  PaymentLink,
  Price,
  PricingTable,
  Product,
  Subscription,
  TLineItemExpanded,
} from '../../store/models';

export async function checkPassportForPaymentLink(doc: PaymentLink) {
  // @ts-ignore
  const items: TLineItemExpanded[] = await Price.expand(doc.line_items);
  const item = items.find((x) => x.price.product?.metadata?.passport);
  return item?.price.product?.metadata?.passport;
}

export async function checkPassportForPricingTable(doc: PricingTable) {
  const products = await Product.findAll({ where: { id: doc?.items.map((x) => x.product_id) } });
  const product = products.find((x) => x.metadata?.passport);
  return product?.metadata?.passport;
}

export async function ensurePassportIssued(checkoutSession: CheckoutSession) {
  if (checkoutSession.status !== 'complete') {
    logger.warn('checkoutSession not complete', pick(checkoutSession, ['id', 'status']));
    return;
  }
  const info = pick(checkoutSession, ['id', 'metadata']);
  if (!checkoutSession.customer_did) {
    logger.warn('checkoutSession have no customer did', info);
    return;
  }
  if (!checkoutSession.metadata?.passport) {
    logger.warn('checkoutSession not contain any passport issue info', info);
    return;
  }

  // eslint-disable-next-line @typescript-eslint/naming-convention
  const { passport, pricing_table_id } = checkoutSession.metadata as any;
  const { role } = await blocklet.getRole(passport);
  if (!role) {
    logger.warn('role not found for passport issuance', info);
    return;
  }

  if (!role.extra?.acquire?.pay || !role.extra?.payment?.product) {
    logger.warn('role config not valid for passport issuance', { ...info, role });
    return;
  }

  const productId = role.extra.payment.product;

  // pre issuance check
  if (pricing_table_id) {
    if (role.extra?.acquire?.pay !== pricing_table_id) {
      logger.warn('source pricing table not match wth role config', merge(info, { role }));
      return;
    }

    const doc = await PricingTable.findByPk(pricing_table_id);
    if (!doc) {
      logger.warn('source pricing table not found for passport issuance', info);
      return;
    }

    if (doc.items.some((x) => x.product_id === productId) === false) {
      logger.warn('source pricing table not contain product for passport issuance', { ...info, productId });
      return;
    }
  } else if (checkoutSession.payment_link_id) {
    if (role.extra?.acquire?.pay !== checkoutSession.payment_link_id) {
      logger.warn('source payment link not match wth role config', merge(info, { role }));
      return;
    }

    const doc = await PaymentLink.findByPk(checkoutSession.payment_link_id);
    if (!doc) {
      logger.warn('source payment link not found for passport issuance', info);
      return;
    }

    const items = await Price.expand(doc.line_items);
    if (items.some((x) => x.price.product_id === productId) === false) {
      logger.warn('source payment link not contain product for passport issuance', { ...info, productId });
      return;
    }
  } else {
    logger.warn('no payment link or pricing table for passport issuance', info);
  }

  // do the issuance
  await blocklet.issuePassportToUser({ userDid: checkoutSession.customer_did, role: passport });
  logger.info(
    'passport issued to user on checkout session complete',
    merge(info, { role, customer: checkoutSession.customer_did })
  );
}

export async function ensurePassportRevoked(subscription: Subscription) {
  const info = pick(subscription, ['id', 'status']);
  if (subscription.status !== 'canceled') {
    logger.warn('subscription not canceled', info);
    return;
  }

  const checkoutSession = await CheckoutSession.findBySubscriptionId(subscription.id);
  if (!checkoutSession) {
    logger.warn('checkoutSession for subscription not found', info);
    return;
  }

  if (!checkoutSession.metadata?.passport) {
    logger.warn('checkoutSession not contain any passport issue info', info);
    return;
  }

  const { user } = await blocklet.getUser(checkoutSession.customer_did as string, { enableConnectedAccount: true });
  if (!user) {
    logger.warn(
      'user not found when revoke on subscription cancel',
      merge(info, { did: checkoutSession.customer_did })
    );
    return;
  }

  const { passport } = checkoutSession.metadata as any;
  const passports = user.passports.filter((x: any) => x.role === passport && x.status === 'valid');
  await Promise.all(passports.map((x: any) => blocklet.revokeUserPassport({ userDid: user.did, passportId: x.id })));
  logger.warn(
    'user passports revoked on subscription cancel',
    merge(info, { did: checkoutSession.customer_did, passports })
  );
}

export async function updatePassportExtra(name: string, updates: any) {
  const { role } = await blocklet.getRole(name);
  if (!role) {
    throw new Error(`passport ${name} not found`);
  }
  const result = await blocklet.updateRole(name, { extra: JSON.stringify(merge(role.extra, updates)) });
  return result.role;
}
