using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Npgsql;
using NpgsqlTypes;
using SkyArtShop.Models;

namespace SkyArtShop.Services;

public class PostgreSQLService
{
	private readonly string _connectionString;

	public PostgreSQLService(string connectionString)
	{
		_connectionString = connectionString;
	}

	private async Task<NpgsqlConnection> GetConnectionAsync()
	{
		NpgsqlConnection conn = new NpgsqlConnection(_connectionString);
		await conn.OpenAsync();
		return conn;
	}

	public async Task<List<T>> GetAllAsync<T>(string tableName) where T : class, new()
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "SELECT * FROM " + tableName.ToLower();
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
		List<T> results = new List<T>();
		while (await reader.ReadAsync())
		{
			results.Add(MapToObject<T>(reader));
		}
		return results;
	}

	public async Task<T?> GetByIdAsync<T>(string tableName, string id) where T : class, new()
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "SELECT * FROM " + tableName.ToLower() + " WHERE id = @id";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("id", id);
		using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
		if (await reader.ReadAsync())
		{
			return MapToObject<T>(reader);
		}
		return null;
	}

	public async Task<AdminUser?> GetUserByEmailAsync(string email)
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "SELECT * FROM adminusers WHERE LOWER(email) = LOWER(@email) AND isactive = true";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("email", email);
		using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
		if (await reader.ReadAsync())
		{
			return MapToAdminUser(reader);
		}
		return null;
	}

	public async Task UpdateUserLastLoginAsync(string userId, DateTime lastLogin)
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "UPDATE adminusers SET lastlogin = @lastlogin WHERE id = @id";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("id", userId);
		cmd.Parameters.AddWithValue("lastlogin", lastLogin);
		await cmd.ExecuteNonQueryAsync();
	}

	public async Task CreateAdminUserAsync(AdminUser user)
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "INSERT INTO adminusers (id, email, passwordhash, name, role, permissions, isactive, createdby, createdat)\n                        VALUES (@id, @email, @passwordhash, @name, @role, @permissions::jsonb, @isactive, @createdby, @createdat)";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("id", Guid.NewGuid().ToString());
		cmd.Parameters.AddWithValue("email", user.Email);
		cmd.Parameters.AddWithValue("passwordhash", user.PasswordHash);
		cmd.Parameters.AddWithValue("name", user.Name);
		cmd.Parameters.AddWithValue("role", user.Role);
		cmd.Parameters.AddWithValue("permissions", JsonSerializer.Serialize(user.Permissions));
		cmd.Parameters.AddWithValue("isactive", user.IsActive);
		cmd.Parameters.AddWithValue("createdby", user.CreatedBy);
		cmd.Parameters.AddWithValue("createdat", user.CreatedAt);
		await cmd.ExecuteNonQueryAsync();
	}

	public async Task<List<Product>> GetProductsAsync()
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "SELECT * FROM products WHERE isactive = true ORDER BY createdat DESC";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
		List<Product> results = new List<Product>();
		while (await reader.ReadAsync())
		{
			results.Add(MapToProduct(reader));
		}
		return results;
	}

	public async Task<Product?> GetProductBySlugAsync(string slug)
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "SELECT * FROM products WHERE slug = @slug AND isactive = true";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("slug", slug);
		using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
		if (await reader.ReadAsync())
		{
			return MapToProduct(reader);
		}
		return null;
	}

	public async Task<Page?> GetPageBySlugAsync(string slug)
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "SELECT * FROM pages WHERE pageslug = @slug AND isactive = true";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("slug", slug);
		using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
		if (await reader.ReadAsync())
		{
			return MapToPage(reader);
		}
		return null;
	}

	public async Task<SiteSettings?> GetSiteSettingsAsync()
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "SELECT * FROM sitesettings LIMIT 1";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
		if (await reader.ReadAsync())
		{
			return MapToSiteSettings(reader);
		}
		return null;
	}

	public async Task<List<MenuItem>> GetMenuItemsAsync()
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "SELECT * FROM menuitems WHERE isactive = true ORDER BY displayorder";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
		List<MenuItem> results = new List<MenuItem>();
		while (await reader.ReadAsync())
		{
			results.Add(MapToMenuItem(reader));
		}
		return results;
	}

	public async Task InsertAsync<T>(string tableName, T entity) where T : class
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		Type typeFromHandle = typeof(T);
		List<PropertyInfo> list = (from p in typeFromHandle.GetProperties()
			where p.CanRead && p.Name != "Id"
			select p).ToList();
		PropertyInfo property = typeFromHandle.GetProperty("Id");
		if (property != null && string.IsNullOrEmpty(property.GetValue(entity)?.ToString()))
		{
			property.SetValue(entity, Guid.NewGuid().ToString());
		}
		string value = string.Join(", ", list.Select((PropertyInfo p) => p.Name.ToLower()).Prepend("id"));
		string value2 = string.Join(", ", list.Select((PropertyInfo p) => "@" + p.Name.ToLower()).Prepend("@id"));
		string cmdText = $"INSERT INTO {tableName.ToLower()} ({value}) VALUES ({value2})";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("id", property?.GetValue(entity)?.ToString() ?? Guid.NewGuid().ToString());
		foreach (PropertyInfo item in list)
		{
			object value3 = item.GetValue(entity);
			string parameterName = item.Name.ToLower();
			if (value3 == null)
			{
				cmd.Parameters.AddWithValue(parameterName, DBNull.Value);
			}
			else if (item.PropertyType == typeof(List<string>) || item.PropertyType == typeof(List<ProductVariant>) || item.PropertyType == typeof(List<TeamMember>) || item.PropertyType == typeof(Dictionary<string, string>))
			{
				cmd.Parameters.AddWithValue(parameterName, NpgsqlDbType.Jsonb, JsonSerializer.Serialize(value3));
			}
			else if (item.PropertyType == typeof(DateTime) || item.PropertyType == typeof(DateTime?))
			{
				cmd.Parameters.AddWithValue(parameterName, value3);
			}
			else if (item.PropertyType == typeof(bool) || item.PropertyType == typeof(bool?))
			{
				cmd.Parameters.AddWithValue(parameterName, value3);
			}
			else
			{
				cmd.Parameters.AddWithValue(parameterName, value3);
			}
		}
		await cmd.ExecuteNonQueryAsync();
	}

	public async Task UpdateAsync<T>(string tableName, string id, T entity) where T : class
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		Type typeFromHandle = typeof(T);
		List<PropertyInfo> list = (from p in typeFromHandle.GetProperties()
			where p.CanRead && p.Name != "Id"
			select p).ToList();
		string value = string.Join(", ", list.Select((PropertyInfo p) => p.Name.ToLower() + " = @" + p.Name.ToLower()));
		string cmdText = $"UPDATE {tableName.ToLower()} SET {value} WHERE id = @id";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("id", id);
		foreach (PropertyInfo item in list)
		{
			object value2 = item.GetValue(entity);
			string parameterName = item.Name.ToLower();
			if (value2 == null)
			{
				cmd.Parameters.AddWithValue(parameterName, DBNull.Value);
			}
			else if (item.PropertyType == typeof(List<string>) || item.PropertyType == typeof(List<ProductVariant>) || item.PropertyType == typeof(List<TeamMember>) || item.PropertyType == typeof(Dictionary<string, string>))
			{
				cmd.Parameters.AddWithValue(parameterName, NpgsqlDbType.Jsonb, JsonSerializer.Serialize(value2));
			}
			else if (item.PropertyType == typeof(DateTime) || item.PropertyType == typeof(DateTime?))
			{
				cmd.Parameters.AddWithValue(parameterName, value2);
			}
			else if (item.PropertyType == typeof(bool) || item.PropertyType == typeof(bool?))
			{
				cmd.Parameters.AddWithValue(parameterName, value2);
			}
			else
			{
				cmd.Parameters.AddWithValue(parameterName, value2);
			}
		}
		await cmd.ExecuteNonQueryAsync();
	}

	public async Task DeleteAsync<T>(string tableName, string id) where T : class
	{
		using NpgsqlConnection conn = await GetConnectionAsync();
		string cmdText = "DELETE FROM " + tableName.ToLower() + " WHERE id = @id";
		using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
		cmd.Parameters.AddWithValue("id", id);
		await cmd.ExecuteNonQueryAsync();
	}

	private T MapToObject<T>(NpgsqlDataReader reader) where T : class, new()
	{
		Type typeFromHandle = typeof(T);
		if (typeFromHandle == typeof(AdminUser))
		{
			return (MapToAdminUser(reader) as T) ?? new T();
		}
		if (typeFromHandle == typeof(Product))
		{
			return (MapToProduct(reader) as T) ?? new T();
		}
		if (typeFromHandle == typeof(Page))
		{
			return (MapToPage(reader) as T) ?? new T();
		}
		if (typeFromHandle == typeof(MenuItem))
		{
			return (MapToMenuItem(reader) as T) ?? new T();
		}
		if (typeFromHandle == typeof(SiteSettings))
		{
			return (MapToSiteSettings(reader) as T) ?? new T();
		}
		if (typeFromHandle == typeof(PortfolioCategory))
		{
			return (MapToPortfolioCategory(reader) as T) ?? new T();
		}
		if (typeFromHandle == typeof(PortfolioProject))
		{
			return (MapToPortfolioProject(reader) as T) ?? new T();
		}
		if (typeFromHandle == typeof(BlogPost))
		{
			return (MapToBlogPost(reader) as T) ?? new T();
		}
		if (typeFromHandle == typeof(HomepageSection))
		{
			return (MapToHomepageSection(reader) as T) ?? new T();
		}
		return new T();
	}

	private AdminUser MapToAdminUser(NpgsqlDataReader reader)
	{
		AdminUser adminUser = new AdminUser();
		adminUser.Id = reader["id"].ToString();
		adminUser.Email = reader["email"].ToString() ?? "";
		adminUser.PasswordHash = reader["passwordhash"].ToString() ?? "";
		adminUser.Name = reader["name"].ToString() ?? "";
		adminUser.Role = reader["role"].ToString() ?? "Admin";
		adminUser.Permissions = JsonSerializer.Deserialize<List<string>>(reader["permissions"].ToString() ?? "[]") ?? new List<string>();
		adminUser.IsActive = (reader["isactive"] as bool?) ?? true;
		adminUser.CreatedBy = reader["createdby"]?.ToString() ?? "";
		adminUser.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
		adminUser.LastLogin = (reader.IsDBNull(reader.GetOrdinal("lastlogin")) ? ((DateTime?)null) : new DateTime?(reader.GetDateTime(reader.GetOrdinal("lastlogin"))));
		adminUser.Phone = reader["phone"]?.ToString() ?? "";
		adminUser.Notes = reader["notes"]?.ToString() ?? "";
		return adminUser;
	}

	private Product MapToProduct(NpgsqlDataReader reader)
	{
		Product product = new Product();
		product.Id = reader["id"].ToString();
		product.Name = reader["name"].ToString() ?? "";
		product.Slug = reader["slug"].ToString() ?? "";
		product.SKU = reader["sku"]?.ToString() ?? "";
		product.ShortDescription = reader["shortdescription"]?.ToString() ?? "";
		product.Description = reader["description"]?.ToString() ?? "";
		product.Price = (reader["price"] as decimal?).GetValueOrDefault();
		product.Category = reader["category"]?.ToString() ?? "";
		product.Color = reader["color"]?.ToString() ?? "";
		product.Colors = JsonSerializer.Deserialize<List<string>>(reader["colors"].ToString() ?? "[]") ?? new List<string>();
		product.ImageUrl = reader["imageurl"]?.ToString() ?? "";
		product.Images = JsonSerializer.Deserialize<List<string>>(reader["images"].ToString() ?? "[]") ?? new List<string>();
		product.IsFeatured = reader["isfeatured"] as bool? == true;
		product.IsTopSeller = reader["istopseller"] as bool? == true;
		product.StockQuantity = (reader["stockquantity"] as int?).GetValueOrDefault();
		product.IsActive = (reader["isactive"] as bool?) ?? true;
		product.UnitsSold = (reader["unitssold"] as int?).GetValueOrDefault();
		product.TotalRevenue = (reader["totalrevenue"] as decimal?).GetValueOrDefault();
		product.AverageRating = (reader["averagerating"] as double?).GetValueOrDefault();
		product.TotalReviews = (reader["totalreviews"] as int?).GetValueOrDefault();
		product.CostPrice = (reader["costprice"] as decimal?).GetValueOrDefault();
		product.Tags = JsonSerializer.Deserialize<List<string>>(reader["tags"].ToString() ?? "[]") ?? new List<string>();
		product.MetaDescription = reader["metadescription"]?.ToString() ?? "";
		product.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
		product.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
		return product;
	}

	private Page MapToPage(NpgsqlDataReader reader)
	{
		Page page = new Page();
		page.Id = reader["id"].ToString();
		page.PageName = reader["pagename"].ToString() ?? "";
		page.PageSlug = reader["pageslug"].ToString() ?? "";
		page.Title = reader["title"]?.ToString() ?? "";
		page.Subtitle = reader["subtitle"]?.ToString() ?? "";
		page.HeroImage = reader["heroimage"]?.ToString() ?? "";
		page.Content = reader["content"]?.ToString() ?? "";
		page.MetaDescription = reader["metadescription"]?.ToString() ?? "";
		page.ImageGallery = JsonSerializer.Deserialize<List<string>>(reader["imagegallery"].ToString() ?? "[]") ?? new List<string>();
		page.IsActive = (reader["isactive"] as bool?) ?? true;
		page.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
		page.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
		return page;
	}

	private MenuItem MapToMenuItem(NpgsqlDataReader reader)
	{
		MenuItem menuItem = new MenuItem();
		menuItem.Id = reader["id"].ToString();
		menuItem.Label = reader["label"].ToString() ?? "";
		menuItem.Url = reader["url"].ToString() ?? "";
		menuItem.DisplayOrder = (reader["displayorder"] as int?).GetValueOrDefault();
		menuItem.IsActive = (reader["isactive"] as bool?) ?? true;
		menuItem.ShowInNavbar = (reader["showinnavbar"] as bool?) ?? true;
		menuItem.ShowInDropdown = (reader["showindropdown"] as bool?) ?? true;
		menuItem.OpenInNewTab = reader["openinnewtab"] as bool? == true;
		menuItem.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
		return menuItem;
	}

	private SiteSettings MapToSiteSettings(NpgsqlDataReader reader)
	{
		SiteSettings siteSettings = new SiteSettings();
		siteSettings.Id = reader["id"].ToString();
		siteSettings.SiteName = reader["sitename"]?.ToString() ?? "Sky Art Shop";
		siteSettings.SiteTagline = reader["sitetagline"]?.ToString() ?? "";
		siteSettings.ContactEmail = reader["contactemail"]?.ToString() ?? "";
		siteSettings.ContactPhone = reader["contactphone"]?.ToString() ?? "";
		siteSettings.InstagramUrl = reader["instagramurl"]?.ToString() ?? "#";
		siteSettings.FooterText = reader["footertext"]?.ToString() ?? "";
		siteSettings.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
		return siteSettings;
	}

	private PortfolioCategory MapToPortfolioCategory(NpgsqlDataReader reader)
	{
		PortfolioCategory portfolioCategory = new PortfolioCategory();
		portfolioCategory.Id = reader["id"].ToString();
		portfolioCategory.Name = reader["name"].ToString() ?? "";
		portfolioCategory.Slug = reader["slug"].ToString() ?? "";
		portfolioCategory.Description = reader["description"]?.ToString() ?? "";
		portfolioCategory.ThumbnailImage = reader["thumbnailimage"]?.ToString() ?? "";
		portfolioCategory.FeaturedImage = reader["featuredimage"]?.ToString() ?? "";
		portfolioCategory.DisplayOrder = (reader["displayorder"] as int?).GetValueOrDefault();
		portfolioCategory.IsActive = (reader["isactive"] as bool?) ?? true;
		portfolioCategory.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
		portfolioCategory.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
		return portfolioCategory;
	}

	private BlogPost MapToBlogPost(NpgsqlDataReader reader)
	{
		BlogPost blogPost = new BlogPost();
		blogPost.Id = reader["id"].ToString();
		blogPost.Title = reader["title"].ToString() ?? "";
		blogPost.Slug = reader["slug"].ToString() ?? "";
		blogPost.Content = reader["content"]?.ToString() ?? "";
		blogPost.Excerpt = reader["excerpt"]?.ToString() ?? "";
		blogPost.FeaturedImage = reader["featuredimage"]?.ToString() ?? "";
		blogPost.Author = reader["author"]?.ToString() ?? "";
		blogPost.Tags = JsonSerializer.Deserialize<List<string>>(reader["tags"].ToString() ?? "[]") ?? new List<string>();
		blogPost.IsPublished = (reader["ispublished"] as bool?) ?? true;
		blogPost.PublishedDate = (reader["publisheddate"] as DateTime?) ?? DateTime.UtcNow;
		blogPost.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
		blogPost.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
		return blogPost;
	}

	private HomepageSection MapToHomepageSection(NpgsqlDataReader reader)
	{
		HomepageSection homepageSection = new HomepageSection();
		homepageSection.Id = reader["id"].ToString();
		homepageSection.Title = reader["title"]?.ToString() ?? "";
		homepageSection.Subtitle = reader["subtitle"]?.ToString() ?? "";
		homepageSection.Content = reader["content"]?.ToString() ?? "";
		homepageSection.SectionType = reader["sectiontype"]?.ToString() ?? "";
		homepageSection.ImageUrl = reader["imageurl"]?.ToString() ?? "";
		homepageSection.ButtonText = reader["buttontext"]?.ToString() ?? "";
		homepageSection.ButtonUrl = reader["buttonurl"]?.ToString() ?? "";
		homepageSection.IsActive = (reader["isactive"] as bool?) ?? true;
		homepageSection.DisplayOrder = (reader["displayorder"] as int?).GetValueOrDefault();
		homepageSection.AdditionalData = JsonSerializer.Deserialize<Dictionary<string, string>>(reader["additionaldata"]?.ToString() ?? "{}") ?? new Dictionary<string, string>();
		homepageSection.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
		homepageSection.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
		return homepageSection;
	}

	private PortfolioProject MapToPortfolioProject(NpgsqlDataReader reader)
	{
		PortfolioProject portfolioProject = new PortfolioProject();
		portfolioProject.Id = reader["id"].ToString();
		portfolioProject.CategoryId = reader["categoryid"]?.ToString() ?? "";
		portfolioProject.Title = reader["title"]?.ToString() ?? "";
		portfolioProject.Description = reader["description"]?.ToString() ?? "";
		portfolioProject.FeaturedImage = reader["featuredimage"]?.ToString() ?? "";
		portfolioProject.Images = JsonSerializer.Deserialize<List<string>>(reader["images"].ToString() ?? "[]") ?? new List<string>();
		portfolioProject.DisplayOrder = (reader["displayorder"] as int?).GetValueOrDefault();
		portfolioProject.IsActive = (reader["isactive"] as bool?) ?? true;
		portfolioProject.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
		portfolioProject.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
		return portfolioProject;
	}
}
