using System;
using System.Collections.Generic;
using System.Reflection;

using AIRuntime.Runtime.Providers;
using AIRuntime.Runtime.Providers.OpenAI;

using NUnit.Framework;


namespace AIRuntime.Tests
{
    public sealed class OpenAIClientTests
    {
        [Test]
        public void ConstructorUsesSafeDefaults()
        {
            OpenAIClient client =
                new OpenAIClient(
                    "test-api-key");


            Assert.AreEqual(
                OpenAIClient.DefaultBaseUrl,
                client.BaseUrl);

            Assert.AreEqual(
                OpenAIClient.DefaultModel,
                client.Model);

            Assert.AreEqual(
                60,
                client.TimeoutSeconds);

            Assert.IsNull(
                client.OrganizationId);

            Assert.IsNull(
                client.ProjectId);
        }


        [Test]
        public void ConstructorNormalizesConfiguration()
        {
            OpenAIClient client =
                new OpenAIClient(
                    " test-api-key ",
                    " custom-model ",
                    "https://example.test/v1///",
                    " organization ",
                    " project ",
                    30);


            Assert.AreEqual(
                "https://example.test/v1",
                client.BaseUrl);

            Assert.AreEqual(
                "custom-model",
                client.Model);

            Assert.AreEqual(
                "organization",
                client.OrganizationId);

            Assert.AreEqual(
                "project",
                client.ProjectId);

            Assert.AreEqual(
                30,
                client.TimeoutSeconds);
        }


        [TestCase(null)]
        [TestCase("")]
        [TestCase("   ")]
        public void ConstructorRejectsMissingApiKey(
            string apiKey)
        {
            Assert.Throws<ArgumentException>(
                () => new OpenAIClient(
                    apiKey));
        }


        [TestCase(null)]
        [TestCase("")]
        [TestCase("relative/path")]
        [TestCase("ftp://example.test/v1")]
        public void ConstructorRejectsInvalidBaseUrl(
            string baseUrl)
        {
            Assert.Throws<ArgumentException>(
                () => new OpenAIClient(
                    "test-api-key",
                    baseUrl: baseUrl));
        }


        [Test]
        public void ConstructorRejectsInvalidTimeout()
        {
            Assert.Throws<ArgumentOutOfRangeException>(
                () => new OpenAIClient(
                    "test-api-key",
                    timeoutSeconds: 0));
        }


        [Test]
        public void ConstructorSupportsCompatibleProviderConfiguration()
        {
            OpenAIClient client =
                new OpenAIClient(
                    "test-api-key",
                    model: "test-model",
                    baseUrl: "https://example.test/v1",
                    providerName: "Compatible API",
                    defaultHeaders:
                        new Dictionary<string, string>
                        {
                            {
                                "X-Application-Title",
                                "AIRuntime"
                            }
                        });


            Assert.AreEqual(
                "Compatible API",
                client.ProviderName);

            Assert.AreEqual(
                "AIRuntime",
                client.DefaultHeaders[
                    "X-Application-Title"]);
        }


        [Test]
        public void ConstructorDefaultsToOpenAIStreamTerminationPolicy()
        {
            OpenAIClient client =
                new OpenAIClient(
                    "test-api-key");


            Assert.AreSame(
                ProviderStreamTerminationPolicy.OpenAICompatible,
                client.StreamTerminationPolicy);
        }


        [Test]
        public void ConstructorAcceptsProviderDeclaredStreamTerminationPolicy()
        {
            OpenAIClient client =
                new OpenAIClient(
                    "test-api-key",
                    streamTerminationPolicy:
                        ProviderStreamTerminationPolicy.NousPortal);


            Assert.AreSame(
                ProviderStreamTerminationPolicy.NousPortal,
                client.StreamTerminationPolicy);
        }


        [Test]
        public void StreamingTransportUsesConversationInactivityTimeout()
        {
            MethodInfo resolver =
                typeof(OpenAIClient).GetMethod(
                    "ResolveTransportTimeoutSeconds",
                    BindingFlags.Static |
                    BindingFlags.NonPublic);

            Assert.IsNotNull(
                resolver);

            Assert.AreEqual(
                0,
                resolver.Invoke(
                    null,
                    new object[] { true, 60 }));

            Assert.AreEqual(
                60,
                resolver.Invoke(
                    null,
                    new object[] { false, 60 }));
        }


        [Test]
        public void RetryAfterParserSupportsDeltaSeconds()
        {
            MethodInfo parser =
                typeof(OpenAIClient).GetMethod(
                    "ParseRetryAfter",
                    BindingFlags.Static |
                    BindingFlags.NonPublic);

            Assert.IsNotNull(parser);


            TimeSpan? delay =
                (TimeSpan?)parser.Invoke(
                    null,
                    new object[]
                    {
                        "12.5",
                        DateTimeOffset.Parse("2026-08-27T08:00:00Z")
                    });


            Assert.AreEqual(
                TimeSpan.FromSeconds(12.5),
                delay);
        }


        [Test]
        public void RetryAfterParserSupportsHttpDate()
        {
            MethodInfo parser =
                typeof(OpenAIClient).GetMethod(
                    "ParseRetryAfter",
                    BindingFlags.Static |
                    BindingFlags.NonPublic);

            Assert.IsNotNull(parser);


            TimeSpan? delay =
                (TimeSpan?)parser.Invoke(
                    null,
                    new object[]
                    {
                        "Thu, 27 Aug 2026 08:01:30 GMT",
                        DateTimeOffset.Parse("2026-08-27T08:00:00Z")
                    });


            Assert.AreEqual(
                TimeSpan.FromSeconds(90),
                delay);
        }


        [TestCase("Authorization")]
        [TestCase("Content-Type")]
        public void ConstructorRejectsManagedDefaultHeaders(
            string headerName)
        {
            Assert.Throws<ArgumentException>(
                () => new OpenAIClient(
                    "test-api-key",
                    defaultHeaders:
                        new Dictionary<string, string>
                        {
                            {
                                headerName,
                                "override"
                            }
                        }));
        }
    }
}
