require "test_helper"

class TransactionsControllerTest < ActionDispatch::IntegrationTest
  include Account::EntriesTestHelper

  setup do
    sign_in @user = users(:family_admin)
    @transaction = account_entries(:transaction)
  end

  test "should get new" do
    get new_transaction_url
    assert_response :success
  end

  test "prefills account_id" do
    get new_transaction_url(account_id: @transaction.account.id)
    assert_response :success
    assert_select "option[selected][value='#{@transaction.account.id}']"
  end

  test "should create transaction" do
    account = @user.family.accounts.first
    entry_params = {
      account_id: account.id,
      amount: 100.45,
      currency: "USD",
      date: Date.current,
      name: "Test transaction",
      entryable_type: "Account::Transaction",
      entryable_attributes: { category_id: categories(:food_and_drink).id }
    }

    assert_difference [ "Account::Entry.count", "Account::Transaction.count" ], 1 do
      post transactions_url, params: { account_entry: entry_params }
    end

    assert_equal entry_params[:amount].to_d, Account::Transaction.order(created_at: :desc).first.entry.amount
    assert_equal "New transaction created successfully", flash[:notice]
    assert_enqueued_with(job: SyncJob)
    assert_redirected_to account_url(account)
  end

  test "expenses are positive" do
    assert_difference([ "Account::Transaction.count", "Account::Entry.count" ], 1) do
      post transactions_url, params: {
        account_entry: {
          nature: "expense",
          account_id: @transaction.account_id,
          amount: @transaction.amount,
          currency: @transaction.currency,
          date: @transaction.date,
          name: @transaction.name,
          entryable_type: "Account::Transaction",
          entryable_attributes: {}
        }
      }
    end

    created_entry = Account::Entry.order(created_at: :desc).first

    assert_redirected_to account_url(@transaction.account)
    assert created_entry.amount.positive?, "Amount should be positive"
  end

  test "incomes are negative" do
    assert_difference("Account::Transaction.count") do
      post transactions_url, params: {
        account_entry: {
          nature: "income",
          account_id: @transaction.account_id,
          amount: @transaction.amount,
          currency: @transaction.currency,
          date: @transaction.date,
          name: @transaction.name,
          entryable_type: "Account::Transaction",
          entryable_attributes: { category_id: categories(:food_and_drink).id }
        }
      }
    end

    created_entry = Account::Entry.order(created_at: :desc).first

    assert_redirected_to account_url(@transaction.account)
    assert created_entry.amount.negative?, "Amount should be negative"
  end

  test "transaction count represents filtered total" do
    family = families(:empty)
    sign_in family.users.first
    account = family.accounts.create! name: "Test", balance: 0, currency: "USD", accountable: Depository.new

    3.times do
      create_transaction(account: account)
    end

    get transactions_url(per_page: 10)

    assert_dom "#total-transactions", count: 1, text: family.entries.account_transactions.size.to_s

    searchable_transaction = create_transaction(account: account, name: "Unique test name")

    get transactions_url(q: { search: searchable_transaction.name })

    # Only finds 1 transaction that matches filter
    assert_dom "#" + dom_id(searchable_transaction), count: 1
    assert_dom "#total-transactions", count: 1, text: "1"
  end

  test "can paginate" do
    family = families(:empty)
    sign_in family.users.first
    account = family.accounts.create! name: "Test", balance: 0, currency: "USD", accountable: Depository.new

    11.times do
      create_transaction(account: account)
    end

    sorted_transactions = family.entries.account_transactions.reverse_chronological.to_a

    assert_equal 11, sorted_transactions.count

    get transactions_url(page: 1, per_page: 10)

    assert_response :success
    sorted_transactions.first(10).each do |transaction|
      assert_dom "#" + dom_id(transaction), count: 1
    end

    get transactions_url(page: 2, per_page: 10)

    assert_dom "#" + dom_id(sorted_transactions.last), count: 1

    get transactions_url(page: 9999999, per_page: 10) # out of range loads last page

    assert_dom "#" + dom_id(sorted_transactions.last), count: 1
  end

  test "can destroy many transactions at once" do
    transactions = @user.family.entries.account_transactions
    delete_count = transactions.size

    assert_difference([ "Account::Transaction.count", "Account::Entry.count" ], -delete_count) do
      post bulk_delete_transactions_url, params: {
        bulk_delete: {
          entry_ids: transactions.pluck(:id)
        }
      }
    end

    assert_redirected_to transactions_url
    assert_equal "#{delete_count} transactions deleted", flash[:notice]
  end

  test "can update many transactions at once" do
    transactions = @user.family.entries.account_transactions

    assert_difference [ "Account::Entry.count", "Account::Transaction.count" ], 0 do
      post bulk_update_transactions_url, params: {
        bulk_update: {
          entry_ids: transactions.map(&:id),
          date: 1.day.ago.to_date,
          category_id: Category.second.id,
          merchant_id: Merchant.second.id,
          notes: "Updated note"
        }
      }
    end

    assert_redirected_to transactions_url
    assert_equal "#{transactions.count} transactions updated", flash[:notice]

    transactions.reload.each do |transaction|
      assert_equal 1.day.ago.to_date, transaction.date
      assert_equal Category.second, transaction.account_transaction.category
      assert_equal Merchant.second, transaction.account_transaction.merchant
      assert_equal "Updated note", transaction.notes
    end
  end
end
