require "rails_helper"

# This spec tests the EvaluationRunner with actual OpenAI API calls
# It's kept separate so it can be excluded from regular test runs
#
# To run these tests:
# 1. Ensure you have OpenAI API credentials configured
# 2. Run: bundle exec rspec spec/services/prompt_engine/evaluation_runner_live_spec.rb
#
# Note: These tests will consume API credits and may be subject to rate limits
# The OpenAI Evals API may not be available on all accounts

RSpec.describe PromptEngine::EvaluationRunner, :live_api do
  # Skip these tests unless explicitly running live API tests
  before(:each) do
    skip "Live API tests skipped. Set LIVE_API_TESTS=true to run" unless ENV["LIVE_API_TESTS"] == "true"
    skip "OpenAI API key not configured" unless Rails.application.credentials.dig(:openai, :api_key).present?
  end

  let(:prompt) do
    create(:prompt,
      name: "Math Problem Solver",
      content: "What is {{num1}} + {{num2}}? Respond with only the number.",
      system_message: "You are a math tutor. Give only the numerical answer with no explanation.",
      model: "gpt-3.5-turbo",
      temperature: 0.0,
      max_tokens: 10,
      status: "active")
  end

  let(:eval_set) do
    create(:eval_set,
      prompt: prompt,
      name: "Basic Addition Tests")
  end

  let!(:test_cases) do
    [
      create(:test_case,
        eval_set: eval_set,
        input_variables: { "num1" => "2", "num2" => "3" },
        expected_output: "5",
        description: "Simple addition"),
      create(:test_case,
        eval_set: eval_set,
        input_variables: { "num1" => "10", "num2" => "15" },
        expected_output: "25",
        description: "Double digit addition"),
      create(:test_case,
        eval_set: eval_set,
        input_variables: { "num1" => "100", "num2" => "200" },
        expected_output: "300",
        description: "Triple digit addition")
    ]
  end

  describe "#execute with live API" do
    it "successfully runs evaluation against OpenAI" do
      eval_run = eval_set.eval_runs.create!(
        prompt_version: prompt.current_version
      )

      runner = described_class.new(eval_run)

      # This will make actual API calls
      expect { runner.execute }.not_to raise_error

      eval_run.reload
      expect(eval_run.status).to eq("completed").or eq("running")

      # If completed immediately (unlikely with OpenAI Evals API)
      if eval_run.status == "completed"
        expect(eval_run.total_count).to eq(3)
        expect(eval_run.passed_count).to be >= 0
        expect(eval_run.failed_count).to be >= 0
        expect(eval_run.openai_run_id).to be_present
      end
    end

    it "handles authentication errors when API key is invalid" do
      # Temporarily use invalid API key
      allow(Rails.application.credentials).to receive(:dig).with(:openai, :api_key).and_return("invalid-key")

      eval_run = eval_set.eval_runs.create!(
        prompt_version: prompt.current_version
      )

      runner = described_class.new(eval_run)

      expect { runner.execute }.to raise_error(PromptEngine::OpenAiEvalsClient::AuthenticationError)

      eval_run.reload
      expect(eval_run.status).to eq("failed")
      expect(eval_run.error_message).to include("Invalid API key")
    end
  end

  describe "OpenAI Evals API availability" do
    it "checks if Evals API is available for the account" do
      client = PromptEngine::OpenAiEvalsClient.new

      # Try to create a simple eval to test API availability
      begin
        response = client.create_eval(
          name: "Test Eval #{SecureRandom.hex(4)}",
          data_source_config: {
            type: "custom",
            item_schema: {
              type: "object",
              properties: {
                input: { type: "string" },
                output: { type: "string" }
              }
            }
          },
          testing_criteria: [
            {
              type: "string_check",
              name: "Exact match",
              operation: "eq"
            }
          ]
        )

        expect(response).to have_key("id")
        puts "✓ OpenAI Evals API is available on this account"
      rescue PromptEngine::OpenAiEvalsClient::NotFoundError => e
        skip "OpenAI Evals API not available on this account: #{e.message}"
      rescue PromptEngine::OpenAiEvalsClient::APIError => e
        skip "OpenAI Evals API error: #{e.message}"
      end
    end
  end

  describe "alternative evaluation approach (without Evals API)" do
    it "can evaluate using regular chat completions as fallback" do
      # This demonstrates a fallback approach using regular completions
      # instead of the Evals API, which may not be available

      eval_run = eval_set.eval_runs.create!(
        prompt_version: prompt.current_version
      )

      # Mock a simple evaluation using chat completions
      results = test_cases.map do |test_case|
        # Render the prompt with variables
        rendered = prompt.render(variables: test_case.input_variables)

        # In a real implementation, you would call OpenAI chat here
        # For testing, we'll simulate the response
        actual_output = case test_case.input_variables
        when { "num1" => "2", "num2" => "3" }
          "5"
        when { "num1" => "10", "num2" => "15" }
          "25"
        when { "num1" => "100", "num2" => "200" }
          "300"
        end

        {
          test_case: test_case,
          actual_output: actual_output,
          passed: actual_output.strip == test_case.expected_output.strip
        }
      end

      # Update eval run with results
      passed_count = results.count { |r| r[:passed] }
      eval_run.update!(
        status: "completed",
        started_at: 1.minute.ago,
        completed_at: Time.current,
        total_count: results.count,
        passed_count: passed_count,
        failed_count: results.count - passed_count
      )

      expect(eval_run.passed_count).to eq(3)
      expect(eval_run.failed_count).to eq(0)
    end
  end
end
