defmodule Berrypod.AnalyticsTest do
  use Berrypod.DataCase, async: false

  import Ecto.Query

  alias Berrypod.Analytics
  alias Berrypod.Analytics.{Buffer, Event}
  alias Berrypod.Repo

  setup do
    # Flush any pending events then clear the table so each test starts clean
    send(Buffer, :flush)
    :timer.sleep(50)
    Repo.delete_all(Event)
    :ok
  end

  # Helper to insert events directly (bypassing the buffer for query tests)
  defp insert_event(attrs) do
    now = DateTime.utc_now() |> DateTime.truncate(:second)

    defaults = %{
      id: Ecto.UUID.generate(),
      name: "pageview",
      pathname: "/",
      visitor_hash: :crypto.strong_rand_bytes(8),
      session_hash: :crypto.strong_rand_bytes(8),
      inserted_at: now
    }

    event = Map.merge(defaults, attrs)
    Repo.insert_all(Event, [Map.to_list(event)])
    event
  end

  defp today_range do
    today = Date.utc_today()
    start_dt = DateTime.new!(today, ~T[00:00:00], "Etc/UTC")
    end_dt = DateTime.new!(Date.add(today, 1), ~T[00:00:00], "Etc/UTC")
    {start_dt, end_dt}
  end

  describe "track_pageview/1" do
    test "records a pageview through the buffer" do
      visitor_hash = :crypto.strong_rand_bytes(8)

      Analytics.track_pageview(%{pathname: "/test", visitor_hash: visitor_hash})

      send(Buffer, :flush)
      :timer.sleep(50)

      [event] =
        from(e in Event, where: e.visitor_hash == ^visitor_hash) |> Repo.all()

      assert event.name == "pageview"
      assert event.pathname == "/test"
    end
  end

  describe "track_event/2" do
    test "records a named event through the buffer" do
      visitor_hash = :crypto.strong_rand_bytes(8)

      Analytics.track_event("add_to_cart", %{
        pathname: "/products/tee",
        visitor_hash: visitor_hash
      })

      send(Buffer, :flush)
      :timer.sleep(50)

      [event] =
        from(e in Event, where: e.visitor_hash == ^visitor_hash) |> Repo.all()

      assert event.name == "add_to_cart"
      assert event.pathname == "/products/tee"
    end
  end

  describe "count_visitors/1" do
    test "counts distinct visitors" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, pathname: "/"})
      insert_event(%{visitor_hash: v1, pathname: "/about"})
      insert_event(%{visitor_hash: v2, pathname: "/"})

      assert Analytics.count_visitors(today_range()) == 2
    end

    test "returns 0 for empty range" do
      assert Analytics.count_visitors(today_range()) == 0
    end
  end

  describe "count_pageviews/1" do
    test "counts all pageview events" do
      v1 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, pathname: "/"})
      insert_event(%{visitor_hash: v1, pathname: "/about"})
      insert_event(%{visitor_hash: v1, pathname: "/products/tee", name: "product_view"})

      assert Analytics.count_pageviews(today_range()) == 2
    end
  end

  describe "bounce_rate/1" do
    test "100% bounce rate when all sessions have 1 pageview" do
      s1 = :crypto.strong_rand_bytes(8)
      s2 = :crypto.strong_rand_bytes(8)

      insert_event(%{session_hash: s1, pathname: "/"})
      insert_event(%{session_hash: s2, pathname: "/about"})

      assert Analytics.bounce_rate(today_range()) == 100
    end

    test "0% bounce rate when all sessions have multiple pageviews" do
      session = :crypto.strong_rand_bytes(8)

      insert_event(%{session_hash: session, pathname: "/"})
      insert_event(%{session_hash: session, pathname: "/about"})

      assert Analytics.bounce_rate(today_range()) == 0
    end

    test "returns 0 for no data" do
      assert Analytics.bounce_rate(today_range()) == 0
    end
  end

  describe "top_pages/2" do
    test "returns pages sorted by visitor count" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, pathname: "/"})
      insert_event(%{visitor_hash: v2, pathname: "/"})
      insert_event(%{visitor_hash: v1, pathname: "/about"})

      pages = Analytics.top_pages(today_range())
      assert hd(pages).pathname == "/"
      assert hd(pages).visitors == 2
    end
  end

  describe "top_sources/2" do
    test "returns sources sorted by visitor count" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, referrer_source: "Google"})
      insert_event(%{visitor_hash: v2, referrer_source: "Google"})
      insert_event(%{visitor_hash: v1, referrer_source: "Facebook"})

      sources = Analytics.top_sources(today_range())
      assert hd(sources).source == "Google"
      assert hd(sources).visitors == 2
    end
  end

  describe "top_countries/2" do
    test "returns countries sorted by visitor count" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, country_code: "GB"})
      insert_event(%{visitor_hash: v2, country_code: "GB"})
      insert_event(%{visitor_hash: v1, country_code: "US"})

      countries = Analytics.top_countries(today_range())
      assert hd(countries).country_code == "GB"
      assert hd(countries).visitors == 2
    end
  end

  describe "device_breakdown/2" do
    test "returns browser breakdown" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, browser: "Chrome"})
      insert_event(%{visitor_hash: v2, browser: "Chrome"})
      insert_event(%{visitor_hash: v1, browser: "Firefox"})

      browsers = Analytics.device_breakdown(today_range(), :browser)
      assert hd(browsers).name == "Chrome"
      assert hd(browsers).visitors == 2
    end
  end

  describe "funnel/1" do
    test "returns counts for each funnel step" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, name: "product_view", pathname: "/products/tee"})
      insert_event(%{visitor_hash: v2, name: "product_view", pathname: "/products/tee"})
      insert_event(%{visitor_hash: v1, name: "add_to_cart", pathname: "/products/tee"})
      insert_event(%{visitor_hash: v1, name: "checkout_start", pathname: "/checkout"})

      insert_event(%{
        visitor_hash: v1,
        name: "purchase",
        pathname: "/checkout/success",
        revenue: 2500
      })

      funnel = Analytics.funnel(today_range())
      assert funnel.product_views == 2
      assert funnel.add_to_carts == 1
      assert funnel.checkouts == 1
      assert funnel.purchases == 1
    end
  end

  describe "total_revenue/1" do
    test "sums revenue from purchase events" do
      v1 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, name: "purchase", pathname: "/", revenue: 2500})
      insert_event(%{visitor_hash: v1, name: "purchase", pathname: "/", revenue: 1500})

      assert Analytics.total_revenue(today_range()) == 4000
    end

    test "returns 0 when no purchases" do
      assert Analytics.total_revenue(today_range()) == 0
    end
  end

  describe "filtering" do
    test "count_visitors respects country_code filter" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, country_code: "GB"})
      insert_event(%{visitor_hash: v2, country_code: "US"})

      assert Analytics.count_visitors(today_range(), %{country_code: "GB"}) == 1
    end

    test "top_pages respects referrer_source filter" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, pathname: "/", referrer_source: "Google"})
      insert_event(%{visitor_hash: v2, pathname: "/about", referrer_source: "Facebook"})

      pages = Analytics.top_pages(today_range(), filters: %{referrer_source: "Google"})
      assert length(pages) == 1
      assert hd(pages).pathname == "/"
    end

    test "bounce_rate respects filter" do
      s1 = :crypto.strong_rand_bytes(8)
      s2 = :crypto.strong_rand_bytes(8)

      # GB visitor bounces (1 pageview)
      insert_event(%{session_hash: s1, country_code: "GB"})
      # US visitor doesn't bounce (2 pageviews)
      insert_event(%{session_hash: s2, country_code: "US", pathname: "/"})
      insert_event(%{session_hash: s2, country_code: "US", pathname: "/about"})

      assert Analytics.bounce_rate(today_range(), %{country_code: "GB"}) == 100
      assert Analytics.bounce_rate(today_range(), %{country_code: "US"}) == 0
    end

    test "multiple filters combine with AND logic" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, country_code: "GB", browser: "Chrome"})
      insert_event(%{visitor_hash: v2, country_code: "GB", browser: "Firefox"})

      assert Analytics.count_visitors(today_range(), %{country_code: "GB", browser: "Chrome"}) ==
               1
    end

    test "unknown filter keys are ignored" do
      v1 = :crypto.strong_rand_bytes(8)
      insert_event(%{visitor_hash: v1})

      assert Analytics.count_visitors(today_range(), %{nonexistent: "val"}) == 1
    end
  end

  describe "entry_pages/2" do
    test "returns the first page per session" do
      s1 = :crypto.strong_rand_bytes(8)
      s2 = :crypto.strong_rand_bytes(8)
      now = DateTime.utc_now() |> DateTime.truncate(:second)
      earlier = DateTime.add(now, -60, :second)

      insert_event(%{session_hash: s1, pathname: "/", inserted_at: earlier})
      insert_event(%{session_hash: s1, pathname: "/about", inserted_at: now})
      insert_event(%{session_hash: s2, pathname: "/products", inserted_at: earlier})

      pages = Analytics.entry_pages(today_range())
      pathnames = Enum.map(pages, & &1.pathname)

      assert "/" in pathnames
      assert "/products" in pathnames
      refute "/about" in pathnames
    end

    test "counts sessions per entry page" do
      s1 = :crypto.strong_rand_bytes(8)
      s2 = :crypto.strong_rand_bytes(8)
      s3 = :crypto.strong_rand_bytes(8)
      now = DateTime.utc_now() |> DateTime.truncate(:second)

      insert_event(%{session_hash: s1, pathname: "/"})
      insert_event(%{session_hash: s2, pathname: "/"})
      insert_event(%{session_hash: s3, pathname: "/about", inserted_at: now})

      pages = Analytics.entry_pages(today_range())
      home = Enum.find(pages, &(&1.pathname == "/"))
      assert home.sessions == 2
    end

    test "returns empty list with no data" do
      assert Analytics.entry_pages(today_range()) == []
    end
  end

  describe "exit_pages/2" do
    test "returns the last page per session" do
      s1 = :crypto.strong_rand_bytes(8)
      s2 = :crypto.strong_rand_bytes(8)
      now = DateTime.utc_now() |> DateTime.truncate(:second)
      earlier = DateTime.add(now, -60, :second)

      insert_event(%{session_hash: s1, pathname: "/", inserted_at: earlier})
      insert_event(%{session_hash: s1, pathname: "/about", inserted_at: now})
      insert_event(%{session_hash: s2, pathname: "/products", inserted_at: earlier})

      pages = Analytics.exit_pages(today_range())
      pathnames = Enum.map(pages, & &1.pathname)

      assert "/about" in pathnames
      assert "/products" in pathnames
      refute "/" in pathnames
    end

    test "counts sessions per exit page" do
      s1 = :crypto.strong_rand_bytes(8)
      s2 = :crypto.strong_rand_bytes(8)
      now = DateTime.utc_now() |> DateTime.truncate(:second)
      earlier = DateTime.add(now, -60, :second)

      insert_event(%{session_hash: s1, pathname: "/", inserted_at: earlier})
      insert_event(%{session_hash: s1, pathname: "/about", inserted_at: now})
      insert_event(%{session_hash: s2, pathname: "/", inserted_at: earlier})
      insert_event(%{session_hash: s2, pathname: "/about", inserted_at: now})

      pages = Analytics.exit_pages(today_range())
      about = Enum.find(pages, &(&1.pathname == "/about"))
      assert about.sessions == 2
    end

    test "returns empty list with no data" do
      assert Analytics.exit_pages(today_range()) == []
    end
  end

  describe "count_pageviews_for_path/1" do
    test "counts all pageviews for a specific path" do
      v1 = :crypto.strong_rand_bytes(8)
      v2 = :crypto.strong_rand_bytes(8)

      insert_event(%{visitor_hash: v1, pathname: "/products/classic-tee"})
      insert_event(%{visitor_hash: v2, pathname: "/products/classic-tee"})
      insert_event(%{visitor_hash: v1, pathname: "/products/other"})

      assert Analytics.count_pageviews_for_path("/products/classic-tee") == 2
    end

    test "returns 0 for paths with no history" do
      assert Analytics.count_pageviews_for_path("/products/never-visited") == 0
    end
  end

  describe "delete_events_before/1" do
    test "deletes old events" do
      old = DateTime.add(DateTime.utc_now(), -400, :day) |> DateTime.truncate(:second)
      recent = DateTime.utc_now() |> DateTime.truncate(:second)

      insert_event(%{inserted_at: old, pathname: "/old"})
      insert_event(%{inserted_at: recent, pathname: "/recent"})

      cutoff = DateTime.add(DateTime.utc_now(), -365, :day) |> DateTime.truncate(:second)
      {deleted, _} = Analytics.delete_events_before(cutoff)

      assert deleted == 1
      assert [event] = Repo.all(Event)
      assert event.pathname == "/recent"
    end
  end
end
