using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using eprintServer.Services; using eprintServer.Requests; using eprintServer.Responses; using eprintServer.Entities; using eprintServer.Enums; using eprintServer.Data; using Newtonsoft.Json; using Microsoft.EntityFrameworkCore; using eprintServer.DTOs; [Route("api/[controller]")] [ApiController] [AllowAnonymous] public class PublicController : ControllerBase { private readonly BlogService _blogService; private readonly FaqService _faqService; private readonly ProductService _productService; private readonly BrandingColorService _brandingService; private readonly ContactService _contactService; private readonly TranslationService _translationService; private readonly ProductCategoryService _categoryService; private readonly OfferService _offerService; private readonly CustomizableProductService _customizableProductService; private readonly StepService _stepService; private readonly FormElementService _formElementService; private readonly FormElementOptionService _formElementOptionService; private readonly CarouselService _carouselService; private readonly UserService _userService; private readonly OAuthSettingsService _oauthSettingsService; private readonly DataContext _context; private readonly ILogger _logger; private readonly OAuthSettingsService _settingsService; public PublicController(UserService userService, BlogService blogService, FaqService faqService, ProductService productService, BrandingColorService brandingService, ContactService contactService, TranslationService translationService, ProductCategoryService categoryService, DataContext context, ILogger logger, CustomizableProductService customizableProductService, StepService stepService, FormElementService formElementService, FormElementOptionService formElementOptionService, CarouselService carouselService, OAuthSettingsService oauthSettingsService, OAuthSettingsService settingsService) { _blogService = blogService; _faqService = faqService; _productService = productService; _contactService = contactService; _brandingService = brandingService; _translationService = translationService; _categoryService = categoryService; _customizableProductService = customizableProductService; _formElementService = formElementService; _formElementOptionService = formElementOptionService; _stepService = stepService; _carouselService = carouselService; _offerService = new OfferService(context); _userService = userService; _oauthSettingsService = oauthSettingsService; _context = context; _logger = logger; _settingsService = settingsService; } [HttpGet("get-setting")] public async Task> GetSettings() { var settings = await _settingsService.GetOrCreateSettingsAsync(); return Ok(settings.ToResponseDto()); } [HttpGet("get-all-blogs")] public async Task>> GetBlogPosts() { var blogs = await _blogService.GetAllBlogPostsAsync(); var response = blogs .Select(BlogPostResponse.FromEntity) .ToList(); return Ok(response); } [HttpGet("get-blog-by-id/{blogId:int}")] public async Task> GetBlogPost(int blogId) { var blog = await _blogService.GetBlogPostByIdAsync(blogId); if (blog == null) { return NotFound($"Blog post with ID {blogId} not found."); } return Ok(BlogPostResponse.FromEntity(blog)); } [HttpGet("get-all-faq")] public async Task>> GetFaqs() { var faqs = await _faqService.GetAllFaqsAsync(); var response = faqs .Select(FaqResponse.FromEntity) .ToList(); return Ok(response); } [HttpGet("get-faq-by-id/{faqId:int}")] public async Task> GetFaqById(int faqId) { var faq = await _faqService.GetFaqByIdAsync(faqId); if (faq == null) { return NotFound($"FAQ with ID {faqId} not found."); } return Ok(FaqResponse.FromEntity(faq)); } [HttpGet("get-all-faqs-paginated")] public async Task>> GetFaqsPaginated( [FromQuery] int page = 0, [FromQuery] int size = 5 ) { var pagedFaqs = await _faqService.GetFaqsPaginatedAsync(page, size); var responseItems = pagedFaqs.Items .Select(FaqResponse.FromEntity) .ToList(); var pagedResponse = new PagedResult( responseItems, pagedFaqs.TotalCount, pagedFaqs.CurrentPage, pagedFaqs.PageSize ); return Ok(pagedResponse); } [HttpGet("all-sellable-products-list")] public async Task>> GetAllProducts( [FromQuery] string? search, [FromQuery] long? categoryId, [FromQuery] SellableProductStatusEnum? status) { var products = await _productService.GetAllProductsAsync(search, categoryId, status); var response = products .Select(SellableProductResponse.FromEntity) .ToList(); return Ok(response); } [HttpGet("all-sellable-products-paginator")] public async Task>> GetPagedProducts( [FromQuery] int page = 0, [FromQuery] int size = 5, [FromQuery] string? search = null, [FromQuery] long? categoryId = null, [FromQuery] SellableProductStatusEnum? status = null, [FromQuery] decimal? lowPrice = null, [FromQuery] decimal? highPrice = null) { var pagedResult = await _productService.GetPagedProductsAsync(page, size, search, categoryId, status, lowPrice, highPrice); var responseItems = pagedResult.Items .Select(SellableProductResponse.FromEntity) .ToList(); var response = new PagedResult( responseItems, pagedResult.TotalCount, pagedResult.CurrentPage, pagedResult.PageSize); return Ok(response); } [HttpGet("sellable-product-by-id/{productId:int}")] public async Task> GetProductById(int productId) { var product = await _productService.GetProductByIdAsync(productId); if (product == null) { return NotFound($"Product with ID {productId} not found."); } return Ok(SellableProductResponse.FromEntity(product)); } [HttpGet("get-branding-settings")] [AllowAnonymous] public async Task> GetBrandingSettings() { var settings = await _brandingService.GetBrandingSettingsAsync(); if (settings == null) { return Ok(new BrandingSettingsRequest()); } var response = new BrandingSettingsRequest { PrimaryColor = settings.PrimaryColor ?? "", SecondaryColor = settings.SecondaryColor ?? "", AccentColor = settings.AccentColor ?? "", ButtonColor = settings.ButtonColor ?? "", HeaderColor = settings.HeaderColor ?? "", PlatformName = settings.PlatformName ?? "", PlatformEmail = settings.PlatformEmail ?? "", Logo = settings.Logo, Favicon = settings.Favicon }; return Ok(response); } [HttpGet("get-contact-settings")] public async Task> GetContactInfo() { var settings = await _contactService.GetOrInitializeContactSettingsAsync(); return Ok(ContactInfoResponse.FromEntity(settings)); } [HttpGet("active-transaction-languages")] public async Task> GetActiveConfig() { var setting = await _translationService.GetTranslationSettingAsync(); return Ok(setting); } [HttpGet("get-all-products-cateogies-paginated")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> GetAllCategoriesPaged( [FromQuery] string? search, [FromQuery] bool? archive, [FromQuery] int page = 0, [FromQuery] int size = 5) { try { var pagedResult = await _categoryService.GetAllCategoriesPagedAsync(search, archive, page, size); return Ok(pagedResult); } catch (Exception e) { return StatusCode(StatusCodes.Status500InternalServerError, $"Error fetching paged categories: {e.Message}"); } } [HttpGet("get-all-currencies")] public async Task>> GetCurrencies() { try { _logger.LogInformation("Fetching all currencies."); var currencies = await _context.Currencies .OrderBy(c => c.Code) .ToListAsync(); if (!currencies.Any()) { return NoContent(); } return Ok(currencies); } catch (Exception ex) { _logger.LogError(ex, "Error occurred while fetching currencies."); return StatusCode(500, "Internal server error while retrieving currencies."); } } [HttpGet("all_offers_list")] public async Task>> GetAll() { var offers = await _offerService.GetAllOffersAsync(true, null, null); return Ok(offers); } [HttpGet("get_offer_by_id/{id}")] public async Task> GetById(int id) { var offer = await _offerService.GetOfferByIdAsync(id); if (offer == null) { return NotFound(); } return Ok(offer); } [HttpGet("get-customizable-product-by-id/{id}")] [AllowAnonymous] public async Task GetById(long id) { try { var product = await _customizableProductService.GetProductDetailByIdAsync(id); return Ok(product); } catch (Exception ex) { return NotFound(new { message = ex.Message }); } } [HttpGet("get-all-customizable-products")] [AllowAnonymous] public async Task GetAll([FromQuery] bool includeArchived = false) { var products = await _customizableProductService.GetAllProductsAsync(includeArchived); return Ok(products); } [HttpGet("get-all-customizable-products-pg")] public async Task>> GetProducts( [FromQuery] int page = 0, [FromQuery] int size = 5, [FromQuery] long? categoryId = null, [FromQuery] decimal? lowPrice = null, [FromQuery] decimal? highPrice = null, [FromQuery] string? search = null) { var result = await _customizableProductService.GetAllProductsPgAsync( page, size, false, categoryId, lowPrice, highPrice, search); return Ok(result); } [HttpGet("get-all-customizable-products-by-category/{categoryId}")] [AllowAnonymous] public async Task GetByCategory(long categoryId) { var products = await _customizableProductService.GetProductsByCategoryAsync(categoryId); return Ok(products); } [HttpPut("get-step-by-id/{id}")] [AllowAnonymous] public async Task GetStepById(long id) { try { var step = await _stepService.GetStepByIdAsync(id); return Ok(step); } catch (Exception ex) { return NotFound(new { message = ex.Message }); } } [HttpGet("get-all-carousels-list")] public async Task GetAllCarousels() => Ok(await _carouselService.GetAllFIltredAsync(true)); [HttpGet("get-carousel-by-id/{id}")] public async Task GetCarouselById(long id) { var item = await _carouselService.GetByIdAsync(id); return item == null ? NotFound() : Ok(item); } [HttpGet("get-category-products/{categoryId}")] public async Task>> GetCategoryProducts( long categoryId, [FromQuery] int page = 0, [FromQuery] int size = 10) { try { var result = await _categoryService.GetCategoryProductsPagedAsync(categoryId, page, size); if (result == null) return NotFound($"Category {categoryId} not found."); return Ok(result); } catch (Exception e) { return StatusCode(500, $"Internal server error: {e.Message}"); } } /* [HttpGet("confirm-payment")] public async Task ConfirmPayment([FromQuery] string token, [FromQuery] long orderId) { // 1. Fetch Thawani credentials from database settings var settings = await _oauthSettingsService.GetOrCreateSettingsAsync(); if (string.IsNullOrEmpty(settings.ThawaniRoot) || string.IsNullOrEmpty(settings.ThawaniApiKey)) { return StatusCode(406, "Payment credentials are not set yet."); } // 2. Get User from Token (Spring Boot: findByUserName) var username = _userService.GetUserNameFromToken(token); if (string.IsNullOrEmpty(username)) return Unauthorized("Invalid token."); // 3. Get the Order and its saved Session ID (Spring Boot: findTopBySupplierOrderByTimestmpDesc) var order = await _context.Orders .Include(o => o.Payments) .FirstOrDefaultAsync(o => o.Id == orderId); if (order == null) return NotFound("Order not found."); string session_id = order.ThawaniSessionId; // retrieved from DB if (string.IsNullOrEmpty(session_id)) { return BadRequest("This order does not have an active payment session."); } // 4. Call Thawani API (Spring Boot: RestTemplate exchange) string root = settings.ThawaniRoot.TrimEnd('/'); string apiUrl = $"{root}/api/v1/checkout/session/{session_id}"; using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); httpClient.DefaultRequestHeaders.Add("thawani-api-key", settings.ThawaniApiKey); try { var response = await httpClient.GetAsync(apiUrl); if (response.IsSuccessStatusCode) { var jsonResponse = await response.Content.ReadAsStringAsync(); dynamic thawaniData = JsonConvert.DeserializeObject(jsonResponse); // 5. Check payment status (Spring Boot: "paid".equals(data.getPayment_status())) string paymentStatus = thawaniData?.data?.payment_status; if (paymentStatus == "paid") { // Update Order Status if not already paid if (order.Status != OrderStatus.PAID) { order.Status = OrderStatus.PAID; // Update the payment record status as well var payment = order.Payments.OrderByDescending(p => p.CreatedAt).FirstOrDefault(); if (payment != null) { payment.Status = PaymentStatus.Paid; } await _context.SaveChangesAsync(); } // Redirect to Frontend Success return Redirect(string.Format("http://localhost:3001/payment/success?orderId={0}", orderId)); } } } catch (Exception ex) { // Log error Console.WriteLine($"Error confirming payment: {ex.Message}"); } // 6. Fail/Cancel Redirect return Redirect("http://localhost:3001/payment/cancel"); } */ [HttpGet("confirm-payment")] public async Task ConfirmPayment([FromQuery] string token, [FromQuery] long orderId) { var settings = await _oauthSettingsService.GetOrCreateSettingsAsync(); if (string.IsNullOrEmpty(settings.ThawaniRoot) || string.IsNullOrEmpty(settings.ThawaniApiKey)) return StatusCode(406, "Payment credentials are not set yet."); var username = _userService.GetUserNameFromToken(token); if (string.IsNullOrEmpty(username)) return Unauthorized("Invalid token."); // IMPORTANT: Include OrderItems so we can move them to the Invoice var order = await _context.Orders .Include(o => o.Payments) .Include(o => o.OrderItems) .FirstOrDefaultAsync(o => o.Id == orderId); if (order == null) return NotFound("Order not found."); string session_id = order.ThawaniSessionId; if (string.IsNullOrEmpty(session_id)) return BadRequest("No active session."); string root = settings.ThawaniRoot.TrimEnd('/'); string apiUrl = $"{root}/api/v1/checkout/session/{session_id}"; using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); httpClient.DefaultRequestHeaders.Add("thawani-api-key", settings.ThawaniApiKey); try { var response = await httpClient.GetAsync(apiUrl); if (response.IsSuccessStatusCode) { var jsonResponse = await response.Content.ReadAsStringAsync(); dynamic thawaniData = JsonConvert.DeserializeObject(jsonResponse); string paymentStatus = thawaniData?.data?.payment_status; if (!string.IsNullOrEmpty(paymentStatus) && paymentStatus.Equals("paid", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine($"DEBUG: Thawani returned status *********************** : '{paymentStatus}'"); if (order.Status != OrderStatus.PAID) { // 1. Update Order & Payment Status order.Status = OrderStatus.PAID; var payment = order.Payments.OrderByDescending(p => p.CreatedAt).FirstOrDefault(); if (payment != null) payment.Status = PaymentStatus.Paid; // 2. Generate and Save Invoice Automatically var invoice = new Invoice { OrderId = order.Id, UserId = order.UserId, TotalAmount = order.TotalAmount, InvoiceDate = DateTime.UtcNow, Status = InvoiceStatus.PAID, Timestamp = DateTime.UtcNow, // We map the existing items to the invoice InvoiceItems = order.OrderItems }; _context.Invoices.Add(invoice); // 3. Save everything (Order update + New Invoice + Item links) await _context.SaveChangesAsync(); } return Redirect($"http://localhost:3001/payment/success?orderId={orderId}"); } } } catch (Exception ex) { Console.WriteLine($"Error confirming payment: {ex.Message}"); } return Redirect("http://localhost:3001/payment/cancel"); } }