using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization;
using Sozsoft.Platform.Data.Seeds;
using Sozsoft.Platform.Enums;
using Sozsoft.Platform.Localization;
using Volo.Abp;
using Volo.Abp.Domain.Services;
namespace Sozsoft.Mcp.Services;
///
/// Yeni tabloyu SQL Query Manager'daki tablo tasarimcisiyla ayni sozlesmeyle kurar: script
/// tasarimcinin generateCreateTableSql ciktisiyla ayni bicimdedir ve tasarimcinin deploy
/// sonrasi yazdigi yerlere (sql/object/{Tablo}.sql ve PostgreSQL esi postgres/object/{Tablo}.sql) yazilir; boylece tablo
/// tasarimcida acilip duzenlenebilir ve veritabani sifirlandiginda seed ile geri gelir.
///
/// Yalnizca olusturma yapar. Var olan tabloya dokunulmaz — kolon ekleme/degistirme tasarimcinin
/// isidir ve sema degisikligi kurallarina (kayipsiz ekleme, degistirme/dusurme yok) tabidir.
/// Kolon tipi ve varsayilan deger serbest metin degil, beyaz listeden gecer; istemci SQL yazamaz.
///
///
public interface IMcpTableCreator
{
Task CreateAsync(McpTableDefinition definition);
}
public class McpTableCreator(
IMcpQueryExecutor queryExecutor,
IConfiguration configuration,
IStringLocalizer localizer) : DomainService, IMcpTableCreator
{
private const string SchemaName = "dbo";
private const int DefaultTextLength = 100;
private const int MaxTextLength = 4000;
private static readonly Regex SafeIdentifierRegex = new(
@"^[A-Za-z_][A-Za-z0-9_]{0,127}$",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
///
/// Varsayilan deger olarak kabul edilenler: sayi, metin sabiti ve tasarimcinin kendi
/// sablonlarinda kullandigi fonksiyonlar. Baska bir ifade script'e hic girmez.
///
///
/// Istemcinin SQL bicimiyle yazdigi tip: nvarchar(50), nvarchar (MAX),
/// decimal(18, 4). Boyut tipten ayrilir; tip adi yine beyaz listeden gecer.
///
private static readonly Regex SizedTypeRegex = new(
@"^(?[A-Za-z0-9]+)\s*\(\s*(?[^)]*?)\s*\)$",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
/// Tasarimci decimal'i her zaman bu kesinlikle yazar (ToColumnLine).
private const string DecimalSize = "18,4";
private static readonly Regex SafeDefaultRegex = new(
@"^(?:-?\d{1,18}(?:\.\d{1,18})?|N?'(?:[^']|'')*'|NEWID\(\)|GETDATE\(\)|GETUTCDATE\(\)|SYSUTCDATETIME\(\))$",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
/// Tasarimcinin tip listesi (DATA_TYPES) ile aynidir.
private static readonly string[] SupportedTypes =
[
"nvarchar", "nvarchar(MAX)", "int", "bigint", "decimal", "float", "bit",
"datetime", "datetime2", "date", "uniqueidentifier", "money"
];
// Sablon kolonlari tasarimcinin FULL_AUDIT_COLUMNS / TENANT_COLUMN tanimlariyla aynidir.
private static readonly McpTableColumn IdColumn = new("Id", "uniqueidentifier", null, false, "NEWID()");
private static readonly McpTableColumn TenantColumn = new("TenantId", "uniqueidentifier", null, true, null);
private static readonly McpTableColumn[] AuditColumns =
[
new("CreationTime", "datetime2", null, false, "GETUTCDATE()"),
new("CreatorId", "uniqueidentifier", null, true, null),
new("LastModificationTime", "datetime2", null, true, null),
new("LastModifierId", "uniqueidentifier", null, true, null),
new("IsDeleted", "bit", null, false, "0"),
new("DeletionTime", "datetime2", null, true, null),
new("DeleterId", "uniqueidentifier", null, true, null)
];
public async Task CreateAsync(McpTableDefinition definition)
{
Check.NotNull(definition, nameof(definition));
var tableName = (definition.TableName ?? string.Empty).Trim();
EnsureSafeIdentifier(tableName);
var columns = BuildColumns(definition);
var context = await queryExecutor.ResolveDataSourceAsync();
var isPostgreSql = context.DataSourceType == DataSourceTypeEnum.Postgresql;
var existing = await context.Repository.ExecuteScalarAsync