///////////////////////////////////////////////////////////////////////////////
//
// (C) 2025 ICE TEA GROUP LLC - ALL RIGHTS RESERVED
//
// 
//
// ALL INFORMATION CONTAINED HEREIN IS, AND REMAINS
// THE PROPERTY OF ICE TEA GROUP LLC AND ITS SUPPLIERS, IF ANY.
// THE INTELLECTUAL PROPERTY AND TECHNICAL CONCEPTS CONTAINED
// HEREIN ARE PROPRIETARY TO ICE TEA GROUP LLC AND ITS SUPPLIERS
// AND MAY BE COVERED BY U.S. AND FOREIGN PATENTS, PATENT IN PROCESS, AND
// ARE PROTECTED BY TRADE SECRET OR COPYRIGHT LAW.
//
// DISSEMINATION OF THIS INFORMATION OR REPRODUCTION OF THIS MATERIAL
// IS STRICTLY FORBIDDEN UNLESS PRIOR WRITTEN PERMISSION IS OBTAINED
// FROM ICE TEA GROUP LLC.
//
///////////////////////////////////////////////////////////////////////////////

using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Wisej.AI.Helpers;

namespace Wisej.AI.Services
{
	/// <summary>
	/// Represents a service for reranking text inputs using the LocalAI API.
	/// </summary>
	/// <remarks>
	/// This service communicates with the LocalAI API to rerank text inputs based on a specified query.
	/// It allows configuration of the API endpoint, API key, model, API version, and the number of top documents to return.
	/// </remarks>
	[ApiCategory("Services/IRerankingService")]
	public class LocalAIRerankingService : IRerankingService
	{
		/// <summary>
		/// Gets or sets the URL of the service.
		/// </summary>
		[DefaultValue("http://localhost:8080/v1/rerank")]
		public virtual string URL
		{
			get;
			set;
		} = "http://localhost:8080/v1/rerank";

		/// <summary>
		/// Gets or sets the reranking model for the LocalAI service.
		/// </summary>
		[DefaultValue("")]
		public virtual string Model
		{
			get;
			set;
		} = "";

		/// <summary>
		/// Gets or sets the maximum number of documents to return in the reranked response.
		/// If set to 0 it will return all the documents in the input.
		/// </summary>
		[DefaultValue(10)]
		public virtual int TopN
		{
			get;
			set;
		} = 10;

		/// <summary>
		/// Gets the HTTP client service used to communicate with the Pinecone API.
		/// </summary>
		/// <remarks>
		/// The client is initialized with the API key if it is not already initialized.
		/// </remarks>
		internal virtual IHttpClientService Client
		{
			get
			{
				if (_client == null)
				{
					_client = ServiceHelper.GetService<IHttpClientService>();
				}

				return _client;
			}
		}
		private IHttpClientService _client;

		/// <summary>
		/// Asynchronously reranks the provided chunks based on the specified query.
		/// </summary>
		/// <param name="query">The query used to rerank the chunks.</param>
		/// <param name="chunks">The array of text inputs to be reranked.</param>
		/// <returns>A task that represents the asynchronous operation. The task result contains an array of reranked text inputs.</returns>
		public virtual async Task<string[]> RerankAsync(string query, string[] chunks)
		{
			if (String.IsNullOrEmpty(query))
				return chunks;

			if (chunks == null || chunks.Length == 1)
				return chunks;

			var result = await PostAsync(new
			{
				model = this.Model,
				query = query,
				documents = chunks,
				top_n = this.TopN == 0 ? null : (object)this.TopN
			});

			var data = result.data as dynamic[];
			if (data != null && data.Length > 0)
			{
				chunks = data.Select(d => chunks[(int)d.index]).ToArray();
			}

			return chunks;
		}

		#region Implementation

		private async Task<dynamic> PostAsync(object data)
		{
			var url = this.URL;
			var client = this.Client;

			var payload = JSON.Stringify(data, JSON.SerializerOptions.None);
			var content = new StringContent(payload ?? "", Encoding.UTF8, "application/json");

			var response = await client.PostAsync(url, content);

			if (!response.IsSuccessStatusCode)
			{
				var reader = new StreamReader(await response.Content.ReadAsStreamAsync());
				throw new Exception(reader.ReadToEnd());
			}

			response.EnsureSuccessStatusCode();
			var stream = await response.Content.ReadAsStreamAsync();
			return JSON.Parse(stream);
		}
		#endregion
	}
}
