defmodule Plausible.ImportedTest do
  use Plausible.DataCase
  use Plausible

  alias Plausible.Imported
  alias Plausible.Stats.{Query, DateTimeRange}

  describe "list_all_imports/1" do
    test "returns empty when there are no imports" do
      site = insert(:site)

      assert Imported.list_all_imports(site) == []
    end

    test "returns imports in various states" do
      site = insert(:site)

      _rogue_import = insert(:site_import)

      import1 = insert(:site_import, site: site, status: :pending)
      import2 = insert(:site_import, site: site, status: :importing)
      import3 = insert(:site_import, site: site, status: :completed)
      import4 = insert(:site_import, site: site, status: :failed)

      assert [%{id: id1}, %{id: id2}, %{id: id3}, %{id: id4}] = Imported.list_all_imports(site)

      ids = [id1, id2, id3, id4]

      assert import1.id in ids
      assert import2.id in ids
      assert import3.id in ids
      assert import4.id in ids
    end
  end

  describe "earliest_import_start_date/1" do
    test "returns nil if no site_imports exist" do
      site = insert(:site)

      assert is_nil(Imported.earliest_import_start_date(site))
    end

    test "returns nil when only incomplete or failed imports are present" do
      site = insert(:site)

      _import1 = insert(:site_import, site: site, status: :pending)
      _import2 = insert(:site_import, site: site, status: :importing)
      _import3 = insert(:site_import, site: site, status: :failed)
      _rogue_import = insert(:site_import, site: build(:site), status: :completed)

      assert is_nil(Imported.earliest_import_start_date(site))
    end

    test "returns start and end dates considering all imports" do
      site = insert(:site)

      _import1 =
        insert(:site_import,
          site: site,
          start_date: ~D[2020-04-02],
          end_date: ~D[2022-06-22],
          status: :completed,
          legacy: true
        )

      _import2 =
        insert(:site_import,
          site: site,
          start_date: ~D[2022-06-22],
          end_date: ~D[2024-01-08],
          status: :completed
        )

      assert Imported.earliest_import_start_date(site) == ~D[2020-04-02]
    end
  end

  describe "completed_imports_in_query_range/2" do
    setup do
      site = insert(:site)

      site_import_feb =
        insert(:site_import, site: site, start_date: ~D[2021-02-01], end_date: ~D[2021-02-28])

      site_import_apr =
        insert(:site_import, site: site, start_date: ~D[2021-04-10], end_date: ~D[2021-04-20])

      {:ok, %{site: site, site_import_feb: site_import_feb, site_import_apr: site_import_apr}}
    end

    test "returns empty list if no imports exist" do
      site = insert(:site)
      tz = "Etc/UTC"

      query = %Query{
        utc_time_range: DateTimeRange.new!(~D[2021-01-01], ~D[2021-12-31], tz),
        timezone: tz
      }

      assert Imported.completed_imports_in_query_range(site, query) == []
    end

    test "returns imports in query range", %{
      site: site,
      site_import_feb: site_import_feb,
      site_import_apr: site_import_apr
    } do
      tz = "Etc/UTC"

      query = %Query{
        utc_time_range: DateTimeRange.new!(~D[2021-01-01], ~D[2021-12-31], tz),
        timezone: tz
      }

      imports_in_range = Imported.completed_imports_in_query_range(site, query)

      assert Enum.find(imports_in_range, &(&1.id == site_import_feb.id))
      assert Enum.find(imports_in_range, &(&1.id == site_import_apr.id))
    end

    test "returns imports in a non-utc timezone query range", %{
      site: site,
      site_import_feb: site_import_feb
    } do
      datetime_from = DateTime.new!(~D[2021-03-01], ~T[03:00:00], "Etc/UTC")
      datetime_to = DateTime.new!(~D[2021-04-10], ~T[03:00:00], "Etc/UTC")

      query = %Query{
        utc_time_range: DateTimeRange.new!(datetime_from, datetime_to),
        timezone: "America/Chicago"
      }

      [site_import] = Imported.completed_imports_in_query_range(site, query)

      assert site_import.id == site_import_feb.id
    end
  end

  describe "clamp_dates/3" do
    test "crops dates from both ends when overlapping with existing import and native stats" do
      site = insert(:site)

      populate_stats(site, [
        build(:pageview, timestamp: ~N[2023-10-25 15:58:00])
      ])

      start_date = ~D[2016-04-03]
      end_date = ~D[2021-05-12]

      _existing_import =
        insert(:site_import,
          site: site,
          start_date: start_date,
          end_date: end_date,
          status: :completed
        )

      assert {:ok, ~D[2021-05-12], ~D[2023-10-25]} =
               Imported.clamp_dates(site, ~D[2021-04-11], ~D[2024-01-12])
    end

    test "picks longest continuous range when containing existing import" do
      site = insert(:site)

      populate_stats(site, [
        build(:pageview, timestamp: ~N[2023-10-25 15:58:00])
      ])

      start_date = ~D[2019-04-03]
      end_date = ~D[2021-05-12]

      _existing_import =
        insert(:site_import,
          site: site,
          start_date: start_date,
          end_date: end_date,
          status: :completed
        )

      assert {:ok, ~D[2021-05-12], ~D[2023-10-25]} =
               Imported.clamp_dates(site, ~D[2019-03-21], ~D[2024-01-12])
    end

    test "does not depend on the order of insertion of site imports (regression fix)" do
      site = insert(:site)

      _existing_import1 =
        insert(:site_import,
          site: site,
          start_date: ~D[2020-10-14],
          end_date: ~D[2024-04-01],
          status: :completed
        )

      _existing_import2 =
        insert(:site_import,
          site: site,
          start_date: ~D[2012-01-18],
          end_date: ~D[2018-03-09],
          status: :completed
        )

      assert {:ok, ~D[2018-03-09], ~D[2020-10-14]} =
               Imported.clamp_dates(site, ~D[2012-01-18], ~D[2018-03-09])
    end

    test "does not alter the dates when there are no imports and no native stats" do
      site = insert(:site)

      assert {:ok, ~D[2021-05-12], ~D[2024-01-12]} =
               Imported.clamp_dates(site, ~D[2021-05-12], ~D[2024-01-12])
    end

    test "ignores input date range difference smaller than 2 days" do
      site = insert(:site)

      assert {:error, :no_time_window} =
               Imported.clamp_dates(site, ~D[2024-01-12], ~D[2024-01-12])

      assert {:error, :no_time_window} =
               Imported.clamp_dates(site, ~D[2024-01-12], ~D[2024-01-13])

      assert {:ok, ~D[2024-01-12], ~D[2024-01-14]} =
               Imported.clamp_dates(site, ~D[2024-01-12], ~D[2024-01-14])
    end

    test "ignores imports with date range difference smaller than 2 days" do
      site = insert(:site)

      start_date = ~D[2024-01-12]
      end_date = ~D[2024-01-13]

      _existing_import =
        insert(:site_import,
          site: site,
          start_date: start_date,
          end_date: end_date,
          status: :completed
        )

      assert {:ok, ~D[2021-04-22], ~D[2024-03-14]} =
               Imported.clamp_dates(site, ~D[2021-04-22], ~D[2024-03-14])
    end

    test "returns no time window when input range starts after native stats start date" do
      site = insert(:site)

      populate_stats(site, [
        build(:pageview, timestamp: ~N[2023-10-25 15:58:00])
      ])

      assert {:error, :no_time_window} =
               Imported.clamp_dates(site, ~D[2023-10-28], ~D[2024-01-13])
    end

    test "returns no time window when input range starts less than 2 days before native stats start date" do
      site = insert(:site)

      populate_stats(site, [
        build(:pageview, timestamp: ~N[2023-10-25 15:58:00])
      ])

      assert {:error, :no_time_window} =
               Imported.clamp_dates(site, ~D[2023-10-24], ~D[2024-01-13])
    end

    test "crops time range at native stats start date when effective range is 2 days or longer" do
      site = insert(:site)

      populate_stats(site, [
        build(:pageview, timestamp: ~N[2023-10-25 15:58:00])
      ])

      assert {:ok, ~D[2023-10-23], ~D[2023-10-25]} =
               Imported.clamp_dates(site, ~D[2023-10-23], ~D[2024-01-13])
    end

    test "returns no time window error when date range overlaps with existing import and stats completely" do
      site = insert(:site)

      populate_stats(site, [
        build(:pageview, timestamp: ~N[2023-10-25 15:58:00])
      ])

      start_date = ~D[2016-04-03]
      end_date = ~D[2023-10-25]

      _existing_import =
        insert(:site_import,
          site: site,
          start_date: start_date,
          end_date: end_date,
          status: :completed
        )

      assert {:error, :no_time_window} =
               Imported.clamp_dates(site, ~D[2021-04-11], ~D[2024-01-12])
    end
  end
end
