defmodule Raxol.Terminal.Graphics.KittyProtocolTest do
  use ExUnit.Case, async: true
  
  alias Raxol.Terminal.Graphics.KittyProtocol

  describe "transmit_image/2" do
    test "transmits PNG image successfully" do
      # Create mock PNG data (simplified)
      png_data = <<137, 80, 78, 71, 13, 10, 26, 10>> # PNG header
      options = %{format: :png, width: 100, height: 100}
      
      assert {:ok, command} = KittyProtocol.transmit_image(png_data, options)
      assert String.starts_with?(command, "\033_G")
      assert String.ends_with?(command, "\033\\")
      assert String.contains?(command, "a=T") # Transmit action
      assert String.contains?(command, "f=100") # PNG format
    end

    test "transmits raw RGB data successfully" do
      rgb_data = <<255, 0, 0, 0, 255, 0, 0, 0, 255>> # 3 pixels RGB
      options = %{format: :rgb, width: 3, height: 1}
      
      assert {:ok, command} = KittyProtocol.transmit_image(rgb_data, options)
      assert String.contains?(command, "f=24") # RGB format
    end

    test "handles chunked transmission" do
      large_data = String.duplicate("test", 2000) # Create large data
      options = %{format: :png, chunked: true, chunk_size: 100}
      
      assert {:ok, commands} = KittyProtocol.transmit_image(large_data, options)
      assert is_binary(commands)
      
      # Should contain multiple commands with m=1 (more chunks) and final m=0
      assert String.contains?(commands, "m=1")
      assert String.contains?(commands, "m=0")
    end

    test "validates image format" do
      data = "invalid_data"
      options = %{format: :invalid_format}
      
      assert {:error, :invalid_format} = KittyProtocol.transmit_image(data, options)
    end

    test "validates RGB data size" do
      invalid_rgb = <<255, 0>> # Incomplete RGB pixel
      options = %{format: :rgb, width: 1, height: 1}
      
      assert {:error, :invalid_rgb_data_size} = KittyProtocol.transmit_image(invalid_rgb, options)
    end
  end

  describe "display_image/2" do
    test "creates display command with positioning" do
      image_id = 42
      options = %{x: 10, y: 20, width: 300, height: 200}
      
      assert {:ok, command} = KittyProtocol.display_image(image_id, options)
      assert String.contains?(command, "a=p") # Put/display action
      assert String.contains?(command, "i=42") # Image ID
      assert String.contains?(command, "x=10") # X position
      assert String.contains?(command, "y=20") # Y position
    end

    test "handles optional display parameters" do
      image_id = 1
      options = %{z_index: 5}
      
      assert {:ok, command} = KittyProtocol.display_image(image_id, options)
      assert String.contains?(command, "z=5") # Z-index
    end
  end

  describe "delete_image/1" do
    test "deletes specific image" do
      image_id = 123
      
      assert {:ok, command} = KittyProtocol.delete_image(image_id)
      assert String.contains?(command, "a=d") # Delete action
      assert String.contains?(command, "i=123") # Image ID
    end

    test "deletes all images" do
      assert {:ok, command} = KittyProtocol.delete_image(:all)
      assert String.contains?(command, "a=d") # Delete action
      assert String.contains?(command, "d=A") # All images
    end

    test "validates image ID" do
      assert {:error, :invalid_image_id} = KittyProtocol.delete_image(-1)
      assert {:error, :invalid_image_id} = KittyProtocol.delete_image("invalid")
    end
  end

  describe "query_capabilities/0" do
    test "returns capability query command" do
      assert {:ok, command} = KittyProtocol.query_capabilities()
      assert String.contains?(command, "a=q") # Query action
      assert String.starts_with?(command, "\033_G")
    end
  end

  describe "create_animation/2" do
    test "creates animation from multiple frames" do
      frame1 = "frame1_data"
      frame2 = "frame2_data"
      frames = [frame1, frame2]
      options = %{frame_delay: 100, loop_count: -1}
      
      assert {:ok, commands} = KittyProtocol.create_animation(frames, options)
      assert is_list(commands)
      assert length(commands) == 2
    end

    test "handles empty frames list" do
      assert {:ok, []} = KittyProtocol.create_animation([], %{})
    end
  end

  describe "detect_support/0" do
    test "detects Kitty terminal support" do
      # Mock TERM environment variable
      original_term = System.get_env("TERM")
      System.put_env("TERM", "xterm-kitty")
      
      assert {:ok, :supported} = KittyProtocol.detect_support()
      
      # Restore original environment
      case original_term do
        nil -> System.delete_env("TERM")
        term -> System.put_env("TERM", term)
      end
    end

    test "detects WezTerm support" do
      # Mock WezTerm environment
      System.put_env("WEZTERM_EXECUTABLE", "/usr/bin/wezterm")
      
      assert {:ok, :supported} = KittyProtocol.detect_support()
      
      System.delete_env("WEZTERM_EXECUTABLE")
    end

    test "detects unsupported terminal" do
      # Mock unsupported terminal
      original_term = System.get_env("TERM")
      System.put_env("TERM", "dumb")
      
      assert {:ok, :unsupported} = KittyProtocol.detect_support()
      
      # Restore original environment
      case original_term do
        nil -> System.delete_env("TERM")
        term -> System.put_env("TERM", term)
      end
    end
  end

  describe "private functions" do
    test "validates transmission options correctly" do
      # Access private function for testing
      valid_options = %{format: :png, compression: :none}
      # Note: In real implementation, we'd test through public interface
      # This is a conceptual test structure
      
      # Test would verify option validation logic
      assert true # Placeholder
    end

    test "builds control data correctly" do
      # Test control data generation
      # Would verify proper key=value formatting
      assert true # Placeholder
    end

    test "chunks binary data properly" do
      # Test binary data chunking logic
      data = String.duplicate("x", 1000)
      # Would test chunking with various sizes
      assert true # Placeholder
    end
  end

  describe "error handling" do
    test "handles file read errors gracefully" do
      non_existent_file = "/tmp/non_existent_file.png"
      
      assert {:error, {:file_read_error, :enoent}} = 
        KittyProtocol.transmit_image(non_existent_file, %{format: :png})
    end

    test "handles invalid image data" do
      invalid_data = "not_valid_image_data"
      options = %{format: :rgba, width: 10, height: 10}
      
      assert {:error, :invalid_rgba_data_size} = 
        KittyProtocol.transmit_image(invalid_data, options)
    end

    test "handles oversized image IDs" do
      huge_id = 5_000_000_000 # Larger than 2^32 - 1
      
      assert {:error, :invalid_image_id} = 
        KittyProtocol.delete_image(huge_id)
    end
  end

  describe "integration scenarios" do
    test "full transmit and display workflow" do
      # Test complete workflow
      image_data = "test_png_data"
      
      # 1. Transmit image
      {:ok, transmit_cmd} = KittyProtocol.transmit_image(image_data, %{
        format: :png, 
        width: 200, 
        height: 150,
        image_id: 100
      })
      
      assert String.contains?(transmit_cmd, "i=100")
      
      # 2. Display image
      {:ok, display_cmd} = KittyProtocol.display_image(100, %{
        x: 50, 
        y: 25, 
        width: 200, 
        height: 150
      })
      
      assert String.contains?(display_cmd, "i=100")
      assert String.contains?(display_cmd, "x=50")
      
      # 3. Delete image
      {:ok, delete_cmd} = KittyProtocol.delete_image(100)
      assert String.contains?(delete_cmd, "i=100")
    end
  end
end