/* eslint-disable @typescript-eslint/indent */
/* eslint-disable @typescript-eslint/lines-between-class-members */
import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';

import { createIdGenerator } from '../../libs/util';
import type { DiscountAmount } from './types';

export const nextInvoiceItemId = createIdGenerator('ii', 24);

export type InvoiceLineItem = {
  id: string;
};

// @link https://stripe.com/docs/api/invoiceitems
// eslint-disable-next-line prettier/prettier
export class InvoiceItem extends Model<InferAttributes<InvoiceItem>, InferCreationAttributes<InvoiceItem>> {
  declare id: CreationOptional<string>;
  declare livemode: boolean;

  declare amount: string;
  declare quantity: number;
  declare description: string;
  declare period?: {
    start: number;
    end: number;
  };

  declare currency_id: string;
  declare customer_id: string;
  declare price_id: string;
  declare invoice_id: string;
  declare subscription_id?: string;
  declare subscription_item_id?: string;
  declare test_clock_id?: string;
  declare tax_rate_id?: string;

  declare discountable: boolean;
  declare discount_amounts: DiscountAmount[];
  declare discounts: string[];

  declare proration: boolean;
  declare proration_details: {
    credited_items?: {
      invoice_id: string;
      invoice_line_items: string[];
    };
  };

  declare metadata: Record<string, any>;

  // TODO: following fields not supported
  // tax_amounts: any;
  // tax_rates: any;
  // unit_amount_excluding_tax: string;

  declare created_at: CreationOptional<Date>;
  declare updated_at: CreationOptional<Date>;

  public static readonly GENESIS_ATTRIBUTES = {
    id: {
      type: DataTypes.STRING(30),
      primaryKey: true,
      allowNull: false,
      defaultValue: nextInvoiceItemId,
    },
    livemode: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
    },
    amount: {
      type: DataTypes.STRING(32),
      allowNull: false,
    },
    quantity: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    description: {
      type: DataTypes.STRING(256),
      allowNull: false,
    },
    period: {
      type: DataTypes.JSON,
      allowNull: true,
    },
    customer_id: {
      type: DataTypes.STRING(18),
      allowNull: false,
    },
    currency_id: {
      type: DataTypes.STRING(15),
      allowNull: false,
    },
    price_id: {
      type: DataTypes.STRING(30),
      allowNull: false,
    },
    invoice_id: {
      type: DataTypes.STRING(30),
      allowNull: false,
    },
    subscription_id: {
      type: DataTypes.STRING(30),
      allowNull: true,
    },
    subscription_item_id: {
      type: DataTypes.STRING(30),
      allowNull: true,
    },
    tax_rate_id: {
      type: DataTypes.STRING(30),
      allowNull: true,
    },
    discountable: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
    },
    // Dynamically generated?
    discount_amounts: {
      type: DataTypes.JSON,
      defaultValue: [],
    },
    discounts: {
      type: DataTypes.JSON,
      defaultValue: [],
    },
    proration: {
      type: DataTypes.BOOLEAN,
      defaultValue: false,
    },
    proration_details: {
      type: DataTypes.JSON,
      allowNull: true,
    },
    metadata: {
      type: DataTypes.JSON,
      defaultValue: {},
    },
    created_at: {
      type: DataTypes.DATE,
      defaultValue: DataTypes.NOW,
      allowNull: false,
    },
    updated_at: {
      type: DataTypes.DATE,
      defaultValue: DataTypes.NOW,
      allowNull: false,
    },
  };

  public static initialize(sequelize: any) {
    this.init(InvoiceItem.GENESIS_ATTRIBUTES, {
      sequelize,
      modelName: 'InvoiceItem',
      tableName: 'invoice_items',
      createdAt: 'created_at',
      updatedAt: 'updated_at',
    });
  }

  public static associate(models: any) {
    this.hasOne(models.PaymentCurrency, {
      sourceKey: 'currency_id',
      foreignKey: 'id',
      as: 'currency',
    });
    this.hasOne(models.Customer, {
      sourceKey: 'customer_id',
      foreignKey: 'id',
      as: 'customer',
    });
    this.hasOne(models.Price, {
      sourceKey: 'price_id',
      foreignKey: 'id',
      as: 'price',
    });
    this.hasOne(models.Invoice, {
      sourceKey: 'invoice_id',
      foreignKey: 'id',
      as: 'invoice',
    });
    this.hasOne(models.Subscription, {
      sourceKey: 'subscription_id',
      foreignKey: 'id',
      as: 'subscription',
    });
    this.hasOne(models.SubscriptionItem, {
      sourceKey: 'subscription_item_id',
      foreignKey: 'id',
      as: 'subscriptionItem',
    });
    this.hasOne(models.TaxRate, {
      sourceKey: 'tax_rate_id',
      foreignKey: 'id',
      as: 'tax_rate',
    });
  }

  public static async isPriceUsed(priceId: string) {
    const count = await InvoiceItem.count({
      where: { price_id: priceId },
    });

    return count > 0;
  }
}

export type TInvoiceItem = InferAttributes<InvoiceItem>;
