using BLAIzor.Data; using BLAIzor.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Identity.Client; namespace BLAIzor.Services { public class ContentEditorService { //private readonly AIService _aiService; private readonly OpenAIApiService _openAIApiService; //private readonly ApplicationDbContext _context; private readonly QDrantService _qDrantService; private readonly HtmlSnippetProcessor _htmlSnippetProcessor; private readonly IServiceScopeFactory _serviceScopeFactory; public ContentEditorService(/*AIService aiService,*/ OpenAIApiService openAIApiService, /*ApplicationDbContext context,*/ QDrantService qDrantService, HtmlSnippetProcessor htmlSnippetProcessor, IServiceScopeFactory serviceScopeFactory) { //_aiService = aiService; _openAIApiService = openAIApiService; //_context = context; _qDrantService = qDrantService; _htmlSnippetProcessor = htmlSnippetProcessor; _serviceScopeFactory = serviceScopeFactory; } // Existing methods public async Task> GetMenuSuggestionsAsync(string sessionId, string prompt) { string systemMessage = "You are a helpful assistant that helps the user in creating a website."; var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt); return result.Split('\n', StringSplitOptions.RemoveEmptyEntries) .Select(line => line.Trim('-').Trim()) .ToList() ?? new List(); } public async Task GetGeneratedContentAsync(string sessionId, string prompt) { string systemMessage = "You are a helpful assistant that helps the user in creating a website. Do not generate html, just plain text."; var result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, prompt); return result; } // CRUD methods for MenuItems // Create a new MenuItem public async Task AddMenuItemAsync(MenuItem menuItem) { using (var scope = _serviceScopeFactory.CreateScope()) { var _context = scope.ServiceProvider.GetRequiredService(); // Now use dbContext safely without violating DI rules var result = await _context.MenuItems.AddAsync(menuItem); await _context.SaveChangesAsync(); return result.Entity; } } /// /// Adds items as list /// /// /// the number of modified rows public async Task AddMenuItemsAsync(List menuItems) { using (var scope = _serviceScopeFactory.CreateScope()) { var _context = scope.ServiceProvider.GetRequiredService(); // Now use dbContext safely without violating DI rules _context.MenuItems.AddRange(menuItems); var result = await _context.SaveChangesAsync(); return result; } } // Get all MenuItems for a specific SiteInfo public async Task> GetMenuItemsBySiteIdAsync(int siteInfoId) { using (var scope = _serviceScopeFactory.CreateScope()) { var _context = scope.ServiceProvider.GetRequiredService(); // Now use dbContext safely without violating DI rules var result = await _context.MenuItems .Where(m => m.SiteInfoId == siteInfoId) .ToListAsync(); return result; } } // Update an existing MenuItem public async Task UpdateMenuItemAsync(MenuItem menuItem) { using (var scope = _serviceScopeFactory.CreateScope()) { var _context = scope.ServiceProvider.GetRequiredService(); // Now use dbContext safely without violating DI rules var existingMenuItem = await _context.MenuItems.FindAsync(menuItem.Id); if (existingMenuItem == null) { throw new Exception("MenuItem not found."); } existingMenuItem.Name = menuItem.Name; existingMenuItem.QdrantPointId = menuItem.QdrantPointId; existingMenuItem.SortOrder = menuItem.SortOrder; existingMenuItem.ShowInMainMenu = menuItem.ShowInMainMenu; _context.MenuItems.Update(existingMenuItem); await _context.SaveChangesAsync(); return existingMenuItem; } } // Delete a MenuItem public async Task DeleteMenuItemAsync(int menuItemId) { using (var scope = _serviceScopeFactory.CreateScope()) { var _context = scope.ServiceProvider.GetRequiredService(); // Now use dbContext safely without violating DI rules var menuItem = await _context.MenuItems.FindAsync(menuItemId); if (menuItem == null) { throw new Exception("MenuItem not found."); } _context.MenuItems.Remove(menuItem); await _context.SaveChangesAsync(); } } public async Task ProcessMenuItems(int SiteId, bool hasCollection, List ExtractedMenuItems, string subject, bool menuItemsSaved, bool updateVectorDatabase) { try { //check if Collection is created already if (!hasCollection) { //create colection await _qDrantService.CreateQdrantCollectionAsync("Site" + SiteId.ToString()); hasCollection = true; } var updateQdrantList = ExtractedMenuItems.OrderBy(mi => mi.MenuItem.PointId).ToList(); for (int i = 0; i < updateQdrantList.Count(); i++) { MenuItem menuItem = new MenuItem(); menuItem.Name = ExtractedMenuItems[i].MenuItem.Name; menuItem.SortOrder = i; menuItem.SiteInfoId = SiteId; menuItem.ShowInMainMenu = true; if (!string.IsNullOrEmpty(ExtractedMenuItems[i].Content)) //if we have content in the menuItem, let's update the vector DB { if(updateVectorDatabase) { Guid guid = Guid.NewGuid(); WebPageContent content = new WebPageContent(); content.Id = i; content.UId = guid.ToString(); content.Type = "page"; content.SiteId = SiteId; content.Name = ExtractedMenuItems[i].MenuItem.Name; content.Description = $"A section called {ExtractedMenuItems[i].MenuItem.Name} of a website of {subject}"; content.Content = ExtractedMenuItems[i].Content; content.LastUpdated = DateTime.UtcNow; menuItem.QdrantPointId = guid; menuItem.PointId = i; await _htmlSnippetProcessor.ProcessAndStoreWebContentAsync(i, content, SiteId); } else { menuItem.QdrantPointId = ExtractedMenuItems[i].MenuItem.QdrantPointId; } } if (!menuItemsSaved) //menuItems just extracted, no content yet because that can be generated only after menuItems have been saved first { //menuItem.Name = ExtractedMenuItems[i].MenuItem.Name; //menuItem.SortOrder = i; //menuItem.SiteInfoId = SiteId; //menuItem.ShowInMainMenu = true; await AddMenuItemAsync(menuItem); } else //menuItems available already { var menuItems = await GetMenuItemsBySiteIdAsync(SiteId); var thisItem = menuItems.FirstOrDefault(x => x.SiteInfoId == SiteId && x.Id == ExtractedMenuItems[i].MenuItem.Id); if (thisItem == null) { //new menu item? await AddMenuItemAsync(menuItem); } else { thisItem.QdrantPointId = menuItem.QdrantPointId; thisItem.SortOrder = ExtractedMenuItems[i].MenuItem.SortOrder; thisItem.ShowInMainMenu = ExtractedMenuItems[i].MenuItem.ShowInMainMenu; await UpdateMenuItemAsync(thisItem); } } } //await ContentEditorService.SaveMenuItemsAsync(menuItems); //menuItemsSaved = true; return "OK"; } catch { return "Error"; } } //public async Task AnalyzeFormFieldsFromText(string text) //{ // string systemMessage = "You are a helpful assistant that extracts structured form field data from plain text."; // string userMessage = // "This is the plain text extracted from a form document. " + // "Please extract all input fields a user is expected to fill in and return a JSON array of objects.\n\n" + // "Each object should have the following structure:\n" + // "- `label`: The label or prompt for the input\n" + // "- `type`: One of `text`, `number`, `date`, `checkbox`, `radio`, or `textarea`\n" + // "- `options`: If type is `radio` or `checkbox`, include the possible options as a list of strings; otherwise, return an empty list\n" + // "- `section`: The section title this field belongs to (if any)\n" + // "- `repeatable`: true if this field (or set of fields) is expected to be repeated for multiple entries (like multiple employers or qualifications), otherwise false\n\n" + // "Return only the JSON array, without any explanation or formatting like ```json.\n\n" + // "Here is the form text:\n\n" + // $"---\n{text}\n---"; // return await _openAIApiService.GetSimpleChatGPTResponseNoSession(systemMessage, userMessage).ConfigureAwait(false); //} public async Task AnalyzeGroupedFormFieldsFromText(string text) { string systemMessage = "You are a helpful assistant that analyzes plain form text and extracts structured, grouped form field definitions."; string userMessage = "This is a plain text version of a printed form. Your task is to extract the **input fields** that a user must fill in. " + "Group related fields under sections where applicable, and return them in a JSON structure.\n\n" + "Each group should follow this structure:\n" + "- `groupName`: Name of the logical section or group (e.g. 'Work Experience')\n" + "- `repeatable`: true if this section can appear multiple times (e.g. multiple employers, qualifications)\n" + "- `fields`: an array of fields within the group\n\n" + "Each field must have:\n" + "- `label`: The field label/question\n" + "- `type`: one of `text`, `number`, `date`, `textarea`, `checkbox`, or `radio`\n" + "- `options`: list of string values if type is `radio` or `checkbox`, or an empty list otherwise\n" + "- `section`: optional section name (can match group name or be more specific)\n\n" + "Return only the JSON array. Do not include any explanation or formatting like ```json.\n\n" + $"Here is the form text:\n\n---\n{text}\n---"; return await _openAIApiService.GetSimpleChatGPTResponseNoSession(systemMessage, userMessage).ConfigureAwait(false); } } }