diff --git a/assets/js/app.js b/assets/js/app.js
index 696c2c133fc3792e4afb9e8c252311b90ad2ff84..f6a937eb5dd613180ca2f7b1afe59983b64e428c 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -5,6 +5,7 @@ import topbar from "../vendor/topbar";
 import "../css/app.css";
 import "./favorites";
 import "./status";
+import "./interest";
 
 let csrfToken = document
   .querySelector("meta[name='csrf-token']")
diff --git a/assets/js/interest.js b/assets/js/interest.js
new file mode 100644
index 0000000000000000000000000000000000000000..f3873f907e1062d0224f549937fcc88cb81449e5
--- /dev/null
+++ b/assets/js/interest.js
@@ -0,0 +1,85 @@
+document.addEventListener("DOMContentLoaded", initializeInterestButtons);
+
+function initializeInterestButtons() {
+  const menuButtons = document.querySelectorAll(".interest-menu-button");
+  
+  menuButtons.forEach((button) => {
+    button.addEventListener("click", (e) => {
+      e.stopPropagation();
+      const menu = button.nextElementSibling;
+      menu.classList.toggle("hidden");
+    });
+
+    const options = button.nextElementSibling.querySelectorAll(".interest-option");
+    options.forEach((option) => {
+      option.addEventListener("click", handleInterestOptionClick);
+    });
+  });
+}
+
+async function handleInterestOptionClick(e) {
+  e.preventDefault();
+  const button = this.closest('.relative').querySelector('.interest-menu-button');
+  const reference = button.dataset.propertyReference;
+  const status = this.dataset.status;
+  const isNotInterested = status === "not_interested";
+
+  try {
+    const response = await fetch(`/api/properties/${reference}/interest`, {
+      method: isNotInterested ? "POST" : "DELETE",
+      headers: {
+        "Content-Type": "application/json",
+        "x-csrf-token": document.querySelector("meta[name='csrf-token']").content,
+      },
+    });
+
+    const data = await response.json();
+
+    if (response.ok) {
+      const menu = this.closest('.interest-menu');
+      const ticks = menu.querySelectorAll(".interest-tick");
+      ticks.forEach(tick => tick.classList.add("invisible"));
+      this.querySelector(".interest-tick").classList.remove("invisible");
+      
+      menu.classList.add("hidden");
+      
+      // Fetch and update content
+      const propertiesContainer = document.getElementById("properties");
+      propertiesContainer.innerHTML = await fetch(window.location.pathname)
+        .then(response => response.text())
+        .then(html => {
+          const doc = new DOMParser().parseFromString(html, 'text/html');
+          return doc.getElementById("properties").innerHTML;
+        });
+      
+      // Initialize both sets of buttons
+      document.dispatchEvent(new Event('DOMContentLoaded'));
+      
+      showFlashMessage("success", data.message);
+    } else {
+      showFlashMessage("error", data.error);
+    }
+  } catch (error) {
+    console.error("Error:", error);
+    showFlashMessage("error", "Something went wrong");
+  }
+}
+
+document.addEventListener("click", () => {
+  document.querySelectorAll(".interest-menu").forEach(menu => {
+    menu.classList.add("hidden");
+  });
+});
+
+function showFlashMessage(type, message) {
+  const flashContainer = document.createElement("div");
+  flashContainer.className = `flash-message ${type}`;
+  flashContainer.textContent = message;
+
+  document.body.appendChild(flashContainer);
+
+  setTimeout(() => {
+    flashContainer.style.animation = "fadeOut 0.3s ease-out";
+    flashContainer.addEventListener("animationend", () => flashContainer.remove());
+  }, 3000);
+}
\ No newline at end of file
diff --git a/features/config.exs b/features/config.exs
index 2872eabdc3d6c785677d5cce5de32f5aa7a2ce3c..50d545e8a3bb43570f374f7c2f82f5b6201993b7 100644
--- a/features/config.exs
+++ b/features/config.exs
@@ -64,4 +64,8 @@ defmodule WhiteBreadConfig do
       suite name:          "FR-18 Display Similar Properties on Listing View",
             context:       SimilarPropertiesContext,
             feature_paths: ["features/similar_properties_view.feature"]
+
+      suite name:          "FR-16: Mark Property as Not Interested",
+            context:       NotInterestedPropertiesContext,
+            feature_paths: ["features/not_interested_properties.feature"]
 end
diff --git a/features/contexts/not_interested_properties_context.exs b/features/contexts/not_interested_properties_context.exs
new file mode 100644
index 0000000000000000000000000000000000000000..742cc9ee2df949dc64362f2320fab724f8d5c5d9
--- /dev/null
+++ b/features/contexts/not_interested_properties_context.exs
@@ -0,0 +1,198 @@
+defmodule NotInterestedPropertiesContext do
+  use WhiteBread.Context
+  use Hound.Helpers
+  import Ecto.Query
+  alias PropTrackr.Accounts
+  alias PropTrackr.Repo
+  alias PropTrackr.Accounts.User
+  alias PropTrackr.Properties.Property
+  alias PropTrackr.NotInterested
+
+  scenario_starting_state fn _state ->
+    Ecto.Adapters.SQL.Sandbox.checkout(PropTrackr.Repo)
+    Ecto.Adapters.SQL.Sandbox.mode(PropTrackr.Repo, {:shared, self()})
+    Hound.start_session()
+    %{}
+  end
+
+  scenario_finalize fn _status, _state ->
+    Ecto.Adapters.SQL.Sandbox.checkin(PropTrackr.Repo)
+    Hound.end_session()
+  end
+
+  given_ ~r/^there exists following accounts$/, fn state, %{table_data: table} ->
+    table
+    |> Enum.map(fn user_details -> User.changeset(%User{}, user_details) end)
+    |> Enum.each(fn changeset -> Repo.insert!(changeset) end)
+
+    random_user = List.first(table)
+    owner = List.last(table)
+
+    {
+      :ok,
+      state
+      |> Map.put(:random_email, random_user[:email])
+      |> Map.put(:random_password, random_user[:password])
+      |> Map.put(:owner_email, owner[:email])
+      |> Map.put(:owner_password, owner[:password])
+    }
+  end
+
+  and_ ~r/^the following properties exist$/, fn state, %{ table_data: table } ->
+    owner = Repo.get_by(User, email: state[:owner_email])
+
+    advertisements =
+      table
+      |> Enum.map(fn details -> details |> Map.put(:reference, Ecto.UUID.generate()) end)
+      |> Enum.map(fn details -> {Ecto.build_assoc(owner, :properties, details), details} end)
+      |> Enum.map(fn {assoc, details} -> Property.changeset(assoc, details) end)
+      |> Enum.map(fn changeset -> Repo.insert!(changeset) end)
+
+    {
+      :ok,
+      state
+      |> Map.put(:advertisements, advertisements)
+    }
+  end
+
+  and_ ~r/^I am logged in as random user$/, fn state ->
+    setup_session(state[:random_email], state[:random_password])
+    {:ok, state}
+  end
+
+  and_ ~r/^I am logged in as owner$/, fn state ->
+    setup_session(state[:owner_email], state[:owner_password])
+    {:ok, state}
+  end
+
+  and_ ~r/^the property "(?<argument_one>[^"]+)" is marked as not interested$/,
+  fn state, %{argument_one: argument_one} ->
+    current_user = Repo.get_by(User, email: state[:random_email])
+    property = Enum.find(state.advertisements, fn advert ->
+      advert.title == argument_one
+    end)
+
+    not_interested_changeset = %NotInterested{}
+    |> NotInterested.changeset(%{
+      user_id: current_user.id,
+      property_id: property.id
+    })
+
+    Repo.insert!(not_interested_changeset)
+
+    {:ok, state}
+  end
+
+  when_ ~r/^I visit the home page$/, fn state ->
+    navigate_to("/")
+    {:ok, state}
+  end
+
+  and_ ~r/^I click interest menu for "(?<argument_one>[^"]+)"$/,
+  fn state, %{argument_one: argument_one} ->
+    property = Enum.find(state.advertisements, fn advert ->
+      advert.title == argument_one
+    end)
+
+    # Update selector to match your HTML
+    # Change this line in ManagePropertyInterestContext
+    menu_button = find_element(:css, ".interest-menu-button[data-property-reference='#{property.reference}']")
+    click(menu_button)
+    :timer.sleep(500)
+
+    {:ok, state}
+  end
+
+  and_ ~r/^I click "(?<option>[^"]+)" option$/, fn state, %{option: option} ->
+    option_button = find_element(:css, ".interest-option[data-status='#{String.downcase(option) |> String.replace(" ", "_")}']")
+    click(option_button)
+    :timer.sleep(500)
+
+    {:ok, state}
+  end
+
+  then_ ~r/^Property should move to bottom of the list$/, fn state ->
+    properties = find_all_elements(:css, "#properties > div")
+    last_property = List.last(properties)
+    property = List.first(state.advertisements)
+
+    title_element = find_within_element(last_property, :css, "h2")
+    title_text = visible_text(title_element)
+
+    assert title_text == property.title
+    {:ok, state}
+  end
+
+  then_ ~r/^Property should move to its original position$/, fn state ->
+    properties = find_all_elements(:css, "#properties > div")
+    first_property = List.first(properties)
+    property = List.first(state.advertisements)
+
+    title_element = find_within_element(first_property, :css, "h2")
+    title_text = visible_text(title_element)
+
+    assert title_text == property.title
+    {:ok, state}
+  end
+
+  then_ ~r/^I should see success message for marking as not interested$/, fn state ->
+    assert visible_in_page?(~r/Marked as not interested/i)
+    {:ok, state}
+  end
+
+  then_ ~r/^I should see success message for marking as interested$/, fn state ->
+    assert visible_in_page?(~r/Marked as interested/i)
+    {:ok, state}
+  end
+
+  then_ ~r/^I should not see interest menu for "(?<argument_one>[^"]+)"$/,
+  fn state, %{argument_one: argument_one} ->
+    property = Enum.find(state.advertisements, fn advert ->
+      advert.title == argument_one
+    end)
+
+    elements = find_all_elements(:css, ".interest-menu-button[data-property-reference='#{property.reference}']")
+
+    assert Enum.empty?(elements),
+      "Expected no interest menu for property '#{argument_one}' but found #{length(elements)} menu(s)"
+
+    {:ok, state}
+  end
+
+  # Add these to ManagePropertyInterestContext
+
+  and_ ~r/^Property should be added to database$/, fn state ->
+    property = List.first(state.advertisements)
+    current_user = Repo.get_by(User, email: state[:random_email])
+
+    not_interested = Repo.one(from n in NotInterested,
+      where: n.user_id == ^current_user.id and n.property_id == ^property.id)
+
+    assert not_interested != nil,
+      "Expected property to be marked as not interested in database"
+
+    navigate_to("/logout")
+    {:ok, state}
+  end
+
+  and_ ~r/^Property should be deleted from database$/, fn state ->
+    property = List.first(state.advertisements)
+    current_user = Repo.get_by(User, email: state[:random_email])
+
+    not_interested = Repo.one(from n in NotInterested,
+      where: n.user_id == ^current_user.id and n.property_id == ^property.id)
+
+    assert not_interested == nil,
+      "Expected property to be removed from not interested in database"
+
+    navigate_to("/logout")
+    {:ok, state}
+  end
+
+  defp setup_session(email, password) do
+    navigate_to("/login")
+    fill_field({:id, "email"}, email)
+    fill_field({:id, "password"}, password)
+    click({:id, "login_button"})
+  end
+ end
diff --git a/features/not_interested_properties.feature b/features/not_interested_properties.feature
new file mode 100644
index 0000000000000000000000000000000000000000..b6c009ed2f2aea15458cff64e384f95c3f294cb8
--- /dev/null
+++ b/features/not_interested_properties.feature
@@ -0,0 +1,55 @@
+Feature: Property Interest Management
+
+ Scenario: User can mark property as not interested
+   Given there exists following accounts
+     | name     | surname | birth_date | phone_number | email                      | password | confirm_password |
+     | Existing | Account | 2000-01-01 | 000          | existing.account@gmail.com | password | password         |  
+     | Property | Owner   | 2000-01-01 | 111          | property.owner@gmail.com   | password | password         |
+   And the following properties exist
+     | title                | description                      | type | property_type | state     | location | room_count | area  | floor | floor_count | price  |
+     | Really cool property | Selling this really really house | sell | house         | available | London   | 3          | 100.0 | 2     | 5           | 500000 |
+   And I am logged in as random user
+   When I visit the home page
+   And I click interest menu for "Really cool property"
+   And I click "Not interested" option
+   Then Property should move to bottom of the list
+   And I should see success message for marking as not interested
+   And Property should be added to database
+
+ Scenario: User can mark not interested property as interested again
+   Given there exists following accounts
+     | name     | surname | birth_date | phone_number | email                      | password | confirm_password |
+     | Existing | Account | 2000-01-01 | 000          | existing.account@gmail.com | password | password         |
+     | Property | Owner   | 2000-01-01 | 111          | property.owner@gmail.com   | password | password         |
+   And the following properties exist
+     | title                | description                      | type | property_type | state     | location | room_count | area  | floor | floor_count | price  |
+     | Really cool property | Selling this really really house | sell | house         | available | London   | 3          | 100.0 | 2     | 5           | 500000 |
+   And I am logged in as random user
+   And the property "Really cool property" is marked as not interested
+   When I visit the home page
+   And I click interest menu for "Really cool property"
+   And I click "Interested" option
+   Then Property should move to its original position
+   And I should see success message for marking as interested
+   And Property should be deleted from database
+
+ Scenario: User cannot mark own property as not interested
+   Given there exists following accounts
+     | name     | surname | birth_date | phone_number | email                    | password | confirm_password |
+     | Property | Owner   | 2000-01-01 | 111          | property.owner@gmail.com | password | password         |
+   And the following properties exist
+     | title                | description                      | type | property_type | state     | location | room_count | area  | floor | floor_count | price  |
+     | Really cool property | Selling this really really house | sell | house         | available | London   | 3          | 100.0 | 2     | 5           | 500000 |
+   And I am logged in as owner
+   When I visit the home page
+   Then I should not see interest menu for "Really cool property"
+
+ Scenario: Unauthenticated user cannot mark property as not interested
+   Given there exists following accounts
+     | name     | surname | birth_date | phone_number | email                    | password | confirm_password |
+     | Property | Owner   | 2000-01-01 | 111          | property.owner@gmail.com | password | password         |
+   And the following properties exist
+     | title                | description                      | type | property_type | state     | location | room_count | area  | floor | floor_count | price  |
+     | Really cool property | Selling this really really house | sell | house         | available | London   | 3          | 100.0 | 2     | 5           | 500000 |
+   When I visit the home page
+   Then I should not see interest menu for "Really cool property"
\ No newline at end of file
diff --git a/lib/proptrackr/properties/not_interested.ex b/lib/proptrackr/properties/not_interested.ex
new file mode 100644
index 0000000000000000000000000000000000000000..528df1217b6795a29fa6cc012f57bbeb3398b9bd
--- /dev/null
+++ b/lib/proptrackr/properties/not_interested.ex
@@ -0,0 +1,17 @@
+defmodule PropTrackr.NotInterested do
+  use Ecto.Schema
+  import Ecto.Changeset
+
+  schema "not_interested" do
+    belongs_to :user, PropTrackr.Accounts.User
+    belongs_to :property, PropTrackr.Properties.Property
+    timestamps()
+  end
+
+  def changeset(not_interested, attrs \\ %{}) do
+    not_interested
+    |> cast(attrs, [:user_id, :property_id])
+    |> validate_required([:user_id, :property_id])
+    |> unique_constraint([:user_id, :property_id])
+  end
+end
diff --git a/lib/proptrackr_web/controllers/interest_controller.ex b/lib/proptrackr_web/controllers/interest_controller.ex
new file mode 100644
index 0000000000000000000000000000000000000000..4632e1ba456839c18f632b538e40088396200ec7
--- /dev/null
+++ b/lib/proptrackr_web/controllers/interest_controller.ex
@@ -0,0 +1,81 @@
+defmodule PropTrackrWeb.InterestController do
+  use PropTrackrWeb, :controller
+
+  import Ecto.Query
+  alias PropTrackr.Repo
+  alias PropTrackr.Properties.Property
+  alias PropTrackr.NotInterested
+
+  def create(conn, %{"reference" => reference}) do
+    current_user = conn.assigns.current_user
+
+    if current_user == nil do
+      conn
+      |> put_status(:unauthorized)
+      |> json(%{error: "You must be logged in"})
+    else
+      case Repo.one(from p in Property, where: p.reference == ^reference) do
+        nil ->
+          conn
+          |> put_status(:not_found)
+          |> json(%{error: "Property not found"})
+
+        property ->
+          if property.user_id == current_user.id do
+            conn
+            |> put_status(:forbidden)
+            |> json(%{error: "Cannot mark own property as not interested"})
+          else
+            changeset = %NotInterested{}
+            |> NotInterested.changeset(%{
+              user_id: current_user.id,
+              property_id: property.id
+            })
+
+            case Repo.insert(changeset) do
+              {:ok, _} -> json(conn, %{message: "Marked as not interested"})
+              {:error, _} ->
+                conn
+                |> put_status(:unprocessable_entity)
+                |> json(%{error: "Already marked as not interested"})
+            end
+          end
+      end
+    end
+  end
+
+  def delete(conn, %{"reference" => reference}) do
+    current_user = conn.assigns.current_user
+
+    if current_user == nil do
+      conn
+      |> put_status(:unauthorized)
+      |> json(%{error: "You must be logged in"})
+    else
+      case Repo.one(from p in Property, where: p.reference == ^reference) do
+        nil ->
+          conn
+          |> put_status(:not_found)
+          |> json(%{error: "Property not found"})
+
+        property ->
+          case Repo.one(from n in NotInterested,
+            where: n.user_id == ^current_user.id and n.property_id == ^property.id) do
+            nil ->
+              conn
+              |> put_status(:not_found)
+              |> json(%{error: "Already marked as interested"})
+
+            not_interested ->
+              case Repo.delete(not_interested) do
+                {:ok, _} -> json(conn, %{message: "Marked as interested"})
+                {:error, _} ->
+                  conn
+                  |> put_status(:internal_server_error)
+                  |> json(%{error: "Failed to update"})
+              end
+          end
+      end
+    end
+  end
+end
diff --git a/lib/proptrackr_web/controllers/properties_controller.ex b/lib/proptrackr_web/controllers/properties_controller.ex
index 33c03a6fb8414fe9c34746ac73421d9d8ea6f239..e25d93d04d868ec2360ecf74ec145290a5fd0ab9 100644
--- a/lib/proptrackr_web/controllers/properties_controller.ex
+++ b/lib/proptrackr_web/controllers/properties_controller.ex
@@ -6,16 +6,28 @@ defmodule PropTrackrWeb.PropertiesController do
   alias PropTrackr.Properties.{Property,  Photo}
   alias PropTrackr.Favorites.Favorite
   alias PropTrackr.Uploads
+  alias PropTrackr.NotInterested
 
   def index(conn, _params) do
-
-    properties = Repo.all(
-                     from p in Property,
-                     where: p.state == :available,
-                     order_by: [asc: p.inserted_at],
-                     preload: [:photos],
-                    # preload: [photos: ^from(ph in Photo, order_by: [asc: ph.order])],
-                     select: p)
+    current_user = conn.assigns.current_user
+    current_user_id = current_user && current_user.id
+
+    properties = from(p in Property,
+      left_join: ni in NotInterested,
+      on: ni.property_id == p.id and ni.user_id == ^(current_user_id || 0),
+      where: p.state == :available,
+      order_by: [asc: not is_nil(ni.id), asc: p.inserted_at],
+      preload: [:photos],
+      select: p)
+    |> Repo.all()
+
+    not_interested = if current_user do
+      Repo.all(from n in NotInterested,
+        where: n.user_id == ^current_user.id,
+        select: n.property_id)
+      else
+        []
+    end
 
     favorites = case conn.assigns.current_user do
       nil -> []
@@ -29,7 +41,8 @@ defmodule PropTrackrWeb.PropertiesController do
 
     render(conn, "index.html",
     properties: properties,
-    favorites: favorites
+    favorites: favorites,
+    not_interested: not_interested
     )
   end
 
diff --git a/lib/proptrackr_web/controllers/properties_html/index.html.heex b/lib/proptrackr_web/controllers/properties_html/index.html.heex
index 43f687abaa7c1e30aac5e5dda12143f4c902b305..3c41d52a211995911209a41753ce3f9c781bdb2b 100644
--- a/lib/proptrackr_web/controllers/properties_html/index.html.heex
+++ b/lib/proptrackr_web/controllers/properties_html/index.html.heex
@@ -37,14 +37,35 @@
           <div class="flex justify-between items-start">
             <h2 class="font-bold"><%= property.title %></h2>
             <%= if @conn.assigns[:current_user] && @conn.assigns.current_user.id != property.user_id do %>
-              <button
-                type="button"
-                class="favorite-button text-2xl"
-                data-property-reference={property.reference}
-                data-favorited={if property.id in @favorites, do: "true", else: "false"}
-              >
-                <%= if property.id in @favorites, do: "âک…", else: "âک†" %>
-              </button>
+              <div class="flex items-center gap-4">
+                <button
+                  type="button"
+                  class="favorite-button text-2xl"
+                  data-property-reference={property.reference}
+                  data-favorited={if property.id in @favorites, do: "true", else: "false"}
+                >
+                  <%= if property.id in @favorites, do: "âک…", else: "âک†" %>
+                </button>
+                <div class="relative">
+                  <button type="button" class="interest-menu-button" data-property-reference={property.reference}>
+                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6">
+                      <circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/>
+                    </svg>
+                  </button>
+                  <div class="interest-menu hidden absolute right-0 mt-2 bg-white border rounded shadow-lg z-10">
+                    <div class="py-1">
+                      <button class="interest-option w-full text-left px-4 py-2 hover:bg-gray-100 flex items-center gap-2" data-status="interested">
+                        <span class={"interest-tick #{if property.id in @not_interested, do: "invisible", else: "visible"}"}>✓</span>
+                        Interested
+                      </button>
+                      <button class="interest-option w-full text-left px-4 py-2 hover:bg-gray-100 flex items-center gap-2 whitespace-nowrap" data-status="not_interested">
+                        <span class={"interest-tick #{if property.id in @not_interested, do: "visible", else: "invisible"}"}>✓</span>
+                        Not interested  
+                      </button>
+                    </div>
+                  </div>
+                </div>
+              </div>
             <% end %>
           </div>
           <p><%= property.description %></p>
diff --git a/lib/proptrackr_web/router.ex b/lib/proptrackr_web/router.ex
index 2bc6c7892a90ae4cd301c45c6665d4c9f58334dc..6e6b915686cb23132be8d9108ca814fa27b49077 100644
--- a/lib/proptrackr_web/router.ex
+++ b/lib/proptrackr_web/router.ex
@@ -54,6 +54,8 @@ defmodule PropTrackrWeb.Router do
     post "/properties/:reference/favorite", FavoriteController, :create
     delete "/properties/:reference/favorite", FavoriteController, :delete
     post "/properties/:reference/status", PropertiesController, :update_status
+    post "/properties/:reference/interest", InterestController, :create
+    delete "/properties/:reference/interest", InterestController, :delete
   end
 
   # Other scopes may use custom stacks.
diff --git a/priv/repo/migrations/20241128095131_add_not_interested.exs b/priv/repo/migrations/20241128095131_add_not_interested.exs
new file mode 100644
index 0000000000000000000000000000000000000000..51c98ad19333587f2472be06906a91a99ffee301
--- /dev/null
+++ b/priv/repo/migrations/20241128095131_add_not_interested.exs
@@ -0,0 +1,13 @@
+defmodule PropTrackr.Repo.Migrations.AddNotInterested do
+  use Ecto.Migration
+
+  def change do
+    create table(:not_interested) do
+      add :user_id, references(:users, on_delete: :delete_all)
+      add :property_id, references(:properties, on_delete: :delete_all)
+      timestamps()
+    end
+
+    create unique_index(:not_interested, [:user_id, :property_id])
+  end
+end
diff --git a/test/proptrackr_web/controllers/interest_controller_test.exs b/test/proptrackr_web/controllers/interest_controller_test.exs
new file mode 100644
index 0000000000000000000000000000000000000000..63d80c49d193ee4c60ace3ed5bcf9c9ab973354a
--- /dev/null
+++ b/test/proptrackr_web/controllers/interest_controller_test.exs
@@ -0,0 +1,133 @@
+defmodule PropTrackrWeb.InterestControllerTest do
+  use PropTrackrWeb.ConnCase
+  alias PropTrackr.Accounts.User
+  alias PropTrackr.Properties.Property
+  alias PropTrackr.NotInterested
+  alias PropTrackr.Repo
+  import Ecto.Query
+
+  setup do
+    owner = %User{
+      name: "Property", surname: "Owner",
+      birth_date: "2000-01-01", phone_number: "111",
+      email: "property.owner@gmail.com",
+      password: "password", confirm_password: "password"
+    } |> Repo.insert!()
+
+    random_user = %User{
+      name: "Random", surname: "User",
+      birth_date: "2000-01-01", phone_number: "000",
+      email: "random.user@gmail.com",
+      password: "password", confirm_password: "password"
+    } |> Repo.insert!()
+
+    property = %Property{
+      reference: Ecto.UUID.generate(),
+      title: "Test Property", description: "Test Description",
+      type: :sell, property_type: :house, state: :available,
+      location: "Test Location", room_count: 3, area: 100.0,
+      floor: 2, floor_count: 5, price: 500000.0,
+      user_id: owner.id
+    } |> Repo.insert!()
+
+    {:ok, %{owner: owner, random_user: random_user, property: property}}
+  end
+
+  test "authenticated user can mark property as not interested", %{conn: conn, random_user: user, property: property} do
+    conn = conn |> setup_session(user)
+    conn = post(conn, ~p"/api/properties/#{property.reference}/interest")
+
+    assert json_response(conn, 200)["message"] == "Marked as not interested"
+
+    not_interested = Repo.one(from n in NotInterested,
+      where: n.user_id == ^user.id and n.property_id == ^property.id)
+    assert not_interested != nil
+  end
+
+  test "authenticated user cannot mark same property as not interested twice", %{conn: conn, random_user: user, property: property} do
+    Repo.insert!(%NotInterested{user_id: user.id, property_id: property.id})
+
+    conn = conn |> setup_session(user)
+    conn = post(conn, ~p"/api/properties/#{property.reference}/interest")
+
+    assert json_response(conn, 422)["error"] == "Already marked as not interested"
+
+    count = Repo.one(from n in NotInterested,
+      where: n.user_id == ^user.id and n.property_id == ^property.id,
+      select: count(n.id))
+    assert count == 1
+  end
+
+  test "owner cannot mark own property as not interested", %{conn: conn, owner: owner, property: property} do
+    conn = conn |> setup_session(owner)
+    conn = post(conn, ~p"/api/properties/#{property.reference}/interest")
+
+    assert json_response(conn, 403)["error"] == "Cannot mark own property as not interested"
+
+    not_interested = Repo.one(from n in NotInterested,
+      where: n.user_id == ^owner.id and n.property_id == ^property.id)
+    assert not_interested == nil
+  end
+
+  test "unauthenticated user cannot mark property as not interested", %{conn: conn, property: property} do
+    initial_count = Repo.one(from n in NotInterested, select: count(n.id))
+    conn = post(conn, ~p"/api/properties/#{property.reference}/interest")
+
+    assert json_response(conn, 401)["error"] == "You must be logged in"
+    final_count = Repo.one(from n in NotInterested, select: count(n.id))
+    assert initial_count == final_count
+  end
+
+  test "cannot mark non-existent property as not interested", %{conn: conn, random_user: user} do
+    conn = conn |> setup_session(user)
+    conn = post(conn, ~p"/api/properties/non-existent/interest")
+
+    assert json_response(conn, 404)["error"] == "Property not found"
+  end
+
+  test "authenticated user can mark property as interested again", %{conn: conn, random_user: user, property: property} do
+    Repo.insert!(%NotInterested{user_id: user.id, property_id: property.id})
+
+    conn = conn |> setup_session(user)
+    conn = delete(conn, ~p"/api/properties/#{property.reference}/interest")
+
+    assert json_response(conn, 200)["message"] == "Marked as interested"
+
+    not_interested = Repo.one(from n in NotInterested,
+      where: n.user_id == ^user.id and n.property_id == ^property.id)
+    assert not_interested == nil
+  end
+
+  test "authenticated user cannot mark interested property as interested again", %{conn: conn, random_user: user, property: property} do
+    conn = conn |> setup_session(user)
+    conn = delete(conn, ~p"/api/properties/#{property.reference}/interest")
+
+    assert json_response(conn, 404)["error"] == "Already marked as interested"
+  end
+
+  test "unauthenticated user cannot mark property as interested", %{conn: conn, random_user: user, property: property} do
+    Repo.insert!(%NotInterested{user_id: user.id, property_id: property.id})
+    initial_count = Repo.one(from n in NotInterested, select: count(n.id))
+
+    conn = delete(conn, ~p"/api/properties/#{property.reference}/interest")
+
+    assert json_response(conn, 401)["error"] == "You must be logged in"
+    final_count = Repo.one(from n in NotInterested, select: count(n.id))
+    assert initial_count == final_count
+  end
+
+  test "deleting property removes associated not interested marks", %{conn: conn, random_user: user, property: property} do
+    Repo.insert!(%NotInterested{user_id: user.id, property_id: property.id})
+    Repo.delete!(property)
+
+    not_interested = Repo.one(from n in NotInterested,
+      where: n.user_id == ^user.id and n.property_id == ^property.id)
+    assert not_interested == nil
+  end
+
+  defp setup_session(conn, user) do
+    conn = conn |> post("/login", email: user.email, password: user.password)
+    conn = get conn, redirected_to(conn)
+    conn
+  end
+ end