'use strict';

const chai = require('chai');

const expect = chai.expect;
const Support = require('../support');
const { DataTypes } = require('@sequelize/core');

describe(Support.getTestDialectTeaser('Hooks'), () => {
  beforeEach(async function () {
    this.User = this.sequelize.define('User', {
      username: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      mood: {
        type: DataTypes.ENUM(['happy', 'sad', 'neutral']),
      },
    });

    await this.sequelize.sync({ force: true });
  });

  describe('#find', () => {
    beforeEach(async function () {
      await this.User.bulkCreate([
        { username: 'adam', mood: 'happy' },
        { username: 'joe', mood: 'sad' },
      ]);
    });

    it('allow changing attributes via beforeFind #5675', async function () {
      this.User.beforeFind(options => {
        options.attributes = {
          include: [['id', 'my_id']],
        };
      });
      await this.User.findAll({});
    });

    describe('on success', () => {
      it('all hooks run', async function () {
        let beforeHook = false;
        let beforeHook2 = false;
        let beforeHook3 = false;
        let afterHook = false;

        this.User.beforeFind(() => {
          beforeHook = true;
        });

        this.User.beforeFindAfterExpandIncludeAll(() => {
          beforeHook2 = true;
        });

        this.User.beforeFindAfterOptions(() => {
          beforeHook3 = true;
        });

        this.User.afterFind(() => {
          afterHook = true;
        });

        const user = await this.User.findOne({ where: { username: 'adam' } });
        expect(user.mood).to.equal('happy');
        expect(beforeHook).to.be.true;
        expect(beforeHook2).to.be.true;
        expect(beforeHook3).to.be.true;
        expect(afterHook).to.be.true;
      });

      it('beforeFind hook can change options', async function () {
        this.User.beforeFind(options => {
          options.where.username = 'joe';
        });

        const user = await this.User.findOne({ where: { username: 'adam' } });
        expect(user.mood).to.equal('sad');
      });

      it('beforeFindAfterExpandIncludeAll hook can change options', async function () {
        this.User.beforeFindAfterExpandIncludeAll(options => {
          options.where.username = 'joe';
        });

        const user = await this.User.findOne({ where: { username: 'adam' } });
        expect(user.mood).to.equal('sad');
      });

      it('beforeFindAfterOptions hook can change options', async function () {
        this.User.beforeFindAfterOptions(options => {
          options.where.username = 'joe';
        });

        const user = await this.User.findOne({ where: { username: 'adam' } });
        expect(user.mood).to.equal('sad');
      });

      it('afterFind hook can change results', async function () {
        this.User.afterFind(user => {
          user.mood = 'sad';
        });

        const user = await this.User.findOne({ where: { username: 'adam' } });
        expect(user.mood).to.equal('sad');
      });
    });

    describe('on error', () => {
      it('in beforeFind hook returns error', async function () {
        this.User.beforeFind(() => {
          throw new Error('Oops!');
        });

        try {
          await this.User.findOne({ where: { username: 'adam' } });
        } catch (error) {
          expect(error.message).to.equal('Oops!');
        }
      });

      it('in beforeFindAfterExpandIncludeAll hook returns error', async function () {
        this.User.beforeFindAfterExpandIncludeAll(() => {
          throw new Error('Oops!');
        });

        try {
          await this.User.findOne({ where: { username: 'adam' } });
        } catch (error) {
          expect(error.message).to.equal('Oops!');
        }
      });

      it('in beforeFindAfterOptions hook returns error', async function () {
        this.User.beforeFindAfterOptions(() => {
          throw new Error('Oops!');
        });

        try {
          await this.User.findOne({ where: { username: 'adam' } });
        } catch (error) {
          expect(error.message).to.equal('Oops!');
        }
      });

      it('in afterFind hook returns error', async function () {
        this.User.afterFind(() => {
          throw new Error('Oops!');
        });

        try {
          await this.User.findOne({ where: { username: 'adam' } });
        } catch (error) {
          expect(error.message).to.equal('Oops!');
        }
      });
    });
  });
});
