// ============================================================
// CONTRACTS: {{pascalCase FEATURE_NAME}} — Vertical Slice Architecture
// Generated by MORPH Framework | Date: {{DATE}}
// Pattern: KanaiyaKatarmal/VerticalSliceArchitectureTemplate
// Ref: framework/standards/architecture/vertical-slice/vertical-slice.md
// ============================================================
//
// INSTRUÇÕES DE USO:
// 1. Copie cada seção para o arquivo correspondente no projeto
// 2. Substitua {{pascalCase FEATURE_NAME}} pelo nome da entidade (ex: Product)
// 3. Substitua {{NAMESPACE}} pelo namespace raiz do projeto (ex: MyApp)
// 4. Substitua {{ROUTE}} pela rota base (ex: /api/products)
// 5. Adicione os campos reais da entidade (marcados com TODO)
// ============================================================

// ============================================================
// ABSTRACTIONS (apenas se ainda não existir no projeto)
// Caminho: Abstractions/IHandler.cs
// ============================================================

namespace {{NAMESPACE}}.Abstractions
{
    public interface IHandler<in TRequest, TResponse>
    {
        Task<TResponse> HandleAsync(TRequest command, CancellationToken cancellationToken);
    }
}

// ============================================================
// Caminho: Abstractions/IApiEndpoint.cs
// ============================================================

namespace {{NAMESPACE}}.Abstractions
{
    public interface IApiEndpoint
    {
        void MapEndpoint(IEndpointRouteBuilder app);
    }
}

// ============================================================
// Caminho: Abstractions/Errors/Error.cs
// ============================================================

namespace {{NAMESPACE}}.Abstractions.Errors;

public record Error(string Code, string? Description = default, ErrorType Type = ErrorType.Failure)
{
    public static readonly Error None = new(string.Empty);
    public static readonly Error Null = new("Error.NullValue", "The specified result value is null.");

    public static implicit operator Result(Error error) => Result.Failure(error);

    public static Error Failure(string code, string description) =>
        new(code, description, ErrorType.Failure);
    public static Error Unexpected(string code, string description) =>
        new(code, description, ErrorType.Unexpected);
    public static Error Validation(string code, string description) =>
        new(code, description, ErrorType.Validation);
    public static Error Conflict(string code, string description) =>
        new(code, description, ErrorType.Conflict);
    public static Error NotFound(string code, string description) =>
        new(code, description, ErrorType.NotFound);
    public static Error Unauthorized(string code, string description) =>
        new(code, description, ErrorType.Unauthorized);
    public static Error Forbidden(string code, string description) =>
        new(code, description, ErrorType.Forbidden);
}

public enum ErrorType
{
    Failure, Unexpected, Validation, Conflict, NotFound, Unauthorized, Forbidden, Custom
}

// ============================================================
// Caminho: Abstractions/Errors/ValidationError.cs
// ============================================================

namespace {{NAMESPACE}}.Abstractions.Errors;

public sealed record ValidationError : Error
{
    public ValidationError(Error[] errors)
        : base("Validation.General", "One or more validation errors occurred", ErrorType.Validation)
    {
        Errors = errors;
    }
    public Error[] Errors { get; }
    public static ValidationError FromResults(IEnumerable<Result> results) =>
        new(results.Where(r => r.IsFailure).Select(r => r.Error).ToArray());
}

// ============================================================
// Caminho: Abstractions/Result.cs
// ============================================================

using {{NAMESPACE}}.Abstractions.Errors;
namespace {{NAMESPACE}}.Abstractions;

public class Result
{
    protected internal Result(bool isSuccess, Error error)
    {
        if (isSuccess && error != Error.None || !isSuccess && error == Error.None)
            throw new ArgumentException("Invalid error", nameof(error));
        IsSuccess = isSuccess;
        Error = error;
    }
    public bool IsSuccess { get; }
    public bool IsFailure => !IsSuccess;
    public Error Error { get; }

    public static Result Success() => new(true, Error.None);
    public static Result<TValue> Success<TValue>(TValue value) => new(value, true, Error.None);
    public static Result Failure(Error error) => new(false, error);
    public static Result<TValue> Failure<TValue>(Error error) => new(default, false, error);
    public static Result<TValue> Create<TValue>(TValue? value) =>
        value is not null ? Success(value) : Failure<TValue>(Error.Null);
}

public class Result<TValue> : Result
{
    private readonly TValue? _value;
    protected internal Result(TValue? value, bool isSuccess, Error error) : base(isSuccess, error)
        => _value = value;
    public TValue Value => IsSuccess
        ? _value!
        : throw new InvalidOperationException("The value of a failure result can not be accessed.");
    public static implicit operator Result<TValue>(TValue? value) => Create(value);
    public static implicit operator Result<TValue>(Error error) => Failure<TValue>(error);
}

// ============================================================
// ENTITY
// Caminho: Entities/{{pascalCase FEATURE_NAME}}.cs
// ============================================================

namespace {{NAMESPACE}}.Entities;

public sealed class {{pascalCase FEATURE_NAME}}
{
    public Guid Id { get; set; }

    // TODO: adicionar campos da entidade com tipos C# corretos
    // Exemplo:
    // public required string Name { get; set; }
    // public decimal Price { get; set; }
    // public int Quantity { get; set; }
    // public DateTime CreatedAt { get; set; }
}

// ============================================================
// FEATURE ERRORS (compartilhado por todos os slices da feature)
// Caminho: Features/{{pascalCase FEATURE_NAME}}Feature/{{pascalCase FEATURE_NAME}}Errors.cs
// ============================================================

using {{NAMESPACE}}.Abstractions.Errors;
namespace {{NAMESPACE}}.Features.{{pascalCase FEATURE_NAME}}Feature;

public static class {{pascalCase FEATURE_NAME}}Errors
{
    public static Error NotFound(Guid id) =>
        Error.NotFound("{{pascalCase FEATURE_NAME}}s.NotFound",
            $"The {{pascalCase FEATURE_NAME}} with Id '{id}' was not found");

    // TODO: adicionar outros tipos de erro específicos da feature
    // public static Error AlreadyExists(string name) =>
    //     Error.Conflict("{{pascalCase FEATURE_NAME}}s.AlreadyExists",
    //         $"A {{pascalCase FEATURE_NAME}} with name '{name}' already exists");
}

// ============================================================
// SLICE: Create{{pascalCase FEATURE_NAME}}
// Caminho: Features/{{pascalCase FEATURE_NAME}}Feature/Create{{pascalCase FEATURE_NAME}}/
// ============================================================

// --- Create{{pascalCase FEATURE_NAME}}Handler.cs ---
namespace {{NAMESPACE}}.Features.{{pascalCase FEATURE_NAME}}Feature.Create{{pascalCase FEATURE_NAME}};

using {{NAMESPACE}}.Abstractions;
using {{NAMESPACE}}.Database;
using {{NAMESPACE}}.Entities;

// TODO: adicionar todos os campos do request (campos obrigatórios para criação)
public sealed record Create{{pascalCase FEATURE_NAME}}Request(/* string Name, decimal Price, ... */);

// TODO: adicionar todos os campos do response (campos retornados após criação)
public sealed record Create{{pascalCase FEATURE_NAME}}Response(Guid Id /* , string Name, ... */);

public sealed class Create{{pascalCase FEATURE_NAME}}Handler(
    ApplicationDbContext _db) : IHandler<Create{{pascalCase FEATURE_NAME}}Request, Result<Create{{pascalCase FEATURE_NAME}}Response>>
{
    public async Task<Result<Create{{pascalCase FEATURE_NAME}}Response>> HandleAsync(
        Create{{pascalCase FEATURE_NAME}}Request command, CancellationToken cancellationToken)
    {
        var entity = new {{pascalCase FEATURE_NAME}}
        {
            Id = Guid.CreateVersion7(),
            // TODO: mapear campos do command para a entidade
            // Name = command.Name,
            // Price = command.Price,
        };

        _db.Set<{{pascalCase FEATURE_NAME}}>().Add(entity);
        await _db.SaveChangesAsync(cancellationToken);

        return Result.Success(new Create{{pascalCase FEATURE_NAME}}Response(entity.Id /* , entity.Name, ... */));
    }
}

// --- Create{{pascalCase FEATURE_NAME}}Validator.cs ---
namespace {{NAMESPACE}}.Features.{{pascalCase FEATURE_NAME}}Feature.Create{{pascalCase FEATURE_NAME}};

using FluentValidation;

public class Create{{pascalCase FEATURE_NAME}}Validator : AbstractValidator<Create{{pascalCase FEATURE_NAME}}Request>
{
    public Create{{pascalCase FEATURE_NAME}}Validator()
    {
        // TODO: adicionar regras de validação para cada campo
        // RuleFor(c => c.Name)
        //     .NotEmpty().WithMessage("Name is required")
        //     .MaximumLength(200).WithMessage("Name must not exceed 200 characters");
        // RuleFor(c => c.Price)
        //     .GreaterThan(0).WithMessage("Price must be greater than 0");
    }
}

// --- Create{{pascalCase FEATURE_NAME}}Endpoint.cs ---
namespace {{NAMESPACE}}.Features.{{pascalCase FEATURE_NAME}}Feature.Create{{pascalCase FEATURE_NAME}};

using {{NAMESPACE}}.Abstractions;
using {{NAMESPACE}}.Extensions;

internal sealed class Create{{pascalCase FEATURE_NAME}}Endpoint : IApiEndpoint
{
    public void MapEndpoint(IEndpointRouteBuilder app)
    {
        app.MapPost("{{ROUTE}}", async (
            IHandler<Create{{pascalCase FEATURE_NAME}}Request, Result<Create{{pascalCase FEATURE_NAME}}Response>> handler,
            Create{{pascalCase FEATURE_NAME}}Request command,
            CancellationToken cancellationToken) =>
        {
            var result = await handler.HandleAsync(command, cancellationToken);
            return result.Match(
                onSuccess: () => Results.Ok(result.Value),
                onFailure: error => Results.BadRequest(error));
        })
        // TODO: substituir "{{pascalCase FEATURE_NAME}}s" pelo tag correto em ApiTags
        .WithTags("{{pascalCase FEATURE_NAME}}s")
        .Produces<Create{{pascalCase FEATURE_NAME}}Response>(StatusCodes.Status200OK)
        .Produces(StatusCodes.Status400BadRequest);
    }
}

// ============================================================
// TODO: adicionar slices restantes (copiar padrão do Create acima)
// ============================================================
// GetAll{{pascalCase FEATURE_NAME}}s — sem validator (sem parâmetros)
//   Request: sealed record GetAll{{pascalCase FEATURE_NAME}}sRequest;
//   Response: sealed record GetAll{{pascalCase FEATURE_NAME}}sResponse(IEnumerable<{{pascalCase FEATURE_NAME}}Dto> Items);
//   DTO: sealed record {{pascalCase FEATURE_NAME}}Dto(Guid Id, /* campos */);
//   Endpoint: app.MapGet("{{ROUTE}}", ...)
//
// Get{{pascalCase FEATURE_NAME}}ById — com validator (Id obrigatório)
//   Request: sealed record Get{{pascalCase FEATURE_NAME}}ByIdRequest(Guid Id);
//   Response: sealed record Get{{pascalCase FEATURE_NAME}}ByIdResponse(Guid Id, /* campos */);
//   Endpoint: app.MapGet("{{ROUTE}}/{id:guid}", ...)
//   OnFailure: Results.NotFound(error)
//
// Update{{pascalCase FEATURE_NAME}} — com validator
//   Request: sealed record Update{{pascalCase FEATURE_NAME}}Request(Guid Id, /* campos nullable */);
//   Response: sealed record Update{{pascalCase FEATURE_NAME}}Response(Guid Id, /* campos */);
//   Endpoint: app.MapPut("{{ROUTE}}/{id:guid}", ...)
//   Checar NotFound antes de atualizar
//
// Delete{{pascalCase FEATURE_NAME}} — com validator
//   Request: sealed record Delete{{pascalCase FEATURE_NAME}}Request(Guid Id);
//   Response: sealed record Delete{{pascalCase FEATURE_NAME}}Response(Guid Id);
//   Endpoint: app.MapDelete("{{ROUTE}}/{id:guid}", ...)
//   Checar NotFound antes de deletar
