require "test_helper"

class Transactions::CategorizesControllerTest < ActionDispatch::IntegrationTest
  include EntriesTestHelper

  setup do
    sign_in @user = users(:family_admin)
    @family = @user.family
    @account = accounts(:depository)
    @category = categories(:food_and_drink)
    # Clear entries for isolation
    @family.accounts.each { |a| a.entries.delete_all }
  end

  # GET /transactions/categorize

  test "show redirects with notice when nothing to categorize" do
    get transactions_categorize_url
    assert_redirected_to transactions_url
    assert_match "categorized", flash[:notice]
  end

  test "show renders wizard when uncategorized transactions exist" do
    create_transaction(account: @account, name: "Starbucks")
    get transactions_categorize_url
    assert_response :success
  end

  test "show renders the first group at position 0" do
    2.times { create_transaction(account: @account, name: "Netflix") }
    3.times { create_transaction(account: @account, name: "Starbucks") }

    get transactions_categorize_url(position: 0)

    assert_response :success
    assert_select "h2", text: "Starbucks"
  end

  test "show at position 1 skips first group" do
    3.times { create_transaction(account: @account, name: "Starbucks") }
    2.times { create_transaction(account: @account, name: "Netflix") }

    get transactions_categorize_url(position: 1)

    assert_response :success
    assert_select "h2", text: "Netflix"
  end

  test "show redirects when position exceeds available groups" do
    create_transaction(account: @account, name: "Starbucks")

    get transactions_categorize_url(position: 99)

    assert_redirected_to transactions_url
  end

  test "requires authentication" do
    sign_out
    get transactions_categorize_url
    assert_redirected_to new_session_url
  end

  # Account sharing authorization

  test "show only groups entries from accounts accessible to the user" do
    accessible_account = accounts(:depository)       # shared with family_member (full_control)
    inaccessible_account = accounts(:investment)     # not shared with family_member

    create_transaction(account: accessible_account, name: "Starbucks")
    create_transaction(account: inaccessible_account, name: "Starbucks")

    sign_in users(:family_member)
    get transactions_categorize_url(position: 0)

    assert_response :success
    # Only 1 entry should appear in the group — the inaccessible account's entry is hidden
    assert_select "input[name='entry_ids[]']", count: 1
  end

  test "create does not categorize entries from inaccessible accounts" do
    inaccessible_account = accounts(:investment)     # not shared with family_member
    entry = create_transaction(account: inaccessible_account, name: "Starbucks")

    sign_in users(:family_member)
    post transactions_categorize_url,
      params: {
        position: 0,
        grouping_key: "Starbucks",
        entry_ids: [ entry.id ],
        all_entry_ids: [ entry.id ],
        category_id: @category.id
      },
      headers: { "Accept" => "text/vnd.turbo-stream.html" }

    assert_nil entry.transaction.reload.category
  end

  test "assign_entry does not categorize an entry from an inaccessible account" do
    inaccessible_account = accounts(:investment)     # not shared with family_member
    entry = create_transaction(account: inaccessible_account, name: "Starbucks")

    sign_in users(:family_member)
    patch assign_entry_transactions_categorize_url, params: {
      entry_id: entry.id,
      category_id: @category.id,
      position: 0,
      all_entry_ids: [ entry.id ]
    }

    assert_response :not_found
    assert_nil entry.transaction.reload.category
  end

  # GET /transactions/categorize/preview_rule

  test "preview_rule returns matching entries for a filter" do
    create_transaction(account: @account, name: "Amazon Prime")
    create_transaction(account: @account, name: "Amazon Music")
    create_transaction(account: @account, name: "Starbucks")

    get preview_rule_transactions_categorize_url(filter: "Amazon"),
      headers: { "Accept" => "text/vnd.turbo-stream.html" }

    assert_response :success
    assert_includes response.body, "Amazon Prime"
    assert_includes response.body, "Amazon Music"
    assert_not_includes response.body, "Starbucks"
  end

  test "preview_rule returns empty list for blank filter" do
    create_transaction(account: @account, name: "Amazon")

    get preview_rule_transactions_categorize_url(filter: ""),
      headers: { "Accept" => "text/vnd.turbo-stream.html" }

    assert_response :success
    assert_not_includes response.body, "Amazon"
  end

  test "preview_rule requires authentication" do
    sign_out
    get preview_rule_transactions_categorize_url(filter: "Amazon")
    assert_redirected_to new_session_url
  end

  private

    def sign_out
      @user.sessions.each { |s| delete session_path(s) }
    end

    # POST /transactions/categorize

    test "create categorizes selected entries and returns redirect stream when all assigned" do
      entry = create_transaction(account: @account, name: "Starbucks")

      post transactions_categorize_url,
        params: {
          position: 0,
          grouping_key: "Starbucks",
          entry_ids: [ entry.id ],
          all_entry_ids: [ entry.id ],
          category_id: @category.id
        },
        headers: { "Accept" => "text/vnd.turbo-stream.html" }

      assert_response :success
      assert_equal @category, entry.transaction.reload.category
      assert_includes response.body, "action=\"redirect\""
    end

    test "create removes assigned rows and replaces remaining when partial assignment" do
      entry1 = create_transaction(account: @account, name: "Starbucks")
      entry2 = create_transaction(account: @account, name: "Starbucks")

      post transactions_categorize_url,
        params: {
          position: 0,
          grouping_key: "Starbucks",
          entry_ids: [ entry1.id ],
          all_entry_ids: [ entry1.id, entry2.id ],
          category_id: @category.id
        },
        headers: { "Accept" => "text/vnd.turbo-stream.html" }

      assert_response :success
      assert_equal @category, entry1.transaction.reload.category
      assert_nil entry2.transaction.reload.category
      # Remove stream for categorized entry
      assert_includes response.body, "categorize_entry_#{entry1.id}"
      # Replace stream for remaining entry (re-checked)
      assert_includes response.body, "categorize_entry_#{entry2.id}"
      # No redirect stream — still in the group
      assert_not_includes response.body, "action=\"redirect\""
    end

    test "create with create_rule param creates rule with name and type conditions" do
      entry = create_transaction(account: @account, name: "Netflix", amount: 15)

      assert_difference "@family.rules.count", 1 do
        post transactions_categorize_url,
          params: {
            position: 0,
            grouping_key: "Netflix",
            transaction_type: "expense",
            entry_ids: [ entry.id ],
            all_entry_ids: [ entry.id ],
            category_id: @category.id,
            create_rule: "1"
          },
          headers: { "Accept" => "text/vnd.turbo-stream.html" }
      end

      rule = @family.rules.find_by(name: "Netflix")
      assert_not_nil rule
      assert rule.active
      assert rule.conditions.any? { |c| c.condition_type == "transaction_name" && c.value == "Netflix" }
      assert rule.conditions.any? { |c| c.condition_type == "transaction_type" && c.value == "expense" }
    end

    test "create falls back to html redirect without turbo stream header" do
      entry = create_transaction(account: @account, name: "Starbucks")

      post transactions_categorize_url, params: {
        position: 0,
        grouping_key: "Starbucks",
        entry_ids: [ entry.id ],
        all_entry_ids: [ entry.id ],
        category_id: @category.id
      }

      assert_redirected_to transactions_categorize_url(position: 0)
      assert flash[:notice].present?
    end

    # PATCH /transactions/categorize/assign_entry

    test "assign_entry categorizes single entry and returns remove stream" do
      entry = create_transaction(account: @account, name: "Starbucks")
      other = create_transaction(account: @account, name: "Starbucks")

      patch assign_entry_transactions_categorize_url, params: {
        entry_id: entry.id,
        category_id: @category.id,
        position: 0,
        all_entry_ids: [ entry.id, other.id ]
      }

      assert_response :success
      assert_equal @category, entry.transaction.reload.category
      assert_includes response.body, "categorize_entry_#{entry.id}"
      assert_not_includes response.body, "action=\"redirect\""
    end

    test "assign_entry returns redirect stream when last entry in group" do
      entry = create_transaction(account: @account, name: "Starbucks")

      patch assign_entry_transactions_categorize_url, params: {
        entry_id: entry.id,
        category_id: @category.id,
        position: 0,
        all_entry_ids: [ entry.id ]
      }

      assert_response :success
      assert_includes response.body, "action=\"redirect\""
    end
end
