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

import { createEvent } from '../../libs/audit';
import { createIdGenerator } from '../../libs/util';
import type { TPaymentCurrency } from './payment-currency';

export const nextMeterId = createIdGenerator('mtr', 14);

export type TMeterExpanded = TMeter & {
  paymentCurrency: TPaymentCurrency;
};

export class Meter extends Model<InferAttributes<Meter>, InferCreationAttributes<Meter>> {
  declare id: CreationOptional<string>;
  declare object: CreationOptional<string>;
  declare name: string;
  declare event_name: string;
  declare aggregation_method: LiteralUnion<'sum' | 'count' | 'last', string>;
  declare status: LiteralUnion<'active' | 'inactive', string>;
  declare unit: string;
  declare description?: string;
  declare component_did?: string;
  declare livemode: boolean;

  declare metadata?: Record<string, any>;
  declare currency_id?: string;

  // 审计字段
  declare created_by?: string;
  declare updated_by?: string;
  declare created_via: LiteralUnion<'api' | 'dashboard' | 'portal', string>;

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

  public static readonly GENESIS_ATTRIBUTES = {
    id: {
      type: DataTypes.STRING(18),
      primaryKey: true,
      allowNull: false,
      defaultValue: nextMeterId,
    },
    object: {
      type: DataTypes.STRING(32),
      defaultValue: 'meter',
      allowNull: false,
    },
    name: {
      type: DataTypes.STRING(255),
      allowNull: false,
    },
    event_name: {
      type: DataTypes.STRING(128),
      allowNull: false,
      unique: true,
    },
    aggregation_method: {
      type: DataTypes.ENUM('sum', 'count', 'last'),
      defaultValue: 'sum',
      allowNull: false,
    },
    status: {
      type: DataTypes.ENUM('active', 'inactive'),
      defaultValue: 'active',
      allowNull: false,
    },
    unit: {
      type: DataTypes.STRING(32),
      allowNull: false,
    },
    description: {
      type: DataTypes.TEXT,
      allowNull: true,
    },
    component_did: {
      type: DataTypes.STRING(40),
      allowNull: true,
    },
    currency_id: {
      type: DataTypes.STRING(40),
      allowNull: true,
    },
    livemode: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
    },
    metadata: {
      type: DataTypes.JSON,
      allowNull: true,
    },
    created_by: {
      type: DataTypes.STRING(40),
      allowNull: true,
    },
    updated_by: {
      type: DataTypes.STRING(40),
      allowNull: true,
    },
    created_via: {
      type: DataTypes.ENUM('api', 'dashboard', 'portal'),
      allowNull: false,
    },
    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(this.GENESIS_ATTRIBUTES, {
      sequelize,
      modelName: 'Meter',
      tableName: 'meters',
      createdAt: 'created_at',
      updatedAt: 'updated_at',
      indexes: [{ fields: ['event_name'] }, { fields: ['currency_id'] }],
      hooks: {
        afterCreate: (model: Meter, options) => {
          createEvent('Meter', 'meter.created', model, options).catch(console.error);
        },
        afterUpdate: (model: Meter, options) => {
          createEvent('Meter', 'meter.updated', model, options).catch(console.error);
        },
        afterDestroy: (model: Meter, options) => {
          createEvent('Meter', 'meter.deleted', model, options).catch(console.error);
        },
      },
    });
  }

  public static associate(models: any) {
    this.hasMany(models.CreditTransaction, {
      foreignKey: 'meter_id',
      as: 'transactions',
    });

    this.hasOne(models.PaymentCurrency, {
      sourceKey: 'currency_id',
      foreignKey: 'id',
      as: 'paymentCurrency',
    });
  }

  public static getMeterByEventName(eventName: string): Promise<Meter | null> {
    const whereClause: any = { event_name: eventName };
    return this.findOne({ where: whereClause });
  }

  public static async getMetricsForMeter(meterId: string, customerId?: string) {
    // @ts-ignore
    const { CreditTransaction } = this.sequelize.models;
    const whereClause: any = { meter_id: meterId };
    if (customerId) {
      whereClause.customer_id = customerId;
    }

    // @ts-ignore
    const totalUsage = await CreditTransaction.sum('credit_amount', {
      where: whereClause,
    });

    return {
      total_usage: totalUsage || 0,
    };
  }
}

export type TMeter = InferAttributes<Meter>;
