defmodule ToweropsWeb.TimeHelpersTest do
  use ExUnit.Case, async: true

  alias ToweropsWeb.TimeHelpers

  describe "to_user_timezone/2" do
    test "returns shifted datetime and abbreviation for known timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert {:ok, shifted, "EST"} = TimeHelpers.to_user_timezone(datetime, "America/New_York")
      # UTC-5 => 09:34
      assert shifted.hour == 9
      assert shifted.minute == 34
    end

    test "returns unchanged datetime for UTC" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert {:ok, shifted, "UTC"} = TimeHelpers.to_user_timezone(datetime, "UTC")
      assert shifted.hour == 14
      assert shifted.minute == 34
    end

    test "handles positive offset timezone (Asia/Tokyo, UTC+9)" do
      datetime = ~U[2026-01-15 14:00:00Z]
      assert {:ok, shifted, "JST"} = TimeHelpers.to_user_timezone(datetime, "Asia/Tokyo")
      assert shifted.hour == 23
    end

    test "handles date rollover forward (Asia/Tokyo near midnight)" do
      datetime = ~U[2026-01-15 20:00:00Z]
      assert {:ok, shifted, "JST"} = TimeHelpers.to_user_timezone(datetime, "Asia/Tokyo")
      # 20:00 + 9h = 05:00 next day
      assert shifted.hour == 5
      assert shifted.day == 16
    end

    test "handles date rollover backward (America/Los_Angeles early UTC)" do
      datetime = ~U[2026-01-15 02:00:00Z]
      assert {:ok, shifted, "PST"} = TimeHelpers.to_user_timezone(datetime, "America/Los_Angeles")
      # 02:00 - 8h = 18:00 previous day
      assert shifted.hour == 18
      assert shifted.day == 14
    end

    test "returns error for unknown timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert {:error, :unknown_timezone} = TimeHelpers.to_user_timezone(datetime, "Invalid/Zone")
    end

    test "handles all supported timezones without error" do
      datetime = ~U[2026-01-15 12:00:00Z]

      timezones = [
        "UTC",
        "America/New_York",
        "America/Chicago",
        "America/Denver",
        "America/Los_Angeles",
        "America/Phoenix",
        "Europe/London",
        "Europe/Paris",
        "Europe/Berlin",
        "Asia/Tokyo",
        "Asia/Shanghai",
        "Asia/Dubai",
        "Australia/Sydney"
      ]

      for tz <- timezones do
        assert {:ok, _shifted, _abbr} = TimeHelpers.to_user_timezone(datetime, tz)
      end
    end
  end

  describe "format_time_ago/1" do
    test "returns 'Never' for nil datetime" do
      assert TimeHelpers.format_time_ago(nil) == "Never"
    end

    test "formats seconds ago" do
      datetime = DateTime.add(DateTime.utc_now(), -30, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result =~ "s ago"
    end

    test "formats 0 seconds ago for very recent datetime" do
      datetime = DateTime.utc_now()
      result = TimeHelpers.format_time_ago(datetime)
      assert result =~ "s ago"
    end

    test "formats minutes ago" do
      datetime = DateTime.add(DateTime.utc_now(), -120, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "2m ago"
    end

    test "formats 1 minute ago at 60 seconds" do
      datetime = DateTime.add(DateTime.utc_now(), -65, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "1m ago"
    end

    test "formats hours ago" do
      datetime = DateTime.add(DateTime.utc_now(), -7200, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "2h ago"
    end

    test "formats 1 hour ago at 3600 seconds" do
      datetime = DateTime.add(DateTime.utc_now(), -3600, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "1h ago"
    end

    test "formats days ago" do
      datetime = DateTime.add(DateTime.utc_now(), -172_800, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "2d ago"
    end

    test "formats 1 day ago at 86400 seconds" do
      datetime = DateTime.add(DateTime.utc_now(), -86_400, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "1d ago"
    end

    test "formats months ago" do
      # 60 days = 5,184,000 seconds
      datetime = DateTime.add(DateTime.utc_now(), -5_184_000, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "2mo ago"
    end

    test "formats 1 month ago at 30 days" do
      # 30 days = 2,592,000 seconds
      datetime = DateTime.add(DateTime.utc_now(), -2_592_000, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "1mo ago"
    end

    test "formats years ago" do
      # 2 years = 63,072,000 seconds
      datetime = DateTime.add(DateTime.utc_now(), -63_072_000, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "2y ago"
    end

    test "formats 1 year ago at 365 days" do
      # 365 days = 31,536,000 seconds
      datetime = DateTime.add(DateTime.utc_now(), -31_536_000, :second)
      result = TimeHelpers.format_time_ago(datetime)
      assert result == "1y ago"
    end

    test "handles future datetime by treating it as just now (clock skew)" do
      # Simulate clock skew where datetime is in the future
      datetime = DateTime.add(DateTime.utc_now(), 5, :second)
      result = TimeHelpers.format_time_ago(datetime)
      # Should not crash and should show "0s ago" or similar
      assert result =~ "s ago"
    end

    test "handles future datetime by many seconds (edge case)" do
      # Extreme clock skew - datetime 30 seconds in the future
      datetime = DateTime.add(DateTime.utc_now(), 30, :second)
      result = TimeHelpers.format_time_ago(datetime)
      # Should clamp to 0 and show as recent
      assert result =~ "s ago"
    end
  end

  describe "format_datetime/1" do
    test "formats datetime in UTC with 24h format by default" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_datetime(datetime) == "Jan 15, 2026 at 14:34:00 UTC"
    end

    test "returns 'Never' for nil" do
      assert TimeHelpers.format_datetime(nil) == "Never"
    end
  end

  describe "format_datetime/2" do
    test "formats datetime in UTC when timezone is nil" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_datetime(datetime, nil) == "Jan 15, 2026 at 14:34:00 UTC"
    end

    test "formats datetime in UTC when timezone is explicitly UTC" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_datetime(datetime, "UTC") == "Jan 15, 2026 at 14:34:00 UTC"
    end

    test "converts datetime to America/New_York timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      result = TimeHelpers.format_datetime(datetime, "America/New_York")
      assert result == "Jan 15, 2026 at 09:34:00 EST"
    end

    test "converts datetime to America/Los_Angeles timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      result = TimeHelpers.format_datetime(datetime, "America/Los_Angeles")
      assert result == "Jan 15, 2026 at 06:34:00 PST"
    end

    test "falls back to UTC for invalid timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_datetime(datetime, "Invalid/Timezone") == "Jan 15, 2026 at 14:34:00 UTC"
    end

    test "returns 'Never' for nil datetime" do
      assert TimeHelpers.format_datetime(nil, "America/New_York") == "Never"
      assert TimeHelpers.format_datetime(nil, nil) == "Never"
    end
  end

  describe "format_datetime/3" do
    test "formats datetime in 12h format" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_datetime(datetime, "UTC", "12h") == "Jan 15, 2026 at 02:34 PM UTC"
    end

    test "formats datetime in 24h format" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_datetime(datetime, "UTC", "24h") == "Jan 15, 2026 at 14:34:00 UTC"
    end

    test "formats datetime in 24h format with timezone conversion" do
      datetime = ~U[2026-01-15 14:34:00Z]
      result = TimeHelpers.format_datetime(datetime, "America/New_York", "24h")
      assert result == "Jan 15, 2026 at 09:34:00 EST"
    end

    test "formats datetime in 12h format with timezone conversion" do
      datetime = ~U[2026-01-15 14:34:00Z]
      result = TimeHelpers.format_datetime(datetime, "America/New_York", "12h")
      assert result == "Jan 15, 2026 at 09:34 AM EST"
    end

    test "returns 'Never' for nil datetime" do
      assert TimeHelpers.format_datetime(nil, "UTC", "24h") == "Never"
      assert TimeHelpers.format_datetime(nil, "UTC", "12h") == "Never"
    end

    test "handles nil timezone by defaulting to UTC" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_datetime(datetime, nil, "24h") == "Jan 15, 2026 at 14:34:00 UTC"
    end

    test "falls back to UTC for invalid timezone in 24h format" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_datetime(datetime, "Fake/Zone", "24h") == "Jan 15, 2026 at 14:34:00 UTC"
    end

    test "formats midnight correctly in 12h format" do
      datetime = ~U[2026-01-15 00:00:00Z]
      assert TimeHelpers.format_datetime(datetime, "UTC", "12h") == "Jan 15, 2026 at 12:00 AM UTC"
    end

    test "formats noon correctly in 12h format" do
      datetime = ~U[2026-01-15 12:00:00Z]
      assert TimeHelpers.format_datetime(datetime, "UTC", "12h") == "Jan 15, 2026 at 12:00 PM UTC"
    end
  end

  describe "format_date/1" do
    test "formats date in UTC by default" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_date(datetime) == "Jan 15, 2026"
    end

    test "returns 'Never' for nil" do
      assert TimeHelpers.format_date(nil) == "Never"
    end
  end

  describe "format_date/2" do
    test "formats date in UTC when timezone is nil" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_date(datetime, nil) == "Jan 15, 2026"
    end

    test "formats date in UTC when timezone is explicitly UTC" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_date(datetime, "UTC") == "Jan 15, 2026"
    end

    test "converts to different date in America/Los_Angeles for early UTC times" do
      datetime = ~U[2026-01-15 02:00:00Z]
      assert TimeHelpers.format_date(datetime, "America/Los_Angeles") == "Jan 14, 2026"
    end

    test "keeps same date for late UTC times in America/Los_Angeles" do
      datetime = ~U[2026-01-15 20:00:00Z]
      assert TimeHelpers.format_date(datetime, "America/Los_Angeles") == "Jan 15, 2026"
    end

    test "rolls date forward for positive offset timezones near midnight" do
      # 23:00 UTC on Jan 15 + 9h (Tokyo) = 08:00 Jan 16
      datetime = ~U[2026-01-15 23:00:00Z]
      assert TimeHelpers.format_date(datetime, "Asia/Tokyo") == "Jan 16, 2026"
    end

    test "falls back to UTC for invalid timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_date(datetime, "Invalid/Timezone") == "Jan 15, 2026"
    end

    test "returns 'Never' for nil datetime" do
      assert TimeHelpers.format_date(nil, "America/New_York") == "Never"
      assert TimeHelpers.format_date(nil, nil) == "Never"
    end
  end

  describe "format_iso8601/1" do
    test "formats datetime in UTC with 24h format by default" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_iso8601(datetime) == "2026-01-15 14:34:00 UTC"
    end

    test "returns 'Never' for nil" do
      assert TimeHelpers.format_iso8601(nil) == "Never"
    end
  end

  describe "format_iso8601/2" do
    test "formats datetime in UTC when timezone is nil" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_iso8601(datetime, nil) == "2026-01-15 14:34:00 UTC"
    end

    test "formats datetime in UTC when timezone is explicitly UTC" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_iso8601(datetime, "UTC") == "2026-01-15 14:34:00 UTC"
    end

    test "converts datetime to America/New_York timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      result = TimeHelpers.format_iso8601(datetime, "America/New_York")
      assert result == "2026-01-15 09:34:00 EST"
    end

    test "converts datetime to America/Los_Angeles timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      result = TimeHelpers.format_iso8601(datetime, "America/Los_Angeles")
      assert result == "2026-01-15 06:34:00 PST"
    end

    test "falls back to UTC for invalid timezone" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_iso8601(datetime, "Invalid/Timezone") == "2026-01-15 14:34:00 UTC"
    end

    test "returns 'Never' for nil datetime" do
      assert TimeHelpers.format_iso8601(nil, "America/New_York") == "Never"
      assert TimeHelpers.format_iso8601(nil, nil) == "Never"
    end
  end

  describe "format_iso8601/3" do
    test "formats datetime in 24h format" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_iso8601(datetime, "UTC", "24h") == "2026-01-15 14:34:00 UTC"
    end

    test "formats datetime in 12h format" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_iso8601(datetime, "UTC", "12h") == "2026-01-15 02:34 PM UTC"
    end

    test "formats datetime in 12h format with timezone conversion" do
      datetime = ~U[2026-01-15 14:34:00Z]
      result = TimeHelpers.format_iso8601(datetime, "America/New_York", "12h")
      assert result == "2026-01-15 09:34 AM EST"
    end

    test "formats datetime in 24h format with timezone conversion" do
      datetime = ~U[2026-01-15 14:34:00Z]
      result = TimeHelpers.format_iso8601(datetime, "America/New_York", "24h")
      assert result == "2026-01-15 09:34:00 EST"
    end

    test "returns 'Never' for nil datetime" do
      assert TimeHelpers.format_iso8601(nil, "UTC", "24h") == "Never"
      assert TimeHelpers.format_iso8601(nil, "UTC", "12h") == "Never"
    end

    test "handles nil timezone by defaulting to UTC" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_iso8601(datetime, nil, "12h") == "2026-01-15 02:34 PM UTC"
    end

    test "falls back to UTC for invalid timezone in 12h format" do
      datetime = ~U[2026-01-15 14:34:00Z]
      assert TimeHelpers.format_iso8601(datetime, "Fake/Zone", "12h") == "2026-01-15 02:34 PM UTC"
    end
  end

  describe "format_utc_hour/1" do
    test "formats hour in UTC with AM" do
      result = TimeHelpers.format_utc_hour(7)
      assert result == "7:00 AM UTC"
    end

    test "formats midnight as 12 AM" do
      result = TimeHelpers.format_utc_hour(0)
      assert result == "12:00 AM UTC"
    end

    test "formats noon as 12 PM" do
      result = TimeHelpers.format_utc_hour(12)
      assert result == "12:00 PM UTC"
    end

    test "formats afternoon hours with PM" do
      result = TimeHelpers.format_utc_hour(15)
      assert result == "3:00 PM UTC"
    end

    test "formats hour 23 correctly" do
      result = TimeHelpers.format_utc_hour(23)
      assert result == "11:00 PM UTC"
    end
  end

  describe "format_utc_hour/2" do
    test "converts UTC hour to America/New_York" do
      # 7 UTC - 5h = 2 AM EST
      result = TimeHelpers.format_utc_hour(7, "America/New_York")
      assert result == "2:00 AM EST"
    end

    test "converts UTC hour to Asia/Tokyo" do
      # 7 UTC + 9h = 16:00 JST = 4 PM
      result = TimeHelpers.format_utc_hour(7, "Asia/Tokyo")
      assert result == "4:00 PM JST"
    end

    test "converts midnight UTC to America/Los_Angeles" do
      # 0 UTC - 8h = 16:00 previous day PST = 4 PM
      result = TimeHelpers.format_utc_hour(0, "America/Los_Angeles")
      assert result == "4:00 PM PST"
    end

    test "converts noon UTC to Europe/Paris" do
      # 12 UTC + 1h = 13:00 CET = 1 PM
      result = TimeHelpers.format_utc_hour(12, "Europe/Paris")
      assert result == "1:00 PM CET"
    end

    test "converts UTC hour to Australia/Sydney" do
      # 14 UTC + 10h = 24 => 0 next day = 12 AM
      result = TimeHelpers.format_utc_hour(14, "Australia/Sydney")
      assert result == "12:00 AM AEST"
    end

    test "falls back to UTC for invalid timezone" do
      result = TimeHelpers.format_utc_hour(7, "Invalid/Zone")
      assert result == "7:00 AM UTC"
    end

    test "handles UTC explicitly" do
      result = TimeHelpers.format_utc_hour(15, "UTC")
      assert result == "3:00 PM UTC"
    end
  end

  describe "format_utc_hour/3" do
    test "formats hour in 24h format" do
      result = TimeHelpers.format_utc_hour(15, "UTC", "24h")
      assert result == "15:00 UTC"
    end

    test "formats midnight in 24h format" do
      result = TimeHelpers.format_utc_hour(0, "UTC", "24h")
      assert result == "0:00 UTC"
    end

    test "formats hour in 12h format" do
      result = TimeHelpers.format_utc_hour(15, "UTC", "12h")
      assert result == "3:00 PM UTC"
    end

    test "formats hour in 24h format with timezone conversion" do
      result = TimeHelpers.format_utc_hour(7, "America/New_York", "24h")
      assert result == "2:00 EST"
    end
  end

  describe "format_datetime_for_scope/2" do
    test "formats datetime using scope timezone and time_format" do
      scope = %{timezone: "America/New_York", time_format: "12h"}
      datetime = ~U[2026-01-15 14:34:00Z]

      result = TimeHelpers.format_datetime_for_scope(datetime, scope)
      assert result == "Jan 15, 2026 at 09:34 AM EST"
    end

    test "formats datetime with 24h scope" do
      scope = %{timezone: "UTC", time_format: "24h"}
      datetime = ~U[2026-01-15 14:34:00Z]

      result = TimeHelpers.format_datetime_for_scope(datetime, scope)
      assert result == "Jan 15, 2026 at 14:34:00 UTC"
    end

    test "returns 'Never' for nil datetime" do
      scope = %{timezone: "UTC", time_format: "24h"}
      assert TimeHelpers.format_datetime_for_scope(nil, scope) == "Never"
    end
  end
end
