/* eslint-disable @typescript-eslint/lines-between-class-members */
import pick from 'lodash/pick';
import uniq from 'lodash/uniq';
import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
import type { LiteralUnion } from 'type-fest';

import { createEvent } from '../../libs/audit';
import { createIdGenerator, formatMetadata } from '../../libs/util';
import type { BrandSettings, PricingTableItem } from './types';

export const nextPricingTableId = createIdGenerator('prctbl', 24);

// @link https://stripe.com/docs/api/payment_links/payment_links
export class PricingTable extends Model<InferAttributes<PricingTable>, InferCreationAttributes<PricingTable>> {
  // Unique identifier for the object.
  declare id: CreationOptional<string>;

  // Whether the PricingTable is currently available for purchase.
  declare active: boolean;
  declare livemode: boolean;
  declare locked: boolean;

  declare name: string;

  // The items representing what is being sold. Each line item represents an item being sold. Up to 20 line items are supported.
  declare items: PricingTableItem[];

  declare branding_settings: BrandSettings;

  declare metadata?: Record<string, any>;

  declare created_at: CreationOptional<Date>;
  declare created_via: LiteralUnion<'api' | 'dashboard' | 'portal', string>;
  declare updated_at: CreationOptional<Date>;

  public static readonly GENESIS_ATTRIBUTES = {
    id: {
      type: DataTypes.STRING(42),
      primaryKey: true,
      allowNull: false,
      defaultValue: nextPricingTableId,
    },
    active: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
    },
    livemode: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
    },
    locked: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
    },
    name: {
      type: DataTypes.STRING(255),
      allowNull: true,
    },
    items: {
      type: DataTypes.JSON,
      defaultValue: [],
    },
    branding_settings: {
      type: DataTypes.JSON,
      allowNull: false,
    },
    metadata: {
      type: DataTypes.JSON,
      allowNull: true,
    },
    created_at: {
      type: DataTypes.DATE,
      defaultValue: DataTypes.NOW,
      allowNull: false,
    },
    created_via: {
      type: DataTypes.ENUM('api', 'dashboard', 'portal'),
    },
    updated_at: {
      type: DataTypes.DATE,
      defaultValue: DataTypes.NOW,
      allowNull: false,
    },
  };

  public static initialize(sequelize: any) {
    this.init(PricingTable.GENESIS_ATTRIBUTES, {
      sequelize,
      modelName: 'PricingTable',
      tableName: 'pricing_tables',
      createdAt: 'created_at',
      updatedAt: 'updated_at',
      hooks: {
        afterCreate: (model: PricingTable, options) => {
          createEvent('PricingTable', 'pricing_table.created', model, options).catch(console.error);
        },
        afterUpdate: (model: PricingTable, options) => {
          createEvent('PricingTable', 'pricing_table.updated', model, options).catch(console.error);
        },
        afterDestroy: (model: PricingTable, options) => {
          createEvent('PricingTable', 'pricing_table.deleted', model, options).catch(console.error);
        },
      },
    });
  }

  public static associate() {
    // Do nothing
  }

  public static formatItem(payload: any) {
    const item = Object.assign(
      {
        adjustable_quantity: {
          enabled: false,
          maximum: 1,
          minimum: 0,
        },
        after_completion: {
          type: 'hosted_confirmation',
          hosted_confirmation: {
            custom_message: '',
          },
        },
        allow_promotion_codes: false,
        customer_creation: 'always',
        consent_collection: {
          promotions: 'none',
          terms_of_service: 'none',
        },
        invoice_creation: {
          enabled: true,
        },
        phone_number_collection: {
          enabled: false,
        },
        billing_address_collection: 'auto',
        subscription_data: {
          description: '',
          trial_period_days: 0,
        },
        nft_mint_settings: {
          enabled: false,
          factory: '',
        },
        submit_type: 'auto',
        cross_sell_behavior: 'auto',
      },
      pick(payload, [
        'adjustable_quantity',
        'after_completion',
        'allow_promotion_codes',
        'billing_address_collection',
        'consent_collection',
        'cross_sell_behavior',
        'custom_fields',
        'highlight_text',
        'is_highlight',
        'nft_mint_settings',
        'phone_number_collection',
        'price_id',
        'product_id',
        'submit_type',
        'subscription_data',
      ])
    );

    if (item.adjustable_quantity?.enabled) {
      item.adjustable_quantity.minimum = Number(item.adjustable_quantity?.minimum);
      item.adjustable_quantity.maximum = Number(item.adjustable_quantity?.maximum);
    }
    if (item.after_completion?.type === 'hosted_confirmation') {
      // @ts-ignore
      item.after_completion.redirect = null;
    }
    if (item.after_completion?.type === 'redirect') {
      // @ts-ignore
      item.after_completion.hosted_confirmation = null;
    }

    return item;
  }

  public static format(payload: any) {
    const raw: Partial<PricingTable> = Object.assign(
      {
        branding_settings: {
          background_color: '#ffffff',
          border_style: 'default',
          button_color: '#0074d4',
          font_family: 'default',
        },
      },
      pick(payload, ['name', 'items', 'metadata', 'brand_settings'])
    );

    raw.items = raw.items?.map((x) => PricingTable.formatItem(x));

    if (payload.highlight && payload.highlight_product_id) {
      raw.items?.forEach((x) => {
        if (x.product_id === payload.highlight_product_id) {
          x.is_highlight = x.product_id === payload.highlight_product_id;
          x.highlight_text = payload.highlight_text || 'popular';
        } else {
          x.is_highlight = false;
          x.highlight_text = 'popular';
        }
      });
    }

    raw.metadata = formatMetadata(raw.metadata);

    return raw;
  }

  public async expand() {
    const { Price, Product } = this.sequelize.models;

    const doc = this.toJSON();
    const prices = await Price!.findAll({ where: { id: uniq(doc.items.map((x) => x.price_id)) } });
    const products = await Product!.findAll({ where: { id: uniq(doc.items.map((x) => x.product_id)) } });

    doc.items.forEach((i) => {
      // @ts-ignore
      i.price = prices.find((p) => p.id === i.price_id);
      // @ts-ignore
      i.product = products.find((p) => p.id === i.product_id);
    });

    return doc;
  }
}

export type TPricingTable = InferAttributes<PricingTable>;
