modules/backend/Write-ApplicationLayer.ps1

param(
    [string]$ProjectName,
    [string]$OrmMode,
    [string]$OutputPath,
    [string[]]$Entities,
    [string]$Language = 'tr'
)

. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Utils.ps1")
. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Get-Locale.ps1")
$L = Get-Locale -Language $Language

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

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

# ===== ApiResponse<T> Wrapper =====
Write-File "Common\ApiResponse.cs" (T @'
namespace {P}.Application.Common;

public class ApiResponse<T>
{
    public bool Success { get; set; }
    public string? Message { get; set; }
    public T? Data { get; set; }

    public static ApiResponse<T> Ok(T data, string? message = null) =>
        new() { Success = true, Data = data, Message = message };

    public static ApiResponse<T> Fail(string message) =>
        new() { Success = false, Message = message };
}
'@
)

# ===== Common Interfaces =====
Write-File "Interfaces\ITokenService.cs" (T @'
namespace {P}.Application.Interfaces;
public interface ITokenService { string GenerateToken(int userId, string username, string role = "User"); }
'@
)

Write-File "Interfaces\IPasswordHasher.cs" (T @'
namespace {P}.Application.Interfaces;
public interface IPasswordHasher { string Hash(string password); bool Verify(string password, string hash); }
'@
)

# ===== Validation & Pipeline =====
Write-File "Behaviors\ValidationBehavior.cs" (T @'
using FluentValidation;
using MediatR;
namespace {P}.Application.Behaviors;
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;
    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators) => _validators = validators;
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
    {
        if (!_validators.Any()) return await next();
        var context = new ValidationContext<TRequest>(request);
        var failures = _validators.Select(v => v.Validate(context)).SelectMany(r => r.Errors).Where(e => e != null).ToList();
        if (failures.Count != 0) throw new ValidationException(failures);
        return await next();
    }
}
'@
)

# ===== Entity Scaffolding =====
foreach ($e in $Entities) {
    if ($e -eq "User") {
        # Auth feature — Command + Response + Handler in one file
        Write-File "Features\Auth\Commands\Login\LoginCommand.cs" (T @'
using MediatR;
using {P}.Application.Interfaces;
using {P}.Domain.Interfaces;

namespace {P}.Application.Features.Auth.Commands.Login;

public record LoginCommand(string Username, string Password) : IRequest<LoginResponse>;
public record LoginResponse(string Token, string Username);

public class LoginCommandHandler : IRequestHandler<LoginCommand, LoginResponse>
{
    private readonly IUserRepository _userRepo;
    private readonly ITokenService _tokenService;
    private readonly IPasswordHasher _hasher;

    public LoginCommandHandler(IUserRepository userRepo, ITokenService tokenService, IPasswordHasher hasher)
    {
        _userRepo = userRepo; _tokenService = tokenService; _hasher = hasher;
    }

    public async Task<LoginResponse> Handle(LoginCommand request, CancellationToken ct)
    {
        var user = await _userRepo.GetByUsernameAsync(request.Username)
            ?? throw new UnauthorizedAccessException("$($L.backendInvalidCreds)");
        if (!_hasher.Verify(request.Password, user.PasswordHash))
            throw new UnauthorizedAccessException("$($L.backendInvalidCreds)");
        return new LoginResponse(_tokenService.GenerateToken(user.Id, user.Username, user.Role), user.Username);
    }
}
'@
)
    } else {
        $pE = Get-Plural $e

        # Feature-root DTO
        $dtoExtra = if ($e -eq "Product") { ", decimal Price" } else { "" }
        Write-File "Features\$pE\$e`Dto.cs" (T @"
namespace {P}.Application.Features.{PE};
public record {E}Dto(int Id, string Name, DateTime CreatedAt$dtoExtra);
"@
 $e $pE)

        # Feature-based AutoMapper profile
        Write-File "Features\$pE\Mappings\$e`Profile.cs" (T @'
using AutoMapper;
using {P}.Application.Features.{PE};
using {P}.Domain.Entities;

namespace {P}.Application.Features.{PE}.Mappings;
public class {E}Profile : Profile { public {E}Profile() { CreateMap<{E}, {E}Dto>(); } }
'@
 $e $pE)

        # Add Command — Command + Handler in one file
        $cmdExtra = if ($e -eq "Product") { ", decimal Price" } else { "" }
        $entitySet = if ($e -eq "Product") { "var entity = new {E} { Name = request.Name, Price = request.Price };" } else { "var entity = new {E} { Name = request.Name };" }
        Write-File "Features\$pE\Commands\Add$e\Add$e`Command.cs" (T @"
using MediatR;
using {P}.Domain.Entities;
using {P}.Domain.Interfaces;

namespace {P}.Application.Features.{PE}.Commands.Add{E};

public record Add{E}Command(string Name$cmdExtra) : IRequest<int>;

public class Add{E}CommandHandler : IRequestHandler<Add{E}Command, int>
{
    private readonly I{E}Repository _repo;
    public Add{E}CommandHandler(I{E}Repository repo) => _repo = repo;

    public async Task<int> Handle(Add{E}Command request, CancellationToken ct)
    {
        $entitySet
        return await _repo.AddAsync(entity);
    }
}
"@
 $e $pE)

        # GetAll Query — Query + Handler in one file
        Write-File "Features\$pE\Queries\GetAll$pE\GetAll$pE`Query.cs" (T @'
using AutoMapper;
using MediatR;
using {P}.Application.Features.{PE};
using {P}.Domain.Interfaces;

namespace {P}.Application.Features.{PE}.Queries.GetAll{PE};

public record GetAll{PE}Query : IRequest<IEnumerable<{E}Dto>>;

public class GetAll{PE}QueryHandler : IRequestHandler<GetAll{PE}Query, IEnumerable<{E}Dto>>
{
    private readonly I{E}Repository _repo;
    private readonly IMapper _mapper;
    public GetAll{PE}QueryHandler(I{E}Repository repo, IMapper mapper) { _repo = repo; _mapper = mapper; }

    public async Task<IEnumerable<{E}Dto>> Handle(GetAll{PE}Query request, CancellationToken ct)
    {
        var items = await _repo.GetAllAsync();
        return _mapper.Map<IEnumerable<{E}Dto>>(items);
    }
}
'@
 $e $pE)

        # GetPaged Query — Query + Handler in one file
        Write-File "Features\$pE\Queries\GetPaged$pE\GetPaged$pE`Query.cs" (T @'
using AutoMapper;
using MediatR;
using {P}.Application.Features.{PE};
using {P}.Domain.Common;
using {P}.Domain.Interfaces;

namespace {P}.Application.Features.{PE}.Queries.GetPaged{PE};

public record GetPaged{PE}Query(int Page = 1, int Size = 20) : IRequest<PagedResult<{E}Dto>>;

public class GetPaged{PE}QueryHandler : IRequestHandler<GetPaged{PE}Query, PagedResult<{E}Dto>>
{
    private readonly I{E}Repository _repo;
    private readonly IMapper _mapper;
    public GetPaged{PE}QueryHandler(I{E}Repository repo, IMapper mapper) { _repo = repo; _mapper = mapper; }

    public async Task<PagedResult<{E}Dto>> Handle(GetPaged{PE}Query request, CancellationToken ct)
    {
        var pagedResult = await _repo.GetPagedAsync(new PagedQuery { Page = request.Page, Size = request.Size });
        var dtos = _mapper.Map<IEnumerable<{E}Dto>>(pagedResult.Items);
        return new PagedResult<{E}Dto>
        {
            Items = dtos,
            TotalCount = pagedResult.TotalCount,
            Page = pagedResult.Page,
            Size = pagedResult.Size
        };
    }
}
'@
 $e $pE)
    }
}

# ===== ServiceRegistration =====
Write-File "ServiceRegistration\ApplicationServiceRegistration.cs" (T @'
using System.Reflection;
using FluentValidation;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using {P}.Application.Behaviors;
namespace {P}.Application.ServiceRegistration;
public static class ApplicationServiceRegistration
{
    public static IServiceCollection AddApplicationServices(this IServiceCollection services)
    {
        services.AddAutoMapper(typeof(ApplicationServiceRegistration).Assembly);
        services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
        services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
        return services;
    }
}
'@
)