/**
 * WC Licensed Product API Client for C#
 *
 * A C# client for interacting with the WC Licensed Product REST API.
 *
 * Requirements:
 *   - .NET 6.0+ or .NET Framework 4.7.2+
 *   - System.Net.Http
 *   - System.Text.Json (or Newtonsoft.Json)
 *
 * Usage:
 *   var client = new WcLicensedProductClient("https://your-site.com");
 *   var result = await client.ValidateAsync("XXXX-XXXX-XXXX-XXXX", "example.com");
 */

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace WcLicensedProduct
{
    /// <summary>
    /// License status response data
    /// </summary>
    public class LicenseStatus
    {
        [JsonPropertyName("valid")]
        public bool Valid { get; set; }

        [JsonPropertyName("status")]
        public string Status { get; set; }

        [JsonPropertyName("domain")]
        public string Domain { get; set; }

        [JsonPropertyName("expires_at")]
        public string ExpiresAt { get; set; }

        [JsonPropertyName("activations_count")]
        public int ActivationsCount { get; set; }

        [JsonPropertyName("max_activations")]
        public int MaxActivations { get; set; }
    }

    /// <summary>
    /// Validation response data
    /// </summary>
    public class ValidationResult
    {
        [JsonPropertyName("valid")]
        public bool Valid { get; set; }

        [JsonPropertyName("error")]
        public string Error { get; set; }

        [JsonPropertyName("message")]
        public string Message { get; set; }

        [JsonPropertyName("license")]
        public LicenseInfo License { get; set; }
    }

    /// <summary>
    /// License information in validation response
    /// </summary>
    public class LicenseInfo
    {
        [JsonPropertyName("status")]
        public string Status { get; set; }

        [JsonPropertyName("domain")]
        public string Domain { get; set; }

        [JsonPropertyName("expires_at")]
        public string ExpiresAt { get; set; }

        [JsonPropertyName("product_id")]
        public int ProductId { get; set; }
    }

    /// <summary>
    /// Activation response data
    /// </summary>
    public class ActivationResult
    {
        [JsonPropertyName("success")]
        public bool Success { get; set; }

        [JsonPropertyName("error")]
        public string Error { get; set; }

        [JsonPropertyName("message")]
        public string Message { get; set; }
    }

    /// <summary>
    /// Exception for API errors
    /// </summary>
    public class WcLicensedProductException : Exception
    {
        public string ErrorCode { get; }
        public int HttpCode { get; }

        public WcLicensedProductException(string message, string errorCode = null, int httpCode = 0)
            : base(message)
        {
            ErrorCode = errorCode;
            HttpCode = httpCode;
        }
    }

    /// <summary>
    /// Exception for rate limit errors
    /// </summary>
    public class RateLimitException : WcLicensedProductException
    {
        public int RetryAfter { get; }

        public RateLimitException(int retryAfter)
            : base($"Rate limit exceeded. Retry after {retryAfter} seconds.")
        {
            RetryAfter = retryAfter;
        }
    }

    /// <summary>
    /// Client for the WC Licensed Product REST API
    /// </summary>
    public class WcLicensedProductClient : IDisposable
    {
        private readonly HttpClient _httpClient;
        private readonly string _baseUrl;
        private readonly JsonSerializerOptions _jsonOptions;

        /// <summary>
        /// Initialize the client
        /// </summary>
        /// <param name="siteUrl">Your WordPress site URL (e.g., "https://example.com")</param>
        /// <param name="timeout">Request timeout (default: 30 seconds)</param>
        public WcLicensedProductClient(string siteUrl, TimeSpan? timeout = null)
        {
            _baseUrl = siteUrl.TrimEnd('/') + "/wp-json/wc-licensed-product/v1";

            _httpClient = new HttpClient
            {
                Timeout = timeout ?? TimeSpan.FromSeconds(30)
            };
            _httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            _jsonOptions = new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            };
        }

        /// <summary>
        /// Validate a license key for a specific domain
        /// </summary>
        /// <param name="licenseKey">The license key to validate</param>
        /// <param name="domain">The domain to validate against</param>
        /// <returns>Validation result</returns>
        public async Task<ValidationResult> ValidateAsync(string licenseKey, string domain)
        {
            var data = new { license_key = licenseKey, domain = domain };
            return await RequestAsync<ValidationResult>("/validate", data);
        }

        /// <summary>
        /// Get the status of a license
        /// </summary>
        /// <param name="licenseKey">The license key to check</param>
        /// <returns>License status</returns>
        public async Task<LicenseStatus> StatusAsync(string licenseKey)
        {
            var data = new { license_key = licenseKey };
            return await RequestAsync<LicenseStatus>("/status", data);
        }

        /// <summary>
        /// Activate a license on a domain
        /// </summary>
        /// <param name="licenseKey">The license key to activate</param>
        /// <param name="domain">The domain to activate on</param>
        /// <returns>Activation result</returns>
        public async Task<ActivationResult> ActivateAsync(string licenseKey, string domain)
        {
            var data = new { license_key = licenseKey, domain = domain };
            return await RequestAsync<ActivationResult>("/activate", data);
        }

        /// <summary>
        /// Make an API request
        /// </summary>
        private async Task<T> RequestAsync<T>(string endpoint, object data)
        {
            var url = _baseUrl + endpoint;
            var json = JsonSerializer.Serialize(data);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            HttpResponseMessage response;
            try
            {
                response = await _httpClient.PostAsync(url, content);
            }
            catch (TaskCanceledException)
            {
                throw new WcLicensedProductException("Request timeout");
            }
            catch (HttpRequestException ex)
            {
                throw new WcLicensedProductException($"Request failed: {ex.Message}");
            }

            // Handle rate limiting
            if ((int)response.StatusCode == 429)
            {
                var retryAfter = 60;
                if (response.Headers.TryGetValues("Retry-After", out var values))
                {
                    int.TryParse(string.Join("", values), out retryAfter);
                }
                throw new RateLimitException(retryAfter);
            }

            var responseBody = await response.Content.ReadAsStringAsync();
            T result;

            try
            {
                result = JsonSerializer.Deserialize<T>(responseBody, _jsonOptions);
            }
            catch (JsonException)
            {
                throw new WcLicensedProductException("Invalid JSON response");
            }

            // Check for API errors (for error responses)
            if (!response.IsSuccessStatusCode)
            {
                var errorResult = JsonSerializer.Deserialize<ValidationResult>(responseBody, _jsonOptions);
                if (errorResult?.Error != null)
                {
                    throw new WcLicensedProductException(
                        errorResult.Message ?? "Unknown error",
                        errorResult.Error,
                        (int)response.StatusCode
                    );
                }
            }

            return result;
        }

        public void Dispose()
        {
            _httpClient?.Dispose();
        }
    }

    // =========================================================================
    // Usage Examples
    // =========================================================================

    public class Program
    {
        public static async Task Main(string[] args)
        {
            using var client = new WcLicensedProductClient("https://your-wordpress-site.com");

            // Example 1: Validate a license
            Console.WriteLine("=== Validate License ===");
            try
            {
                var result = await client.ValidateAsync("XXXX-XXXX-XXXX-XXXX", "myapp.example.com");

                if (result.Valid)
                {
                    Console.WriteLine("License is valid!");
                    Console.WriteLine($"Status: {result.License?.Status ?? "active"}");
                    Console.WriteLine($"Expires: {result.License?.ExpiresAt ?? "Never"}");
                }
                else
                {
                    Console.WriteLine($"License is not valid: {result.Message ?? "Unknown error"}");
                }
            }
            catch (RateLimitException ex)
            {
                Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
            }
            catch (WcLicensedProductException ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }

            Console.WriteLine();

            // Example 2: Check license status
            Console.WriteLine("=== Check License Status ===");
            try
            {
                var status = await client.StatusAsync("XXXX-XXXX-XXXX-XXXX");

                Console.WriteLine($"Valid: {(status.Valid ? "Yes" : "No")}");
                Console.WriteLine($"Status: {status.Status ?? "unknown"}");
                Console.WriteLine($"Domain: {status.Domain ?? "None"}");
                Console.WriteLine($"Expires: {status.ExpiresAt ?? "Never"}");
                Console.WriteLine($"Activations: {status.ActivationsCount}/{status.MaxActivations}");
            }
            catch (RateLimitException ex)
            {
                Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
            }
            catch (WcLicensedProductException ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }

            Console.WriteLine();

            // Example 3: Activate license on a domain
            Console.WriteLine("=== Activate License ===");
            try
            {
                var result = await client.ActivateAsync("XXXX-XXXX-XXXX-XXXX", "newsite.example.com");

                if (result.Success)
                {
                    Console.WriteLine("License activated successfully!");
                }
                else
                {
                    Console.WriteLine($"Activation failed: {result.Message ?? "Unknown error"}");
                }
            }
            catch (RateLimitException ex)
            {
                Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
            }
            catch (WcLicensedProductException ex)
            {
                Console.WriteLine($"Error ({ex.ErrorCode}): {ex.Message}");
            }
        }
    }
}
