using System.Text;
using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.Sqlite;

string GetCurrentWeather() => Random.Shared.NextDouble() > 0.5 ? "It's sunny." : "It's raining.";
string GetCurrentDateTime() => DateTimeOffset.UtcNow.ToString();

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddEmbeddingGenerator(
    new OllamaEmbeddingGenerator(new Uri("http://192.168.66.34:11434"), "all-minilm"));
builder.Services.AddChatClient(
    new AzureOpenAIClient(Secret.Endpoint, Secret.Key)
        .GetChatClient("gpt-4o-mini")
        .AsIChatClient())
    .UseFunctionInvocation();
var host = builder.Build();

#region Load Data
List<Dog> dogs =
[
    new()
    {
        Key = 1,
        Name = "Dorka",
        Behavior = "Dorka is perfect example of Dachshund breed. She's smart, curious and brave. Smart, but sometimes stubborn. Likes people and is a great dog for kids and as a therapy dog. Dorka enjoys digging and burrowing. She can be quite vocal when she's excited or wants attention."
    },
    new()
    {
        Key = 2,
        Name = "Max",
        Behavior = "Max is energetic and playful. He loves to run around and play fetch. He's very friendly and gets along well with other dogs and people. Max is very social and enjoys going to dog parks. He's curious and loves exploring new places."
    },
    new()
    {
        Key = 3,
        Name = "Bella",
        Behavior = "Bella is patient and responds well to gentle training methods. She's very obedient and eager to please. Bella loves to lounge around the house and enjoys a cozy spot to nap. She's not as high-energy as Max but still enjoys her daily walks."
    },
];
var vectorStore = new SqliteVectorStore("Data Source=demo.db");
var dogsCollection = vectorStore.GetCollection<string, Dog>("dog");
await dogsCollection.CreateCollectionIfNotExistsAsync();
foreach (var dog in dogs)
{
    dog.Embedding = await GetEmbeddingGenerator().GenerateVectorAsync(dog.Behavior);
    await dogsCollection.UpsertAsync(dog);
}
#endregion

var chatOptions = new ChatOptions()
{
    Tools = [
        AIFunctionFactory.Create(GetCurrentWeather, nameof(GetCurrentWeather)),
        AIFunctionFactory.Create(GetCurrentDateTime, nameof(GetCurrentDateTime))],
};
List<ChatMessage> history = [];
var chatClient = host.Services.GetRequiredService<IChatClient>();
while (true)
{
    Console.Write("?: ");
    var question = Console.ReadLine()!;

    var sb = new StringBuilder();
    sb.AppendLine($"""
        User's question: "{question}"
        """);
    sb.AppendLine();
    sb.AppendLine($"""
        Here's extra information found in local sources. 
        This information might or might not be relevant to user's question. 
        Score is included. Feel free to ignore, if the score seems too low.
        """);
    // too simple :)
    var questionVector = await GetEmbeddingGenerator().GenerateVectorAsync(question);
    await foreach (var item in dogsCollection.SearchEmbeddingAsync(questionVector, 2))
    {
        sb.AppendLine($"""
            <dog_info>
            - Score: {item.Score}
            - Dog's name: "{item.Record.Name}"
            - Dog's behavior: "{item.Record.Behavior}"
            - Dog's key: "{item.Record.Key}"
            </dog_info>
            """);
    }
    history.Add(new(ChatRole.User, sb.ToString()));

    var response = await chatClient.GetResponseAsync<DogResponse>(history, chatOptions);
    Console.WriteLine(response.Result.Response);
    Console.WriteLine($"Key: {response.Result.DogKey}");

    history.AddMessages(response);
}

IEmbeddingGenerator<string, Embedding<float>> GetEmbeddingGenerator()
    => host.Services.GetRequiredService<IEmbeddingGenerator<string, Embedding<float>>>();

public class DogResponse
{
    public string Response { get; set; } = null!;
    public ulong DogKey { get; set; }
}

public class Dog
{
    [VectorStoreRecordKey]
    public ulong Key { get; set; }

    [VectorStoreRecordData]
    public string Name { get; set; } = null!;

    [VectorStoreRecordData]
    public string Behavior { get; set; } = null!;

    [VectorStoreRecordVector(384, DistanceFunction = DistanceFunction.CosineDistance)]
    public ReadOnlyMemory<float> Embedding { get; set; }
}

