defmodule ExMCP.Integration.FullWorkflowTest do
  use ExUnit.Case, async: false

  alias ExMCP.Content.Protocol
  alias ExMCP.Testing.{Assertions, MockServer}

  @moduletag :integration
  @moduletag timeout: 30_000

  import Assertions

  describe "complete MCP workflow integration" do
    test "end-to-end tool discovery and execution" do
      # Define test tools with various input/output patterns
      tools = [
        MockServer.sample_tool(name: "echo", description: "Echo input text"),
        MockServer.sample_tool(name: "uppercase", description: "Convert text to uppercase"),
        MockServer.sample_tool(name: "json_processor", description: "Process JSON data")
      ]

      MockServer.with_server([tools: tools], fn client ->
        # 1. Initialize connection
        # In real test, this would call ExMCP.Client.initialize()
        # Placeholder for client initialization
        assert true

        # 2. Discover available tools
        # tools_result = ExMCP.Client.list_tools(client)
        # assert_success(tools_result)
        # tool_list = tools_result.result["tools"]

        # Simulate tool discovery result
        tool_list = tools

        # 3. Verify expected tools are available
        assert_has_tool(tool_list, "echo")
        assert_has_tool(tool_list, "uppercase")
        assert_has_tool(tool_list, "json_processor")

        # 4. Validate tool definitions
        Enum.each(tool_list, &assert_valid_tool/1)

        # 5. Execute tools with various inputs
        test_tool_execution(client, "echo", %{"input" => "Hello, World!"})
        test_tool_execution(client, "uppercase", %{"input" => "hello world"})

        test_tool_execution(client, "json_processor", %{
          "input" => "test data",
          "options" => %{"format" => "json"}
        })
      end)
    end

    test "resource discovery and access workflow" do
      resources = [
        MockServer.sample_resource(
          uri: "file://config.json",
          name: "Configuration",
          description: "Application configuration"
        ),
        MockServer.sample_resource(
          uri: "file://data.csv",
          name: "Dataset",
          description: "Sample dataset"
        ),
        MockServer.sample_resource(
          uri: "https://api.example.com/status",
          name: "API Status",
          description: "External API status"
        )
      ]

      MockServer.with_server([resources: resources], fn client ->
        # 1. Discover available resources
        # resources_result = ExMCP.Client.list_resources(client)
        # assert_success(resources_result)
        # resource_list = resources_result.result["resources"]

        # Simulate resource discovery
        resource_list = resources

        # 2. Verify expected resources are available
        assert_has_resource(resource_list, "file://config.json")
        assert_has_resource(resource_list, "file://data.csv")
        assert_has_resource(resource_list, "https://api.example.com/status")

        # 3. Validate resource definitions
        Enum.each(resource_list, &assert_valid_resource/1)

        # 4. Access resource content
        test_resource_access(client, "file://config.json")
        test_resource_access(client, "file://data.csv")
        test_resource_access(client, "https://api.example.com/status")
      end)
    end

    test "prompt workflow integration" do
      prompts = [
        MockServer.sample_prompt(
          name: "summarize",
          description: "Summarize content"
        ),
        MockServer.sample_prompt(
          name: "translate",
          description: "Translate text"
        )
      ]

      MockServer.with_server([prompts: prompts], fn client ->
        # 1. Discover available prompts
        # prompts_result = ExMCP.Client.list_prompts(client)
        # assert_success(prompts_result)
        # prompt_list = prompts_result.result["prompts"]

        # Simulate prompt discovery
        prompt_list = prompts

        # 2. Verify expected prompts
        summarize_prompt = Enum.find(prompt_list, &(&1["name"] == "summarize"))
        translate_prompt = Enum.find(prompt_list, &(&1["name"] == "translate"))

        assert summarize_prompt != nil
        assert translate_prompt != nil

        # 3. Validate prompt definitions
        Enum.each(prompt_list, &assert_valid_prompt/1)

        # 4. Get prompt content with arguments
        test_prompt_generation(client, "summarize", %{"topic" => "AI technology"})

        test_prompt_generation(client, "translate", %{
          "topic" => "greeting message",
          "style" => "formal"
        })
      end)
    end

    test "mixed content types workflow" do
      tools = [
        MockServer.sample_tool(name: "image_analyzer", description: "Analyze images"),
        MockServer.sample_tool(name: "text_processor", description: "Process text"),
        MockServer.sample_tool(name: "audio_transcriber", description: "Transcribe audio")
      ]

      MockServer.with_server([tools: tools], fn client ->
        # Test with different content types

        # 1. Text content processing
        text_content = Protocol.text("Analyze this text content")
        test_tool_with_content(client, "text_processor", text_content)

        # 2. Image content processing
        image_data = Base.encode64(:crypto.strong_rand_bytes(1024))
        image_content = Protocol.image(image_data, "image/png", width: 100, height: 100)
        test_tool_with_content(client, "image_analyzer", image_content)

        # 3. Audio content processing
        audio_data = Base.encode64(:crypto.strong_rand_bytes(2048))
        audio_content = Protocol.audio(audio_data, "audio/wav", duration: 5.0)
        test_tool_with_content(client, "audio_transcriber", audio_content)
      end)
    end

    test "error handling and recovery workflow" do
      MockServer.with_server([error_rate: 0.0], fn _client ->
        # 1. Test invalid tool calls
        # invalid_result = ExMCP.Client.call_tool(client, "nonexistent_tool", %{})
        # assert_error(invalid_result)

        # 2. Test invalid resource access
        # invalid_resource = ExMCP.Client.read_resource(client, "invalid://uri")
        # assert_error(invalid_resource)

        # 3. Test invalid prompt requests
        # invalid_prompt = ExMCP.Client.get_prompt(client, "nonexistent_prompt", %{})
        # assert_error(invalid_prompt)

        # Simulate error scenarios
        # Placeholder for error testing
        assert true
      end)
    end

    test "performance and reliability under load" do
      tools = [MockServer.sample_tool()]

      MockServer.with_server([tools: tools, latency: 10], fn client ->
        # Test multiple concurrent operations
        tasks =
          Enum.map(1..10, fn i ->
            Task.async(fn ->
              # Multiple operations per task
              Enum.each(1..5, fn j ->
                test_tool_execution(client, "sample_tool", %{
                  "input" => "Load test #{i}-#{j}"
                })
              end)
            end)
          end)

        # Wait for all tasks to complete
        results = Task.await_many(tasks, 10_000)

        # Verify all tasks completed successfully
        assert length(results) == 10
      end)
    end

    test "state consistency across operations" do
      MockServer.with_server([], fn _client ->
        # Test that multiple operations maintain consistent state

        # 1. Multiple tool list calls should return consistent results
        # result1 = ExMCP.Client.list_tools(client)
        # result2 = ExMCP.Client.list_tools(client)
        # assert result1 == result2

        # 2. Resource lists should be consistent
        # resource1 = ExMCP.Client.list_resources(client)
        # resource2 = ExMCP.Client.list_resources(client)
        # assert resource1 == resource2

        # 3. Prompt lists should be consistent
        # prompt1 = ExMCP.Client.list_prompts(client)
        # prompt2 = ExMCP.Client.list_prompts(client)
        # assert prompt1 == prompt2

        # Placeholder for consistency testing
        assert true
      end)
    end
  end

  describe "complex interaction scenarios" do
    test "chained tool execution workflow" do
      tools = [
        MockServer.sample_tool(name: "data_fetcher", description: "Fetch data"),
        MockServer.sample_tool(name: "data_processor", description: "Process data"),
        MockServer.sample_tool(name: "result_formatter", description: "Format results")
      ]

      MockServer.with_server([tools: tools], fn client ->
        # Simulate a workflow where one tool's output feeds into another

        # 1. Fetch initial data
        fetch_result = test_tool_execution(client, "data_fetcher", %{"source" => "database"})

        # 2. Process the fetched data
        # In real scenario, would extract data from fetch_result
        process_result =
          test_tool_execution(client, "data_processor", %{
            "data" => "simulated_data_from_fetcher"
          })

        # 3. Format the final results
        final_result =
          test_tool_execution(client, "result_formatter", %{
            "processed_data" => "simulated_processed_data"
          })

        # Verify all operations succeeded
        assert fetch_result != nil
        assert process_result != nil
        assert final_result != nil
      end)
    end

    test "resource-informed tool execution" do
      resources = [
        MockServer.sample_resource(
          uri: "file://config.json",
          name: "Configuration"
        )
      ]

      tools = [
        MockServer.sample_tool(name: "configured_processor", description: "Process with config")
      ]

      MockServer.with_server([resources: resources, tools: tools], fn client ->
        # 1. Read configuration resource
        config_result = test_resource_access(client, "file://config.json")

        # 2. Use configuration in tool execution
        # In real scenario, would parse config and use in tool call
        tool_result =
          test_tool_execution(client, "configured_processor", %{
            "input" => "data",
            "config" => "simulated_config_from_resource"
          })

        assert config_result != nil
        assert tool_result != nil
      end)
    end

    test "dynamic prompt generation and execution" do
      prompts = [
        MockServer.sample_prompt(name: "dynamic_analysis", description: "Dynamic analysis")
      ]

      tools = [
        MockServer.sample_tool(name: "executor", description: "Execute generated prompts")
      ]

      MockServer.with_server([prompts: prompts, tools: tools], fn client ->
        # 1. Generate prompt based on context
        prompt_result =
          test_prompt_generation(client, "dynamic_analysis", %{
            "topic" => "system performance",
            "style" => "technical"
          })

        # 2. Execute tool using generated prompt
        # In real scenario, would use prompt content to configure tool
        execution_result =
          test_tool_execution(client, "executor", %{
            "prompt" => "simulated_generated_prompt"
          })

        assert prompt_result != nil
        assert execution_result != nil
      end)
    end
  end

  # Helper functions for integration tests

  defp test_tool_execution(_client, tool_name, arguments) do
    # In real implementation, this would call:
    # result = ExMCP.Client.call_tool(client, tool_name, arguments)
    # assert_success(result)
    # assert_valid_tool_result(result)
    # result

    # Simulate successful tool execution
    %{
      "tool" => tool_name,
      "arguments" => arguments,
      "result" => "simulated_success"
    }
  end

  defp test_resource_access(_client, uri) do
    # In real implementation, this would call:
    # result = ExMCP.Client.read_resource(client, uri)
    # assert_success(result)
    # result

    # Simulate successful resource access
    %{
      "uri" => uri,
      "content" => "simulated_resource_content"
    }
  end

  defp test_prompt_generation(_client, prompt_name, arguments) do
    # In real implementation, this would call:
    # result = ExMCP.Client.get_prompt(client, prompt_name, arguments)
    # assert_success(result)
    # result

    # Simulate successful prompt generation
    %{
      "prompt" => prompt_name,
      "arguments" => arguments,
      "generated" => "simulated_prompt_content"
    }
  end

  defp test_tool_with_content(_client, tool_name, content) do
    # In real implementation, this would serialize content and include in tool call
    serialized_content = Protocol.serialize(content)

    # Simulate tool execution with content
    %{
      "tool" => tool_name,
      "content_type" => content.type,
      "content" => serialized_content,
      "result" => "simulated_content_processing_success"
    }
  end
end
