modules/ai-guide/Write-AiGuide-Application.ps1

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

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

$guideDir = Join-Path $OutputPath "ai-guide"
function Write-Guide($fileName, $content) {
    Write-CoreFile (Join-Path $guideDir $fileName) $content
}

$appGuideContent = @'
# Application Layer AI Developer Guide
 
You are an AI assistant helping developers build the Application Layer of the {P} solution.
This layer contains the application use cases, CQRS commands/queries, MediatR handlers, DTOs, FluentValidation validators, and AutoMapper mappings.
 
---
 
## Strict Application Layer Rules
 
1. **Clean Dependencies:** The Application project depends only on the Domain project. Do not reference WebApi or Persistence projects.
2. **Feature Folder Scoping:** Group CQRS logic by feature (e.g. `Features/Products/Commands/AddProduct/AddProductCommand.cs`).
3. **Single-File CQRS Handlers:** Put the request command/query record, the handler class, and the validator (if applicable) inside a single `.cs` file.
4. **Input Validation:** Use FluentValidation. Place validation rules (`Validator`) in the same file as the command it validates. Do not use Data Annotations.
5. **No Controller Logic:** Controllers only pass queries/commands to MediatR. Do not write business rules in WebApi controllers.
6. **Pagination Rule:** Every entity list endpoint MUST have a `GetPaged` query. Use `GetPagedAsync` for public-facing list endpoints. `GetAll` is for internal use only.
 
---
 
## How to Add a Feature
 
When adding a new feature (e.g., `Category`), implement the following files in `{P}-backend/src/{P}.Application`:
 
### 1. DTO (Data Transfer Object)
Define DTOs as C# records inside the feature folder:
```csharp
// {P}-backend/src/{P}.Application/Features/Categories/CategoryDto.cs
namespace {P}.Application.Features.Categories;
 
public record CategoryDto(int Id, string Name, DateTime CreatedAt);
```
 
### 2. AutoMapper Mapping Profile
Create profile inside a `Mappings/` subfolder within the feature folder:
```csharp
// {P}-backend/src/{P}.Application/Features/Categories/Mappings/CategoryProfile.cs
using AutoMapper;
using {P}.Domain.Entities;
 
namespace {P}.Application.Features.Categories.Mappings;
 
public class CategoryProfile : Profile
{
    public CategoryProfile()
    {
        CreateMap<Category, CategoryDto>();
    }
}
```
 
### 3. Paged Query (Required for list endpoints)
```csharp
// {P}-backend/src/{P}.Application/Features/Categories/Queries/GetPagedCategories/GetPagedCategoriesQuery.cs
using AutoMapper;
using MediatR;
using {P}.Domain.Common;
using {P}.Domain.Interfaces;
 
namespace {P}.Application.Features.Categories.Queries.GetPagedCategories;
 
public record GetPagedCategoriesQuery(int Page = 1, int Size = 20) : IRequest<PagedResult<CategoryDto>>;
 
public class GetPagedCategoriesQueryHandler : IRequestHandler<GetPagedCategoriesQuery, PagedResult<CategoryDto>>
{
    private readonly ICategoryRepository _repo;
    private readonly IMapper _mapper;
    public GetPagedCategoriesQueryHandler(ICategoryRepository repo, IMapper mapper) { _repo = repo; _mapper = mapper; }
 
    public async Task<PagedResult<CategoryDto>> Handle(GetPagedCategoriesQuery request, CancellationToken ct)
    {
        var pagedResult = await _repo.GetPagedAsync(new PagedQuery { Page = request.Page, Size = request.Size });
        return new PagedResult<CategoryDto>
        {
            Items = _mapper.Map<IEnumerable<CategoryDto>>(pagedResult.Items),
            TotalCount = pagedResult.TotalCount,
            Page = pagedResult.Page,
            Size = pagedResult.Size
        };
    }
}
```
 
### 4. Command, Handler, & Validator (Single File)
Define the Command record, the MediatR handler, and the FluentValidation rules in the same file:
```csharp
// {P}-backend/src/{P}.Application/Features/Categories/Commands/AddCategory/AddCategoryCommand.cs
using FluentValidation;
using MediatR;
using {P}.Domain.Entities;
using {P}.Domain.Interfaces;
 
namespace {P}.Application.Features.Categories.Commands.AddCategory;
 
public record AddCategoryCommand(string Name) : IRequest<int>;
 
public class AddCategoryCommandValidator : AbstractValidator<AddCategoryCommand>
{
    public AddCategoryCommandValidator()
    {
        RuleFor(x => x.Name)
            .NotEmpty().WithMessage("Category name cannot be empty.")
            .MaximumLength(200).WithMessage("Category name cannot exceed 200 characters.");
    }
}
 
public class AddCategoryCommandHandler : IRequestHandler<AddCategoryCommand, int>
{
    private readonly ICategoryRepository _repository;
 
    public AddCategoryCommandHandler(ICategoryRepository repository)
    {
        _repository = repository;
    }
 
    public async Task<int> Handle(AddCategoryCommand request, CancellationToken ct)
    {
        var category = new Category(request.Name);
        return await _repository.AddAsync(category);
    }
}
```
'@
.Replace("{P}", $ProjectName)

Write-Guide "application-guide.md" $appGuideContent