import { LOAN_GARNISHMENT_RATE } from '../../constants.js'
import { carrot } from '../../data/crops/index.js'
import { LOAN_PAYOFF } from '../../templates.js'
import { castToMoney } from '../../utils/castToMoney.js'
import { getKegValue } from '../../utils/getKegValue.js'
import { testState } from '../../test-utils/index.js'

import { sellKeg } from './sellKeg.js'

const stubKeg: farmhand.keg = {
  id: 'stub-keg',
  daysUntilMature: -4,
  itemId: carrot.id,
}

const stubKegValue = getKegValue(stubKeg)

describe('sellKeg', () => {
  test('updates inventory', () => {
    const state = sellKeg(
      testState({
        playerId: 'abc123',
        cellarInventory: [stubKeg],
        cellarItemsSold: {},
        completedAchievements: {},
        itemsSold: {},
        learnedRecipes: {},
        loanBalance: 500,
        money: 1000,
        pendingPeerMessages: [],
        revenue: 0,
        todaysRevenue: 0,
      }),
      stubKeg
    )

    expect(state).toMatchObject({
      cellarInventory: [],
    })
  })

  test('updates sales records', () => {
    const state = sellKeg(
      testState({
        playerId: 'abc123',
        cellarInventory: [stubKeg],
        cellarItemsSold: {},
        completedAchievements: {},
        itemsSold: {},
        learnedRecipes: {},
        loanBalance: 500,
        money: 1000,
        pendingPeerMessages: [],
        revenue: 0,
        todaysRevenue: 0,
      }),
      stubKeg
    )

    expect(state).toMatchObject({
      cellarItemsSold: { carrot: 1 },
      itemsSold: { carrot: 1 },
      pendingPeerMessages: [
        {
          playerId: 'abc123',
          message: 'sold one unit of Fermented Carrot.',
          severity: 'warning',
        },
      ],
    })
  })

  test('applies achievement bonus', () => {
    const state = sellKeg(
      testState({
        playerId: 'abc123',
        cellarInventory: [stubKeg],
        cellarItemsSold: {},
        completedAchievements: { 'i-am-rich-1': true },
        itemsSold: {},
        learnedRecipes: {},
        loanBalance: 500,
        money: 1000,
        pendingPeerMessages: [],
        revenue: 0,
        todaysRevenue: 0,
      }),
      stubKeg
    )

    const adjustedKegValue = stubKegValue * 1.05

    expect(state).toMatchObject({
      loanBalance: castToMoney(500 - stubKegValue * LOAN_GARNISHMENT_RATE),
      money: castToMoney(
        1000 + adjustedKegValue - stubKegValue * LOAN_GARNISHMENT_RATE
      ),
      revenue: castToMoney(
        adjustedKegValue - stubKegValue * LOAN_GARNISHMENT_RATE
      ),
      todaysRevenue: castToMoney(
        adjustedKegValue - stubKegValue * LOAN_GARNISHMENT_RATE
      ),
    })
  })

  test('updates learnedRecipes', () => {
    const state = sellKeg(
      testState({
        playerId: 'abc123',
        cellarInventory: [stubKeg],
        cellarItemsSold: {},
        completedAchievements: {},
        itemsSold: { carrot: 9 },
        learnedRecipes: {},
        loanBalance: 500,
        money: 1000,
        pendingPeerMessages: [],
        revenue: 0,
        todaysRevenue: 0,
      }),
      stubKeg
    )

    expect(state).toMatchObject({
      learnedRecipes: { 'carrot-soup': true },
      itemsSold: { carrot: 10 },
    })
  })

  describe('there is an outstanding loan', () => {
    describe('loan is greater than garnishment', () => {
      test('sale is garnished', () => {
        const state = sellKeg(
          testState({
            playerId: 'abc123',
            cellarInventory: [stubKeg],
            cellarItemsSold: {},
            completedAchievements: {},
            itemsSold: {},
            learnedRecipes: {},
            loanBalance: 500,
            money: 1000,
            pendingPeerMessages: [],
            revenue: 0,
            todaysRevenue: 0,
          }),
          stubKeg
        )

        expect(state).toMatchObject({
          loanBalance: castToMoney(500 - stubKegValue * LOAN_GARNISHMENT_RATE),
          money: castToMoney(
            1000 + stubKegValue - stubKegValue * LOAN_GARNISHMENT_RATE
          ),
          revenue: castToMoney(
            stubKegValue - stubKegValue * LOAN_GARNISHMENT_RATE
          ),
          todaysRevenue: castToMoney(
            stubKegValue - stubKegValue * LOAN_GARNISHMENT_RATE
          ),
        })
      })
    })

    describe('loan is less than garnishment', () => {
      test('loan is payed off', () => {
        const state = sellKeg(
          testState({
            playerId: 'abc123',
            cellarInventory: [stubKeg],
            cellarItemsSold: {},
            completedAchievements: {},
            itemsSold: {},
            learnedRecipes: {},
            loanBalance: 1,
            money: 1000,
            pendingPeerMessages: [],
            revenue: 0,
            todaysRevenue: 0,
            todaysNotifications: [],
          }),
          stubKeg
        )

        expect(state).toMatchObject({
          loanBalance: 0,
        })
      })

      test('sale is reduced by remaining loan balance', () => {
        const state = sellKeg(
          testState({
            playerId: 'abc123',
            cellarInventory: [stubKeg],
            cellarItemsSold: {},
            completedAchievements: {},
            itemsSold: {},
            learnedRecipes: {},
            loanBalance: 1,
            money: 1000,
            pendingPeerMessages: [],
            revenue: 0,
            todaysRevenue: 0,
            todaysNotifications: [],
          }),
          stubKeg
        )

        expect(state).toMatchObject({
          money: castToMoney(1000 + stubKegValue - 1),
          revenue: castToMoney(stubKegValue - 1),
          todaysRevenue: castToMoney(stubKegValue - 1),
        })
      })

      test('payoff notification is shown', () => {
        const state = sellKeg(
          testState({
            playerId: 'abc123',
            cellarInventory: [stubKeg],
            cellarItemsSold: {},
            completedAchievements: {},
            itemsSold: {},
            learnedRecipes: {},
            loanBalance: 1,
            money: 1000,
            pendingPeerMessages: [],
            revenue: 0,
            todaysRevenue: 0,
            todaysNotifications: [],
          }),
          stubKeg
        )

        expect(state).toMatchObject({
          todaysNotifications: [
            { message: LOAN_PAYOFF(), severity: 'success' },
          ],
        })
      })
    })
  })

  describe('there is not an outstanding loan', () => {
    test('sale is not garnished', () => {
      const state = sellKeg(
        testState({
          playerId: 'abc123',
          cellarInventory: [stubKeg],
          cellarItemsSold: {},
          completedAchievements: {},
          itemsSold: {},
          learnedRecipes: {},
          loanBalance: 0,
          money: 1000,
          pendingPeerMessages: [],
          revenue: 0,
          todaysRevenue: 0,
        }),
        stubKeg
      )

      expect(state).toMatchObject({
        cellarInventory: [],
        cellarItemsSold: { carrot: 1 },
        completedAchievements: {},
        itemsSold: { carrot: 1 },
        learnedRecipes: {},
        loanBalance: 0,
        money: castToMoney(1000 + stubKegValue),
        revenue: castToMoney(stubKegValue),
        todaysRevenue: castToMoney(stubKegValue),
      })
    })
  })
})
