package com.openosrs.client.core;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Supporting classes for the AgentTestingFramework.
 */

// ================================
// TEST CASE DEFINITIONS
// ================================

/**
 * Represents a single test case for agent behavior validation.
 */
class AgentTestCase {
    private final String name;
    private final ScenarioType scenario;
    private final Duration timeout;
    private final Function<TestContext, TestExecutionResult> testExecution;
    private final Consumer<TestContext> preTestSetup;
    private final Consumer<TestContext> postTestCleanup;
    private final Map<String, Object> environmentConfig;
    
    private AgentTestCase(Builder builder) {
        this.name = builder.name;
        this.scenario = builder.scenario;
        this.timeout = builder.timeout;
        this.testExecution = builder.testExecution;
        this.preTestSetup = builder.preTestSetup;
        this.postTestCleanup = builder.postTestCleanup;
        this.environmentConfig = builder.environmentConfig;
    }
    
    public String getName() { return name; }
    public ScenarioType getScenario() { return scenario; }
    public Duration getTimeout() { return timeout; }
    public Function<TestContext, TestExecutionResult> getTestExecution() { return testExecution; }
    public Consumer<TestContext> getPreTestSetup() { return preTestSetup; }
    public Consumer<TestContext> getPostTestCleanup() { return postTestCleanup; }
    public Map<String, Object> getEnvironmentConfig() { return environmentConfig; }
    
    public static class Builder {
        private final String name;
        private ScenarioType scenario;
        private Duration timeout = Duration.ofMinutes(5);
        private Function<TestContext, TestExecutionResult> testExecution;
        private Consumer<TestContext> preTestSetup;
        private Consumer<TestContext> postTestCleanup;
        private Map<String, Object> environmentConfig = new HashMap<>();
        
        public Builder(String name) {
            this.name = name;
        }
        
        public Builder withScenario(ScenarioType scenario) {
            this.scenario = scenario;
            return this;
        }
        
        public Builder withTimeout(Duration timeout) {
            this.timeout = timeout;
            return this;
        }
        
        public Builder withTestExecution(Function<TestContext, TestExecutionResult> testExecution) {
            this.testExecution = testExecution;
            return this;
        }
        
        public Builder withPreTestSetup(Consumer<TestContext> preTestSetup) {
            this.preTestSetup = preTestSetup;
            return this;
        }
        
        public Builder withPostTestCleanup(Consumer<TestContext> postTestCleanup) {
            this.postTestCleanup = postTestCleanup;
            return this;
        }
        
        public Builder withEnvironmentConfig(Map<String, Object> config) {
            this.environmentConfig.putAll(config);
            return this;
        }
        
        public AgentTestCase build() {
            if (testExecution == null) {
                throw new IllegalStateException("Test execution function is required");
            }
            return new AgentTestCase(this);
        }
    }
}

/**
 * Test execution context providing access to test environment.
 */
class TestContext {
    private final String testId;
    private final String testName;
    private final LocalDateTime startTime;
    private final Map<String, Object> testData;
    
    public TestContext(String testId, String testName) {
        this.testId = testId;
        this.testName = testName;
        this.startTime = LocalDateTime.now();
        this.testData = new HashMap<>();
    }
    
    public String getTestId() { return testId; }
    public String getTestName() { return testName; }
    public LocalDateTime getStartTime() { return startTime; }
    
    public void setData(String key, Object value) {
        testData.put(key, value);
    }
    
    public Object getData(String key) {
        return testData.get(key);
    }
    
    public Map<String, Object> getAllData() {
        return new HashMap<>(testData);
    }
}

/**
 * Result of test execution.
 */
class TestExecutionResult {
    private final boolean success;
    private final String message;
    private final String failureReason;
    private final Map<String, Object> details;
    
    private TestExecutionResult(boolean success, String message, String failureReason, Map<String, Object> details) {
        this.success = success;
        this.message = message;
        this.failureReason = failureReason;
        this.details = details != null ? details : new HashMap<>();
    }
    
    public static TestExecutionResult success(String message) {
        return new TestExecutionResult(true, message, null, null);
    }
    
    public static TestExecutionResult success(String message, Map<String, Object> details) {
        return new TestExecutionResult(true, message, null, details);
    }
    
    public static TestExecutionResult failure(String failureReason) {
        return new TestExecutionResult(false, null, failureReason, null);
    }
    
    public static TestExecutionResult failure(String failureReason, Map<String, Object> details) {
        return new TestExecutionResult(false, null, failureReason, details);
    }
    
    public boolean isSuccess() { return success; }
    public String getMessage() { return message; }
    public String getFailureReason() { return failureReason; }
    public Map<String, Object> getDetails() { return new HashMap<>(details); }
}

/**
 * Test result with status and timing information.
 */
class TestResult {
    private final String testId;
    private final String testName;
    private final TestStatus status;
    private final String message;
    private final long durationMs;
    private final LocalDateTime timestamp;
    private final Map<String, Object> details;
    
    private TestResult(String testId, String testName, TestStatus status, String message, long durationMs, Map<String, Object> details) {
        this.testId = testId;
        this.testName = testName;
        this.status = status;
        this.message = message;
        this.durationMs = durationMs;
        this.timestamp = LocalDateTime.now();
        this.details = details != null ? details : new HashMap<>();
    }
    
    public static TestResult success(String testId, String testName, long durationMs) {
        return new TestResult(testId, testName, TestStatus.PASSED, "Test passed", durationMs, null);
    }
    
    public static TestResult failure(String testId, String testName, String message) {
        return new TestResult(testId, testName, TestStatus.FAILED, message, 0, null);
    }
    
    public static TestResult timeout(String testId, String testName) {
        return new TestResult(testId, testName, TestStatus.TIMEOUT, "Test timed out", 0, null);
    }
    
    public TestResult withDuration(long durationMs) {
        return new TestResult(testId, testName, status, message, durationMs, details);
    }
    
    public TestResult withDetails(Map<String, Object> details) {
        return new TestResult(testId, testName, status, message, durationMs, details);
    }
    
    public String getTestId() { return testId; }
    public String getTestName() { return testName; }
    public TestStatus getStatus() { return status; }
    public String getMessage() { return message; }
    public long getDurationMs() { return durationMs; }
    public LocalDateTime getTimestamp() { return timestamp; }
    public Map<String, Object> getDetails() { return new HashMap<>(details); }
}

/**
 * Test suite containing multiple test cases.
 */
class TestSuite {
    private final String name;
    private final Map<String, AgentTestCase> testCases;
    
    public TestSuite(String name) {
        this.name = name;
        this.testCases = new HashMap<>();
    }
    
    public void addTest(String testName, AgentTestCase testCase) {
        testCases.put(testName, testCase);
    }
    
    public String getName() { return name; }
    public Map<String, AgentTestCase> getTestCases() { return new HashMap<>(testCases); }
}

/**
 * Result of running an entire test suite.
 */
class TestSuiteResult {
    private final String suiteName;
    private final List<TestResult> results;
    private final LocalDateTime timestamp;
    
    public TestSuiteResult(String suiteName, List<TestResult> results) {
        this.suiteName = suiteName;
        this.results = new ArrayList<>(results);
        this.timestamp = LocalDateTime.now();
    }
    
    public String getSuiteName() { return suiteName; }
    public List<TestResult> getResults() { return new ArrayList<>(results); }
    public LocalDateTime getTimestamp() { return timestamp; }
    
    public long getPassedCount() {
        return results.stream().filter(r -> r.getStatus() == TestStatus.PASSED).count();
    }
    
    public long getFailedCount() {
        return results.stream().filter(r -> r.getStatus() == TestStatus.FAILED).count();
    }
    
    public long getTimeoutCount() {
        return results.stream().filter(r -> r.getStatus() == TestStatus.TIMEOUT).count();
    }
}

// ================================
// ENUMS
// ================================

enum TestStatus {
    PASSED, FAILED, TIMEOUT, SKIPPED
}

enum ScenarioType {
    BASIC_GAMEPLAY,
    PERFORMANCE_STRESS,
    SCRIPTING_AUTOMATION,
    INTERFACE_INTERACTION,
    NETWORK_CONDITIONS,
    MEMORY_PRESSURE,
    CONCURRENT_AGENTS,
    ERROR_RECOVERY
}