defmodule Towerops.Workers.BackupSummaryWorkerTest do
  use Towerops.DataCase, async: false

  import ExUnit.CaptureLog
  import Towerops.AccountsFixtures

  alias Towerops.Devices
  alias Towerops.Devices.BackupRequests
  alias Towerops.Organizations
  alias Towerops.Sites
  alias Towerops.Workers.BackupSummaryWorker

  require Logger

  setup do
    # Ensure Logger level is set to info for capturing logs
    original_level = Logger.level()
    Logger.configure(level: :info)

    on_exit(fn ->
      Logger.configure(level: original_level)
    end)

    :ok
  end

  describe "perform/1" do
    test "returns :ok when no backup requests exist" do
      # Execute the worker
      assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
    end

    test "logs summary when only successful backups exist" do
      device = insert_device()

      # Create successful requests
      {:ok, req} = BackupRequests.create_request(device.id, "job1")
      BackupRequests.update_request_status(req.id, "success", nil)

      # Execute the worker and capture logs
      log =
        capture_log(fn ->
          assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
        end)

      # Verify summary was logged
      assert log =~ "Backup summary for past 24 hours"
      refute log =~ "Backup failures detected"
    end

    test "logs warning when failed backups exist" do
      device = insert_device()

      # Create a failed request
      {:ok, req} = BackupRequests.create_request(device.id, "job_failed")
      BackupRequests.update_request_status(req.id, "failed", "Connection error")

      # Execute the worker and capture logs
      log =
        capture_log(fn ->
          assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
        end)

      # Verify warning was logged
      assert log =~ "Backup summary for past 24 hours"
      assert log =~ "Backup failures detected"
    end

    test "logs warning when timeout backups exist" do
      device = insert_device()

      # Create a timeout request
      {:ok, req} = BackupRequests.create_request(device.id, "job_timeout")
      BackupRequests.update_request_status(req.id, "timeout", "Request timed out")

      # Execute the worker and capture logs
      log =
        capture_log(fn ->
          assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
        end)

      # Verify warning was logged
      assert log =~ "Backup summary for past 24 hours"
      assert log =~ "Backup failures detected"
    end

    test "logs warning when both failed and timeout backups exist" do
      device = insert_device()

      # Create a failed request
      {:ok, req1} = BackupRequests.create_request(device.id, "job_failed")
      BackupRequests.update_request_status(req1.id, "failed", "Connection error")

      # Create a timeout request
      {:ok, req2} = BackupRequests.create_request(device.id, "job_timeout")
      BackupRequests.update_request_status(req2.id, "timeout", "Request timed out")

      # Execute the worker and capture logs
      log =
        capture_log(fn ->
          assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
        end)

      # Verify warning was logged
      assert log =~ "Backup summary for past 24 hours"
      assert log =~ "Backup failures detected"
    end

    test "includes device IDs in warning log" do
      device1 = insert_device()
      device2 = insert_device()

      # Create failed requests for multiple devices
      {:ok, req1} = BackupRequests.create_request(device1.id, "job1")
      BackupRequests.update_request_status(req1.id, "failed", "Error")

      {:ok, req2} = BackupRequests.create_request(device2.id, "job2")
      BackupRequests.update_request_status(req2.id, "timeout", "Timeout")

      # Execute the worker and capture logs
      log =
        capture_log(fn ->
          assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
        end)

      # Verify warnings were logged (metadata fields like device IDs aren't captured in text logs)
      assert log =~ "Backup summary for past 24 hours"
      assert log =~ "Backup failures detected"
    end

    test "only includes requests from past 24 hours" do
      device = insert_device()

      # Create an old failed request (30 hours ago)
      {:ok, old_req} = BackupRequests.create_request(device.id, "job_old")

      old_req
      |> Ecto.Changeset.change(
        requested_at: DateTime.utc_now() |> DateTime.add(-30 * 60 * 60, :second) |> DateTime.truncate(:second)
      )
      |> Repo.update!()

      BackupRequests.update_request_status(old_req.id, "failed", "Old error")

      # Create a recent failed request (12 hours ago)
      {:ok, recent_req} = BackupRequests.create_request(device.id, "job_recent")

      recent_req
      |> Ecto.Changeset.change(
        requested_at: DateTime.utc_now() |> DateTime.add(-12 * 60 * 60, :second) |> DateTime.truncate(:second)
      )
      |> Repo.update!()

      BackupRequests.update_request_status(recent_req.id, "failed", "Recent error")

      # Execute the worker and capture logs
      log =
        capture_log(fn ->
          assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
        end)

      # Verify summary and failure warning are logged (only recent failure should be counted)
      assert log =~ "Backup summary for past 24 hours"
      assert log =~ "Backup failures detected"
    end

    test "handles mixed statuses correctly" do
      device = insert_device()

      # Create requests with various statuses
      {:ok, req1} = BackupRequests.create_request(device.id, "job_success")
      BackupRequests.update_request_status(req1.id, "success", nil)

      {:ok, req2} = BackupRequests.create_request(device.id, "job_failed")
      BackupRequests.update_request_status(req2.id, "failed", "Error")

      {:ok, req3} = BackupRequests.create_request(device.id, "job_timeout")
      BackupRequests.update_request_status(req3.id, "timeout", "Timeout")

      {:ok, _req4} = BackupRequests.create_request(device.id, "job_pending")
      # Leave as pending

      # Execute the worker and capture logs
      log =
        capture_log(fn ->
          assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
        end)

      # Verify summary and failure warnings are logged
      assert log =~ "Backup summary for past 24 hours"
      assert log =~ "Backup failures detected"
    end

    test "does not log warning when all backups are successful or pending" do
      device = insert_device()

      # Create successful and pending requests
      {:ok, req1} = BackupRequests.create_request(device.id, "job_success")
      BackupRequests.update_request_status(req1.id, "success", nil)

      {:ok, _req2} = BackupRequests.create_request(device.id, "job_pending")
      # Leave as pending

      # Execute the worker and capture logs
      log =
        capture_log(fn ->
          assert :ok = BackupSummaryWorker.perform(%Oban.Job{args: %{}})
        end)

      # Verify no warning was logged
      assert log =~ "Backup summary for past 24 hours"
      refute log =~ "Backup failures detected"
    end
  end

  # Helper function to create a device
  defp insert_device do
    user = user_fixture()
    {:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
    {:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})

    {:ok, device} =
      Devices.create_device(%{
        name: "Test Device #{System.unique_integer()}",
        ip_address: "192.168.1.#{:rand.uniform(254)}",
        site_id: site.id,
        organization_id: organization.id
      })

    device
  end
end
