modules/backend/Write-DomainLayer.ps1

param(
    [string]$ProjectName,
    [string]$OrmMode,
    [string]$OutputPath,
    [string[]]$Entities
)

. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Utils.ps1")

$base = Join-Path $OutputPath "src\$ProjectName.Domain"

function Write-File($rel, $con) { Write-CoreFile (Join-Path $base $rel) $con }
function T($template, $e = "") {
    $res = $template.Replace("{P}", $ProjectName)
    if ($e) { $res = $res.Replace("{E}", $e) }
    return $res
}

# ---- IAggregateRoot ----
Write-File "Common\IAggregateRoot.cs" (T @'
namespace {P}.Domain.Common;
 
public interface IAggregateRoot
{
}
'@
)

# ---- ValueObject ----
Write-File "Common\ValueObject.cs" (T @'
using System.Reflection;
 
namespace {P}.Domain.Common;
 
public abstract class ValueObject
{
    protected abstract IEnumerable<object> GetEqualityComponents();
 
    public override bool Equals(object? obj)
    {
        if (obj == null || obj.GetType() != GetType())
            return false;
 
        var other = (ValueObject)obj;
        return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
    }
 
    public override int GetHashCode()
    {
        return GetEqualityComponents()
            .Select(x => x != null ? x.GetHashCode() : 0)
            .Aggregate((x, y) => x ^ y);
    }
 
    public static bool operator ==(ValueObject? left, ValueObject? right)
    {
        if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
            return false;
        return ReferenceEquals(left, null) || left.Equals(right);
    }
 
    public static bool operator !=(ValueObject? left, ValueObject? right)
    {
        return !(left == right);
    }
}
'@
)

# ---- BaseEntity ----
Write-File "Common\BaseEntity.cs" (T @'
namespace {P}.Domain.Common;
 
public abstract class BaseEntity
{
    public int Id { get; protected set; }
    public DateTime CreatedAt { get; protected set; } = DateTime.UtcNow;
    public DateTime UpdatedAt { get; protected set; } = DateTime.UtcNow;
    public bool IsDeleted { get; protected set; } = false;
    public DateTime? DeletedAt { get; protected set; }
 
    public virtual void Delete()
    {
        if (!IsDeleted)
        {
            IsDeleted = true;
            DeletedAt = DateTime.UtcNow;
            UpdatedAt = DateTime.UtcNow;
        }
    }
}
'@
)

# ---- PagedQuery ----
Write-File "Common\PagedQuery.cs" (T @'
namespace {P}.Domain.Common;
 
public class PagedQuery
{
    public int Page { get; set; } = 1;
    public int Size { get; set; } = 20;
    public int Skip => (Page - 1) * Size;
}
'@
)

# ---- PagedResult<T> ----
Write-File "Common\PagedResult.cs" (T @'
namespace {P}.Domain.Common;
 
public class PagedResult<T>
{
    public IEnumerable<T> Items { get; set; } = Enumerable.Empty<T>();
    public int TotalCount { get; set; }
    public int Page { get; set; }
    public int Size { get; set; }
    public int TotalPages => (int)Math.Ceiling((double)TotalCount / Size);
    public bool HasNextPage => Page < TotalPages;
    public bool HasPreviousPage => Page > 1;
}
'@
)

# ---- Email Value Object ----
Write-File "ValueObjects\Email.cs" (T @'
using System.Text.RegularExpressions;
using {P}.Domain.Common;
 
namespace {P}.Domain.ValueObjects;
 
public class Email : ValueObject
{
    private static readonly Regex EmailRegex = new(
        @"^[^@\s]+@[^@\s]+\.[^@\s]+$",
        RegexOptions.Compiled | RegexOptions.IgnoreCase);
 
    public string Value { get; }
 
    protected Email()
    {
        Value = string.Empty;
    }
 
    public Email(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("Email address cannot be empty.", nameof(value));
 
        if (!EmailRegex.IsMatch(value))
            throw new ArgumentException("Invalid email format.", nameof(value));
 
        Value = value.Trim();
    }
 
    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Value.ToLowerInvariant();
    }
 
    public override string ToString() => Value;
}
'@
)

# ---- IRepository<T> ----
Write-File "Interfaces\IRepository.cs" (T @'
using {P}.Domain.Common;
 
namespace {P}.Domain.Interfaces;
 
public interface IRepository<T> where T : class
{
    Task<T?> GetByIdAsync(int id);
    Task<IEnumerable<T>> GetAllAsync();
    Task<PagedResult<T>> GetPagedAsync(PagedQuery query);
    Task<int> AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(int id);
}
'@
)

# ---- IUnitOfWork ----
Write-File "Interfaces\IUnitOfWork.cs" (T @'
namespace {P}.Domain.Interfaces;
 
public interface IUnitOfWork : IDisposable
{
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
    Task BeginTransactionAsync();
    Task CommitTransactionAsync();
    Task RollbackTransactionAsync();
}
'@
)

# ---- Dinamik Entity ve Repository Üretimi ----
foreach ($e in $Entities) {
    if ($e -eq "User") {
        Write-File "Entities\User.cs" (T @'
using {P}.Domain.Common;
using {P}.Domain.ValueObjects;
 
namespace {P}.Domain.Entities;
 
public class User : BaseEntity, IAggregateRoot
{
    public string Username { get; private set; } = string.Empty;
    public string PasswordHash { get; private set; } = string.Empty;
    public Email Email { get; private set; } = null!;
    public bool IsActive { get; private set; } = true;
    public string Role { get; private set; } = "User";
 
    protected User()
    {
    }
 
    public User(string username, string passwordHash, Email email, string role = "User")
    {
        if (string.IsNullOrWhiteSpace(username))
            throw new ArgumentException("Username cannot be empty.", nameof(username));
        if (string.IsNullOrWhiteSpace(passwordHash))
            throw new ArgumentException("Password hash cannot be empty.", nameof(passwordHash));
         
        Username = username.Trim();
        PasswordHash = passwordHash;
        Email = email ?? throw new ArgumentNullException(nameof(email));
        Role = string.IsNullOrWhiteSpace(role) ? "User" : role;
        IsActive = true;
        CreatedAt = DateTime.UtcNow;
        UpdatedAt = DateTime.UtcNow;
    }
 
    public void UpdateRole(string role)
    {
        if (string.IsNullOrWhiteSpace(role))
            throw new ArgumentException("Role cannot be empty.", nameof(role));
         
        Role = role;
        UpdatedAt = DateTime.UtcNow;
    }
 
    public void ChangeEmail(Email email)
    {
        Email = email ?? throw new ArgumentNullException(nameof(email));
        UpdatedAt = DateTime.UtcNow;
    }
 
    public void Activate()
    {
        IsActive = true;
        UpdatedAt = DateTime.UtcNow;
    }
 
    public void Deactivate()
    {
        IsActive = false;
        UpdatedAt = DateTime.UtcNow;
    }
}
'@
)

        Write-File "Interfaces\IUserRepository.cs" (T @'
using {P}.Domain.Entities;
 
namespace {P}.Domain.Interfaces;
 
public interface IUserRepository
{
    Task<User?> GetByUsernameAsync(string username);
    Task<int> AddAsync(User user);
}
'@
)
    }
    else {
        # Jenerik Entity (Rich Domain Model)
        $extraFields = ""
        $extraCtorParams = ""
        $ctorAssignments = ""
        
        if ($e -eq "Product") {
            $extraFields = "`n public decimal Price { get; private set; }"
            $extraCtorParams = ", decimal price"
            $ctorAssignments = "`n if (price < 0) throw new ArgumentException(`"Price cannot be negative.`", nameof(price));`n Price = price;"
        }
        
        $entityTemplate = @"
using {P}.Domain.Common;
 
namespace {P}.Domain.Entities;
 
public class {E} : BaseEntity
{
    public string Name { get; private set; } = string.Empty;$extraFields
 
    protected {E}()
    {
    }
 
    public {E}(string name$extraCtorParams)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new ArgumentException("Name cannot be empty.", nameof(name));
         
        Name = name.Trim();$ctorAssignments
        CreatedAt = DateTime.UtcNow;
        UpdatedAt = DateTime.UtcNow;
    }
 
    public void UpdateDetails(string name$extraCtorParams)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new ArgumentException("Name cannot be empty.", nameof(name));
         
        Name = name.Trim();$ctorAssignments
        UpdatedAt = DateTime.UtcNow;
    }
}
"@

        Write-File "Entities\$e.cs" (T $entityTemplate $e)

        # Jenerik Repository Arayüzü
        Write-File "Interfaces\I$e`Repository.cs" (T @'
using {P}.Domain.Entities;
 
namespace {P}.Domain.Interfaces;
 
public interface I{E}Repository : IRepository<{E}>
{
}
'@
 $e)
    }
}