import { DataSource } from "typeorm";
import { licenseAutoAllocationProcessing } from "../functions/licenseAutoAllocation";
import {
  LICENSE_ALLOCATED_STATUS,
  LICENSE_TYPE,
  USER_ROLES,
} from "../constants";
import { DateWithDayEndTime } from "../common/types/types";
import {
  makeTestAccount,
  createLicense,
  makeTestUser,
  selectLicenseByAllocatedUser,
  selectLicenseAllocationHistory,
} from "./common/utility";
import * as dotenv from "dotenv";
import { InvocationContext } from "@azure/functions";

describe("licenseAutoAllocation", () => {
  dotenv.config({ path: ".env" });
  dotenv.config({ path: ".env.local", override: true });
  let source: DataSource | null = null;
  beforeEach(async () => {
    source = new DataSource({
      type: "sqlite",
      database: ":memory:",
      logging: false,
      entities: [__dirname + "/../../**/*.entity{.ts,.js}"],
      synchronize: true, // trueにすると自動的にmigrationが行われるため注意
    });
    return source.initialize();
  });

  afterEach(async () => {
    if (!source) return;
    await source.destroy();
    source = null;
  });

  it("有効期限が本日のライセンスが自動更新されること", async () => {
    if (!source) fail();
    const context = new InvocationContext();

    const currentDateEndTime = new DateWithDayEndTime();

    // アカウント
    const account1 = await makeTestAccount(
      source,
      { tier: 5 },
      { role: `${USER_ROLES.NONE}` }
    );
    const account2 = await makeTestAccount(
      source,
      { tier: 5 },
      { role: `${USER_ROLES.NONE}` }
    );

    // 更新対象のユーザー（3role分）
    const user1 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
    });
    const user2 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.AUTHOR}`,
    });
    const user3 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.TYPIST}`,
    });

    // 更新対象ではないユーザー（まだ有効期限が残っている）
    const user4 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
    });

    // 更新対象ではないユーザー（auto_renewがfalse）
    const user5 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
      auto_renew: false,
    });
    // 更新対象のユーザー（Author二人目）
    const user6 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.AUTHOR}`,
    });
    // 更新対象のユーザー（ただしライセンスが足りない）
    const user7 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
    });

    // 割り当て済みで有効期限が本日のライセンス
    await createLicense(
      source,
      1,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user1.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      2,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user2.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      3,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user3.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      20,
      currentDateEndTime,
      account2.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      account2.admin.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      5,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user5.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      6,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user6.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      7,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user7.id,
      null,
      null,
      null
    );

    // 割り当て済みの更新対象ではないライセンス
    const nextDate = new Date();
    nextDate.setDate(nextDate.getDate() + 1);
    nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
    nextDate.setMilliseconds(0);
    await createLicense(
      source,
      4,
      nextDate,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user4.id,
      null,
      null,
      null
    );

    // 有効期限が先の未割当ライセンスを作成
    // idが100のものは有効期限が当日なので自動割り当て対象外
    // idが101のものから割り当てられる
    for (let i = 0; i < 5; i++) {
      const date = new Date();
      date.setDate(date.getDate() + i);
      date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
      date.setMilliseconds(0);
      await createLicense(
        source,
        i + 100,
        date,
        account1.account.id,
        LICENSE_TYPE.CARD,
        LICENSE_ALLOCATED_STATUS.UNALLOCATED,
        null,
        null,
        null,
        null
      );
    }

    const date = new Date();
    date.setDate(date.getDate() + 30);
    date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
    date.setMilliseconds(0);
    await createLicense(
      source,
      200,
      date,
      account2.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.REUSABLE,
      null,
      null,
      null,
      null
    );

    await licenseAutoAllocationProcessing(context, source);
    const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
    const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
    const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
    const user4Allocated = await selectLicenseByAllocatedUser(source, user4.id);
    const user5Allocated = await selectLicenseByAllocatedUser(source, user5.id);
    const user6Allocated = await selectLicenseByAllocatedUser(source, user6.id);
    const user7Allocated = await selectLicenseByAllocatedUser(source, user7.id);
    const admin2Allocated = await selectLicenseByAllocatedUser(
      source,
      account2.admin.id
    );
    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      user1.id,
      104
    );
    // Author、Typist、Noneの優先順位で割り当てられていることを確認
    expect(user1Allocated.license?.id).toBe(104);
    expect(user2Allocated.license?.id).toBe(101);
    expect(user3Allocated.license?.id).toBe(103);
    // 有効期限がまだあるので、ライセンスが更新されていないことを確認
    expect(user4Allocated.license?.id).toBe(4);
    // auto_renewがfalseなので、ライセンスが更新されていないことを確認
    expect(user5Allocated.license?.id).toBe(5);
    // 複数Authorがいる場合、それぞれに割り当てられていることを確認
    expect(user6Allocated.license?.id).toBe(102);
    // ライセンスが足りない場合、ライセンスが更新されていないことを確認
    expect(user7Allocated.license?.id).toBe(7);
    // 複数アカウント分の処理が正常に行われていることの確認
    expect(admin2Allocated.license?.id).toBe(200);

    // ライセンス割り当て履歴テーブルが更新されていることを確認
    expect(licenseAllocationHistory.licenseAllocationHistory?.user_id).toBe(
      user1.id
    );
    expect(
      licenseAllocationHistory.licenseAllocationHistory?.is_allocated
    ).toBe(true);
    expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe(
      account1.account.id
    );
  });

  it("有効期限が指定日のライセンスが自動更新されること（リトライ用）", async () => {
    if (!source) fail();
    const context = new InvocationContext();
    // 11/22の日付を作成
    const dateSeptember22 = new Date();
    dateSeptember22.setMonth(11);
    dateSeptember22.setDate(22);
    dateSeptember22.setHours(23, 59, 59);
    const currentDateEndTime = new DateWithDayEndTime(dateSeptember22);

    // アカウント
    const account1 = await makeTestAccount(
      source,
      { tier: 5 },
      { role: `${USER_ROLES.NONE}` }
    );
    const account2 = await makeTestAccount(
      source,
      { tier: 5 },
      { role: `${USER_ROLES.NONE}` }
    );

    // 更新対象のユーザー（3role分）
    const user1 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
    });
    const user2 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.AUTHOR}`,
    });
    const user3 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.TYPIST}`,
    });

    // 更新対象ではないユーザー（まだ有効期限が残っている）
    const user4 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
    });

    // 更新対象ではないユーザー（auto_renewがfalse）
    const user5 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
      auto_renew: false,
    });

    // 割り当て済みで有効期限が12/31のライセンス
    await createLicense(
      source,
      1,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user1.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      2,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user2.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      3,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user3.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      20,
      currentDateEndTime,
      account2.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      account2.admin.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      5,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user5.id,
      null,
      null,
      null
    );

    // 割り当て済みの更新対象ではないライセンス
    const nextDate = new Date();
    nextDate.setDate(dateSeptember22.getDate() + 1);
    nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
    nextDate.setMilliseconds(0);
    await createLicense(
      source,
      4,
      nextDate,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user4.id,
      null,
      null,
      null
    );

    // 有効期限が先の未割当ライセンスを作成
    // idが100のものは有効期限が当日なので自動割り当て対象外
    // idが101のものから割り当てられる
    for (let i = 0; i < 10; i++) {
      const date = new Date();
      date.setDate(dateSeptember22.getDate() + i);
      date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
      date.setMilliseconds(0);
      await createLicense(
        source,
        i + 100,
        date,
        account1.account.id,
        LICENSE_TYPE.CARD,
        LICENSE_ALLOCATED_STATUS.UNALLOCATED,
        null,
        null,
        null,
        null
      );
    }

    const dateMarch31 = new Date();
    dateMarch31.setMonth(12);
    dateMarch31.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
    dateMarch31.setMilliseconds(0);
    await createLicense(
      source,
      200,
      dateMarch31,
      account2.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.REUSABLE,
      null,
      null,
      null,
      null
    );

    await licenseAutoAllocationProcessing(context, source, dateSeptember22);
    const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
    const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
    const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
    const user4Allocated = await selectLicenseByAllocatedUser(source, user4.id);
    const user5Allocated = await selectLicenseByAllocatedUser(source, user5.id);
    const admin2Allocated = await selectLicenseByAllocatedUser(
      source,
      account2.admin.id
    );
    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      user1.id,
      103
    );
    // Author、Typist、Noneの優先順位で割り当てられていることを確認
    expect(user1Allocated.license?.id).toBe(103);
    expect(user2Allocated.license?.id).toBe(101);
    expect(user3Allocated.license?.id).toBe(102);
    // 有効期限がまだあるので、ライセンスが更新されていないことを確認
    expect(user4Allocated.license?.id).toBe(4);
    // auto_renewがfalseなので、ライセンスが更新されていないことを確認
    expect(user5Allocated.license?.id).toBe(5);
    // 複数アカウント分の処理が正常に行われていることの確認
    expect(admin2Allocated.license?.id).toBe(200);

    // ライセンス割り当て履歴テーブルが更新されていることを確認
    expect(licenseAllocationHistory.licenseAllocationHistory?.user_id).toBe(
      user1.id
    );
    expect(
      licenseAllocationHistory.licenseAllocationHistory?.is_allocated
    ).toBe(true);
    expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe(
      account1.account.id
    );
  });
  it("新たに割り当てられるライセンスが存在しないため、ライセンスが自動更新されない（エラーではない）", async () => {
    if (!source) fail();
    const context = new InvocationContext();

    const currentDateEndTime = new DateWithDayEndTime();

    // アカウント
    const account1 = await makeTestAccount(
      source,
      { tier: 5 },
      { role: `${USER_ROLES.NONE}` }
    );

    // 更新対象のユーザー（3role分）
    const user1 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
    });
    const user2 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.AUTHOR}`,
    });
    const user3 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.TYPIST}`,
    });

    // 割り当て済みで有効期限が本日のライセンス
    await createLicense(
      source,
      1,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user1.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      2,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user2.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      3,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user3.id,
      null,
      null,
      null
    );

    await licenseAutoAllocationProcessing(context, source);
    const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
    const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
    const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
    // ライセンスが更新されていないことを確認
    expect(user1Allocated.license?.id).toBe(1);
    expect(user2Allocated.license?.id).toBe(2);
    expect(user3Allocated.license?.id).toBe(3);
  });

  it("tier4のアカウントのため、ライセンスが自動更新されない", async () => {
    if (!source) fail();
    const context = new InvocationContext();

    const currentDateEndTime = new DateWithDayEndTime();

    // アカウント
    const account1 = await makeTestAccount(
      source,
      { tier: 4 },
      { role: `${USER_ROLES.NONE}` }
    );

    // 更新対象のユーザー（3role分）
    const user1 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.NONE}`,
    });
    const user2 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.AUTHOR}`,
    });
    const user3 = await makeTestUser(source, {
      account_id: account1.account.id,
      role: `${USER_ROLES.TYPIST}`,
    });

    // 割り当て済みで有効期限が本日のライセンス
    await createLicense(
      source,
      1,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user1.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      2,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user2.id,
      null,
      null,
      null
    );
    await createLicense(
      source,
      3,
      currentDateEndTime,
      account1.account.id,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      user3.id,
      null,
      null,
      null
    );

    // 有効期限が先の未割当ライセンスを作成
    // idが100，101のものは有効期限が当日、翌日なので自動割り当て対象外
    // idが102のものから割り当てられる
    for (let i = 0; i < 10; i++) {
      const date = new Date();
      date.setDate(date.getDate() + i);
      date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
      date.setMilliseconds(0);
      await createLicense(
        source,
        i + 100,
        date,
        account1.account.id,
        LICENSE_TYPE.CARD,
        LICENSE_ALLOCATED_STATUS.UNALLOCATED,
        null,
        null,
        null,
        null
      );
    }

    await licenseAutoAllocationProcessing(context, source);
    const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
    const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
    const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
    // ライセンスが更新されていないことを確認
    expect(user1Allocated.license?.id).toBe(1);
    expect(user2Allocated.license?.id).toBe(2);
    expect(user3Allocated.license?.id).toBe(3);
  });
});
