require "test_helper"

class EnableBankingEntry::ProcessorTest < ActiveSupport::TestCase
  setup do
    @family = families(:dylan_family)
    @account = accounts(:depository)
    @enable_banking_item = EnableBankingItem.create!(
      family: @family,
      name: "Test Enable Banking",
      country_code: "DE",
      application_id: "test_app_id",
      client_certificate: "test_cert"
    )
    @enable_banking_account = EnableBankingAccount.create!(
      enable_banking_item: @enable_banking_item,
      name: "N26 Hauptkonto",
      uid: "eb_uid_1",
      currency: "EUR"
    )
    AccountProvider.create!(
      account: @account,
      provider: @enable_banking_account
    )
  end

  test "uses entry_reference as external_id when transaction_id is nil" do
    tx = {
      entry_reference: "31e13269-03fc-11f1-89d2-cd465703551c",
      transaction_id: nil,
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "11.65", currency: "EUR" },
      creditor: { name: "Spar Dankt 3418" },
      credit_debit_indicator: "DBIT",
      status: "BOOK"
    }

    assert_difference "@account.entries.count", 1 do
      EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    end

    entry = @account.entries.find_by!(
      external_id: "enable_banking_31e13269-03fc-11f1-89d2-cd465703551c",
      source: "enable_banking"
    )
    assert_equal 11.65, entry.amount.to_f
    assert_equal "EUR", entry.currency
  end

  test "uses transaction_id as external_id when present" do
    tx = {
      entry_reference: "ref_123",
      transaction_id: "txn_456",
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "25.00", currency: "EUR" },
      creditor: { name: "Amazon" },
      credit_debit_indicator: "DBIT",
      status: "BOOK"
    }

    EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process

    entry = @account.entries.find_by!(external_id: "enable_banking_txn_456", source: "enable_banking")
    assert_equal 25.0, entry.amount.to_f
  end

  test "does not create duplicate when same entry_reference is processed twice" do
    tx = {
      entry_reference: "unique_ref_abc",
      transaction_id: nil,
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "50.00", currency: "EUR" },
      creditor: { name: "Rewe" },
      credit_debit_indicator: "DBIT",
      status: "BOOK"
    }

    assert_difference "@account.entries.count", 1 do
      EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    end

    assert_no_difference "@account.entries.count" do
      EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    end
  end

  test "raises ArgumentError when both transaction_id and entry_reference are nil" do
    tx = {
      transaction_id: nil,
      entry_reference: nil,
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "10.00", currency: "EUR" },
      creditor: { name: "Test" },
      credit_debit_indicator: "DBIT",
      status: "BOOK"
    }

    assert_raises(ArgumentError) do
      EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    end
  end

  test "handles string keys in transaction data" do
    tx = {
      "entry_reference" => "string_key_ref",
      "transaction_id" => nil,
      "booking_date" => Date.current.to_s,
      "transaction_amount" => { "amount" => "15.00", "currency" => "EUR" },
      "creditor" => { "name" => "Lidl" },
      "credit_debit_indicator" => "DBIT",
      "status" => "BOOK"
    }

    assert_difference "@account.entries.count", 1 do
      EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    end

    entry = @account.entries.find_by!(external_id: "enable_banking_string_key_ref", source: "enable_banking")
    assert_equal 15.0, entry.amount.to_f
  end

  test "includes note field in transaction notes alongside remittance_information" do
    tx = {
      entry_reference: "ref_note",
      transaction_id: nil,
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "10.00", currency: "EUR" },
      credit_debit_indicator: "DBIT",
      remittance_information: [ "Facture 2026-001" ],
      note: "Détail comptable interne",
      status: "BOOK"
    }

    EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    entry = @account.entries.find_by!(external_id: "enable_banking_ref_note")
    assert_includes entry.notes, "Facture 2026-001"
    assert_includes entry.notes, "Détail comptable interne"
  end

  test "stores exchange_rate in extra when present" do
    tx = {
      entry_reference: "ref_fx",
      transaction_id: nil,
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "100.00", currency: "EUR" },
      credit_debit_indicator: "DBIT",
      exchange_rate: {
        unit_currency: "USD",
        exchange_rate: "1.0821",
        rate_type: "SPOT",
        instructed_amount: { amount: "108.21", currency: "USD" }
      },
      status: "BOOK"
    }

    EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    entry = @account.entries.find_by!(external_id: "enable_banking_ref_fx")
    eb_extra = entry.transaction&.extra&.dig("enable_banking")
    assert_equal "1.0821", eb_extra["fx_rate"]
    assert_equal "USD", eb_extra["fx_unit_currency"]
    assert_equal "108.21", eb_extra["fx_instructed_amount"]
  end

  test "stores merchant_category_code in extra when present" do
    tx = {
      entry_reference: "ref_mcc",
      transaction_id: nil,
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "25.00", currency: "EUR" },
      credit_debit_indicator: "DBIT",
      merchant_category_code: "5411",
      status: "BOOK"
    }

    EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    entry = @account.entries.find_by!(external_id: "enable_banking_ref_mcc")
    eb_extra = entry.transaction&.extra&.dig("enable_banking")
    assert_equal "5411", eb_extra["merchant_category_code"]
  end

  test "stores pending true in extra for PDNG-tagged transactions" do
    tx = {
      entry_reference: "ref_pdng",
      transaction_id: nil,
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "15.00", currency: "EUR" },
      credit_debit_indicator: "DBIT",
      status: "PDNG",
      _pending: true
    }

    EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    entry = @account.entries.find_by!(external_id: "enable_banking_ref_pdng")
    eb_extra = entry.transaction&.extra&.dig("enable_banking")
    assert_equal true, eb_extra["pending"]
  end

  test "does not add enable_banking extra key when no extra data present" do
    tx = {
      entry_reference: "ref_noextra",
      transaction_id: nil,
      booking_date: Date.current.to_s,
      transaction_amount: { amount: "5.00", currency: "EUR" },
      credit_debit_indicator: "DBIT",
      status: "BOOK"
    }

    EnableBankingEntry::Processor.new(tx, enable_banking_account: @enable_banking_account).process
    entry = @account.entries.find_by!(external_id: "enable_banking_ref_noextra")
    assert_nil entry.transaction&.extra&.dig("enable_banking")
  end
end
