import { ValueTransformer } from 'typeorm';

// DBのbigint型をnumber型に変換するためのtransformer
// DBのBigInt型をそのまま扱うと、JSのNumber型の最大値を超えると誤差が発生するため、本来はNumber型に変換すべきではないが、
// 影響範囲を最小限に抑えるため、Number型に変換する。使用するのはAutoIncrementされるIDのみの想定のため、
// Number.MAX_SAFE_INTEGERより大きい値は現実的には発生しない想定で変換する。
export const bigintTransformer: ValueTransformer = {
  from: (value: any): number | null => {
    // valueがnullであればそのまま返す
    if (value === null) {
      return value;
    }
    // valueがnumber型かどうかを判定
    // 利用DBによってはbigint型であってもnumber型で返ってくる場合があるため、number型の場合はそのまま返す(sqliteの場合)
    if (typeof value === 'number') {
      return value;
    }
    // valueが文字列かどうかを判定
    if (typeof value !== 'string') {
      throw new Error(`${value} is not string.`);
    }
    // 数値に変換可能な文字列かどうかを判定
    if (Number.isNaN(parseInt(value))) {
      throw new Error(`${value} is not int.`);
    }

    // 文字列ならbigintに変換
    // valueが整数でない場合は値が丸められてしまうが、TypeORMのEntityの定義上、整数を表す文字列以外はありえないため、少数点は考慮しない
    const bigIntValue = BigInt(value);
    // bigIntValueがNumber.MAX_SAFE_INTEGERより大きいかどうかを判定
    if (bigIntValue > Number.MAX_SAFE_INTEGER) {
      throw new Error(`${value} is greater than ${Number.MAX_SAFE_INTEGER}.`);
    }
    // number型で表現できる整数であればnumber型に変換して返す
    return Number(bigIntValue);
  },
  to: (value: any): string | null | undefined => {
    // valueがnullまたはundefinedであればそのまま返す
    if (value === null || value === undefined) {
      return value;
    }
    // valueがnumber型かどうかを判定
    if (typeof value !== 'number') {
      throw new Error(`${value} is not number.`);
    }

    // valueがNumber.MAX_SAFE_INTEGERより大きいかどうかを判定
    if (value > Number.MAX_SAFE_INTEGER) {
      throw new Error(`value is greater than ${Number.MAX_SAFE_INTEGER}.`);
    }
    // valueが整数かどうかを判定
    if (!Number.isInteger(value)) {
      throw new Error(`${value} is not integer.`);
    }
    return value.toString();
  },
};
