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

import dayjs from '../../libs/dayjs';

// eslint-disable-next-line prettier/prettier
export class Lock extends Model<InferAttributes<Lock>, InferCreationAttributes<Lock>> {
  declare id: CreationOptional<string>;
  declare lock_at: number;
  declare release_at: number;

  declare reason?: 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,
    },
    lock_at: {
      type: DataTypes.INTEGER,
      defaultValue: 0,
    },
    release_at: {
      type: DataTypes.INTEGER,
      defaultValue: -1,
    },
    reason: {
      type: DataTypes.STRING(255),
      allowNull: true,
    },
    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(Lock.GENESIS_ATTRIBUTES, {
      sequelize,
      modelName: 'Lock',
      tableName: 'locks',
      createdAt: 'created_at',
      updatedAt: 'updated_at',
    });
  }

  public static async acquire(id: string, releaseAt: number, reason?: string): Promise<boolean> {
    const now = dayjs().unix();
    const exist = await this.findByPk(id);
    if (exist) {
      if (exist.release_at <= now) {
        await exist.update({ lock_at: now, release_at: releaseAt, reason });
        return true;
      }

      return false;
    }

    await this.create({ id, lock_at: now, release_at: releaseAt, reason });
    return true;
  }

  public static async isLocked(id: string): Promise<boolean> {
    const now = dayjs().unix();
    const exist = await this.findByPk(id);
    return !!exist && exist.release_at > now;
  }

  public static async release(id: string) {
    const exist = await this.findByPk(id);
    if (exist) {
      await exist.update({ release_at: dayjs().unix() });
    }
  }

  public static associate() {}
}

export type TLock = InferAttributes<Lock>;
