using Meezi.Core.Branding; using Meezi.Core.Discover; using Meezi.Core.Entities; using Meezi.Core.Enums; using Meezi.Infrastructure.Branding; using Meezi.Infrastructure.Discover; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Meezi.Infrastructure.Data; public static class DevelopmentDataSeeder { public static async Task SeedAsync(IServiceProvider services) { var env = services.GetRequiredService(); if (!env.IsDevelopment()) return; var logger = services.GetRequiredService().CreateLogger("DevelopmentDataSeeder"); await using var scope = services.CreateAsyncScope(); var db = scope.ServiceProvider.GetRequiredService(); var cafe = await db.Cafes.FirstOrDefaultAsync(c => c.Slug == "demo-cafe"); if (cafe is null) { cafe = new Cafe { Id = "cafe_demo_001", Name = "کافه دمو", NameEn = "Demo Cafe", Slug = "demo-cafe", City = "تهران", Address = "تهران، خیابان ولیعصر", Description = "کافه دمو میزی — مناسب کار، میهمانی و قهوه تخصصی.", PlanTier = PlanTier.Pro, PreferredLanguage = "fa", IsVerified = true, SnappfoodVendorId = "demo_vendor" }; var owner = new Employee { Id = "emp_demo_owner", CafeId = cafe.Id, BranchId = "branch_demo_main", Name = "مدیر دمو", Phone = "09212273138", Role = EmployeeRole.Owner, BaseSalary = 0 }; db.Cafes.Add(cafe); db.Employees.Add(owner); await db.SaveChangesAsync(); logger.LogInformation("Development seed: cafe slug={Slug}, owner phone={Phone}", cafe.Slug, owner.Phone); } else if (string.IsNullOrEmpty(cafe.SnappfoodVendorId)) { cafe.SnappfoodVendorId = "demo_vendor"; await db.SaveChangesAsync(); } if (string.IsNullOrEmpty(cafe.ThemeJson)) { cafe.ThemeJson = CafeThemeSerializer.Serialize(new CafeTheme { PaletteId = CafeThemeDefaults.PaletteMeeziGreen, PanelStyle = CafeThemeDefaults.PanelModern, MenuStyle = CafeThemeDefaults.MenuCards, Density = CafeThemeDefaults.DensityComfortable, Radius = CafeThemeDefaults.RadiusMd }); await db.SaveChangesAsync(); } if (string.IsNullOrEmpty(cafe.DiscoverProfileJson)) { cafe.DiscoverProfileJson = CafeDiscoverProfileSerializer.Serialize(new CafeDiscoverProfile { Themes = ["modern", "plants_heavy"], Size = "cozy", Vibes = ["quiet", "cozy"], Occasions = ["date", "friends", "study_work"], SpaceFeatures = ["indoor", "wifi", "plants"], NoiseLevel = "quiet", PriceTier = "mid" }); await db.SaveChangesAsync(); } await SeedDemoReviewsAsync(db, cafe.Id, logger); await SeedDemoBranchAsync(db, cafe.Id, logger); await SeedDemoOpenShiftsAsync(db, cafe.Id, logger); var ownerEmp = await db.Employees.FirstOrDefaultAsync(e => e.Id == "emp_demo_owner"); if (ownerEmp is not null) { var changed = false; if (ownerEmp.BranchId is null) { ownerEmp.BranchId = "branch_demo_main"; changed = true; } if (ownerEmp.Phone != "09212273138") { ownerEmp.Phone = "09212273138"; changed = true; } if (changed) await db.SaveChangesAsync(); } await DemoEmployeesSeeder.EnsureEmployeesAsync(db, cafe.Id, logger); const string taxId = "tax_demo_vat"; await DemoMenuSeeder.EnsureMenuAsync(db, cafe.Id, taxId, logger); await SeedDemoInventoryAsync(db, cafe.Id, logger); await DemoCouponSeeder.EnsureCouponsAsync(db, cafe.Id, logger); await SeedDemoTablesAsync(db, cafe.Id, logger); if (!await db.EmployeeSchedules.AnyAsync(s => s.EmployeeId == "emp_demo_owner")) { for (var day = 0; day <= 6; day++) { db.EmployeeSchedules.Add(new EmployeeSchedule { EmployeeId = "emp_demo_owner", DayOfWeek = day, ShiftType = day is 5 ? ShiftType.DayOff : ShiftType.Morning }); } var monthYear = DateTime.UtcNow.ToString("yyyy-MM"); db.EmployeeSalaries.Add(new EmployeeSalary { EmployeeId = "emp_demo_owner", MonthYear = monthYear, BaseSalary = 25_000_000, OvertimePay = 0, Deductions = 0, NetSalary = 25_000_000, IsPaid = false }); await db.SaveChangesAsync(); logger.LogInformation("Development HR seed for cafe {CafeId}", cafe.Id); } await SeedDemoOrdersAsync(db, cafe.Id, logger); await DiscoverShowcaseSeeder.SeedAsync(db, logger); } private static async Task SeedDemoBranchAsync(AppDbContext db, string cafeId, ILogger logger) { const string branchId = "branch_demo_main"; if (!await db.Branches.AnyAsync(b => b.Id == branchId)) { var now = DateTime.UtcNow; db.Branches.Add(new Branch { Id = branchId, CafeId = cafeId, Name = "شعبه اصلی", City = "تهران", Address = "تهران، خیابان ولیعصر", Phone = "02112345678", IsActive = true, CreatedAt = now, UpdatedAt = now }); await db.SaveChangesAsync(); logger.LogInformation("Development branch seed: {BranchId}", branchId); } } private static async Task SeedDemoOpenShiftsAsync(AppDbContext db, string cafeId, ILogger logger) { const string ownerId = "emp_demo_owner"; var branchIds = await db.Branches .Where(b => b.CafeId == cafeId && b.IsActive) .Select(b => b.Id) .ToListAsync(); var added = false; foreach (var branchId in branchIds) { var hasOpen = await db.RegisterShifts.AnyAsync( s => s.CafeId == cafeId && s.BranchId == branchId && s.Status == ShiftStatus.Open); if (hasOpen) continue; db.RegisterShifts.Add(new Shift { Id = $"shift_open_{branchId}", CafeId = cafeId, BranchId = branchId, OpenedByUserId = ownerId, OpenedAt = DateTime.UtcNow, OpeningCash = 0, ExpectedCash = 0, Status = ShiftStatus.Open }); added = true; } if (added) { await db.SaveChangesAsync(); logger.LogInformation("Development open-shift seed for cafe {CafeId}", cafeId); } } private static async Task SeedDemoInventoryAsync(AppDbContext db, string cafeId, ILogger logger) { if (await db.Ingredients.AnyAsync(i => i.CafeId == cafeId)) return; db.Ingredients.AddRange( new Ingredient { Id = "ing_demo_milk", CafeId = cafeId, Name = "شیر", Unit = "میلی‌لیتر", QuantityOnHand = 12000, ReorderLevel = 2000, ParLevel = 12000, UnitCost = 80, LowStockWarningPercent = 20 }, new Ingredient { Id = "ing_demo_coffee", CafeId = cafeId, Name = "پودر قهوه", Unit = "گرم", QuantityOnHand = 500, ReorderLevel = 100, ParLevel = 500, UnitCost = 12, LowStockWarningPercent = 20 }, new Ingredient { Id = "ing_demo_cups", CafeId = cafeId, Name = "لیوان یکبارمصرف", Unit = "عدد", QuantityOnHand = 80, ReorderLevel = 20, ParLevel = 100, UnitCost = 500, LowStockWarningPercent = 20 }); await db.SaveChangesAsync(); var espresso = await db.MenuItems .FirstOrDefaultAsync(m => m.CafeId == cafeId && m.Name.Contains("اسپرسو")); if (espresso is not null) { db.MenuItemIngredients.AddRange( new MenuItemIngredient { Id = "mii_demo_espresso_coffee", CafeId = cafeId, MenuItemId = espresso.Id, IngredientId = "ing_demo_coffee", QuantityPerUnit = 10 }, new MenuItemIngredient { Id = "mii_demo_espresso_cup", CafeId = cafeId, MenuItemId = espresso.Id, IngredientId = "ing_demo_cups", QuantityPerUnit = 1 }); await db.SaveChangesAsync(); } logger.LogInformation("Development inventory seed for cafe {CafeId}", cafeId); } private static async Task SeedDemoTablesAsync(AppDbContext db, string cafeId, ILogger logger) { var specs = new (string Id, string Number, int Capacity, string? Floor, string? QrCode)[] { ("table_demo_1", "1", 4, "همکف", "demo_table_01"), ("table_demo_2", "2", 2, "همکف", null), ("table_demo_3", "3", 4, "همکف", null), ("table_demo_4", "4", 6, "بالکن", null), ("table_demo_5", "5", 4, "بالکن", null), ("table_demo_6", "6", 2, "بالکن", null), ("table_demo_7", "7", 8, "سالن VIP", null), ("table_demo_8", "8", 4, "سالن VIP", null), }; foreach (var s in specs) { if (await db.Tables.AnyAsync(t => t.Id == s.Id)) continue; db.Tables.Add(new Table { Id = s.Id, CafeId = cafeId, BranchId = "branch_demo_main", Number = s.Number, Capacity = s.Capacity, Floor = s.Floor, QrCode = s.QrCode ?? Guid.NewGuid().ToString("N"), IsActive = true }); } await db.SaveChangesAsync(); logger.LogInformation("Development tables seed (8 tables, QR: demo_table_01) for cafe {CafeId}", cafeId); } private static async Task SeedDemoReviewsAsync(AppDbContext db, string cafeId, ILogger logger) { if (await db.CafeReviews.AnyAsync(r => r.CafeId == cafeId)) return; db.CafeReviews.AddRange( new CafeReview { CafeId = cafeId, AuthorName = "سارا", Rating = 5, Comment = "قهوه و فضا عالی بود.", CreatedAt = DateTime.UtcNow.AddDays(-3) }, new CafeReview { CafeId = cafeId, AuthorName = "علی", Rating = 4, Comment = "سرویس سریع، کیک خوشمزه.", CreatedAt = DateTime.UtcNow.AddDays(-1) }); await db.SaveChangesAsync(); logger.LogInformation("Development reviews seed for cafe {CafeId}", cafeId); } private static async Task SeedDemoOrdersAsync(AppDbContext db, string cafeId, ILogger logger) { if (await db.Orders.AnyAsync(o => o.CafeId == cafeId)) return; var latte = await db.MenuItems.FirstOrDefaultAsync(m => m.Id == "item_demo_latte"); var cake = await db.MenuItems.FirstOrDefaultAsync(m => m.Id == "item_demo_cake"); if (latte is null || cake is null) return; var customer = new Customer { Id = "cust_demo_reports", CafeId = cafeId, Name = "مشتری گزارش", Phone = "09120000001", Group = CustomerGroup.Regular, CreatedAt = DateTime.UtcNow.AddDays(-10) }; db.Customers.Add(customer); for (var i = 0; i < 7; i++) { var createdAt = DateTime.UtcNow.AddDays(-i).AddHours(-2); var subtotal = 215_000m; var tax = Math.Round(subtotal * 0.09m, 0); var order = new Order { Id = $"order_demo_{i}", CafeId = cafeId, CustomerId = i % 2 == 0 ? customer.Id : null, OrderType = OrderType.DineIn, Status = OrderStatus.Delivered, DisplayNumber = i + 1, Subtotal = subtotal, TaxTotal = tax, DiscountAmount = i == 0 ? 15_000m : 0, Total = subtotal + tax - (i == 0 ? 15_000m : 0), CreatedAt = createdAt }; db.Orders.Add(order); db.OrderItems.AddRange( new OrderItem { OrderId = order.Id, MenuItemId = latte.Id, Quantity = 1, UnitPrice = latte.Price }, new OrderItem { OrderId = order.Id, MenuItemId = cake.Id, Quantity = 1, UnitPrice = cake.Price }); } await db.SaveChangesAsync(); logger.LogInformation("Development reports seed: 7 demo orders for cafe {CafeId}", cafeId); } }