<go_file>
<go_version> go1.24
<go_pkg> repositories

// infra/repositories/userRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// UserRepositoryImpl implements contracts.UserRepository using GORM.
type UserRepositoryImpl struct {
	db *gorm.DB
}

// NewUserRepository constructs a new UserRepositoryImpl.
func NewUserRepository(db *gorm.DB) *UserRepositoryImpl {
	return &UserRepositoryImpl{db: db}
}

func (r *UserRepositoryImpl) Create(dto dtos.UserDTO) (uuid.UUID, error) {
	entity := entities.User{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("user create: %w", err)
	}
	return entity.ID, nil
}

func (r *UserRepositoryImpl) FindByID(id uuid.UUID) (*dtos.UserDTO, error) {
	var entity entities.User
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("user not found: %s", id)
		}
		return nil, fmt.Errorf("user find: %w", err)
	}
	dto := dtos.UserDTO{} // map entity → DTO
	return &dto, nil
}

func (r *UserRepositoryImpl) FindByEmail(email string) (*dtos.UserDTO, error) {
	var entity entities.User
	if err := r.db.Where("email = ?", email).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("user with email=%s not found", email)
		}
		return nil, fmt.Errorf("user find by email: %w", err)
	}
	dto := dtos.UserDTO{}
	return &dto, nil
}

func (r *UserRepositoryImpl) List(page, limit int) ([]dtos.UserDTO, error) {
	var entities []entities.User
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("user list: %w", err)
	}
	dtos := make([]dtos.UserDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *UserRepositoryImpl) Update(id uuid.UUID, dto dtos.UserDTO) error {
	result := r.db.Model(&entities.User{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("user update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("user not found: %s", id)
	}
	return nil
}

func (r *UserRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.User{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("user delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("user not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> initializers

// initializers/database.go
// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN
// Observed in: Medical-App-Core/initializers/database.go
package initializers

import (
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// InitialDB opens a PostgreSQL connection using environment variables.
// Panics on failure — the app cannot run without a database.
func InitialDB() *gorm.DB {
	dsn := buildDSN()

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		log.Fatalf("database connection failed: %v", err)
	}

	sqlDB, err := db.DB()
	if err != nil {
		log.Fatalf("failed to get underlying sql.DB: %v", err)
	}
	sqlDB.SetMaxOpenConns(25)
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetConnMaxLifetime(5 * time.Minute)

	log.Println("database connected:", maskDSN(dsn))
	return db
}

func buildDSN() string {
	return fmt.Sprintf(
		"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
		getEnv("DB_HOST",     "localhost"),
		getEnv("DB_PORT",     "5432"),
		getEnv("DB_USER",     "postgres"),
		os.Getenv("DB_PASSWORD"),
		getEnv("DB_NAME",     "appdb"),
		getEnv("DB_SSLMODE",  "disable"),
		getEnv("DB_TIMEZONE", "UTC"),
	)
}

func getEnv(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

func maskDSN(dsn string) string {
	password := os.Getenv("DB_PASSWORD")
	if password == "" {
		return dsn
	}
	return strings.ReplaceAll(dsn, password, "*****")
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> initializers

// initializers/database.go
// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN
// Observed in: Medical-App-Core/initializers/database.go
package initializers

import (
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// InitialDB opens a PostgreSQL connection using environment variables.
// Panics on failure — the app cannot run without a database.
func InitialDB() *gorm.DB {
	dsn := buildDSN()

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		log.Fatalf("database connection failed: %v", err)
	}

	sqlDB, err := db.DB()
	if err != nil {
		log.Fatalf("failed to get underlying sql.DB: %v", err)
	}
	sqlDB.SetMaxOpenConns(25)
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetConnMaxLifetime(5 * time.Minute)

	log.Println("database connected:", maskDSN(dsn))
	return db
}

func buildDSN() string {
	return fmt.Sprintf(
		"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
		getEnv("DB_HOST",     "localhost"),
		getEnv("DB_PORT",     "5432"),
		getEnv("DB_USER",     "postgres"),
		os.Getenv("DB_PASSWORD"),
		getEnv("DB_NAME",     "appdb"),
		getEnv("DB_SSLMODE",  "disable"),
		getEnv("DB_TIMEZONE", "UTC"),
	)
}

func getEnv(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

func maskDSN(dsn string) string {
	password := os.Getenv("DB_PASSWORD")
	if password == "" {
		return dsn
	}
	return strings.ReplaceAll(dsn, password, "*****")
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> initializers

// initializers/database.go
// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN
// Observed in: Medical-App-Core/initializers/database.go
package initializers

import (
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// InitialDB opens a PostgreSQL connection using environment variables.
// Panics on failure — the app cannot run without a database.
func InitialDB() *gorm.DB {
	dsn := buildDSN()

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		log.Fatalf("database connection failed: %v", err)
	}

	sqlDB, err := db.DB()
	if err != nil {
		log.Fatalf("failed to get underlying sql.DB: %v", err)
	}
	sqlDB.SetMaxOpenConns(25)
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetConnMaxLifetime(5 * time.Minute)

	log.Println("database connected:", maskDSN(dsn))
	return db
}

func buildDSN() string {
	return fmt.Sprintf(
		"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
		getEnv("DB_HOST",     "localhost"),
		getEnv("DB_PORT",     "5432"),
		getEnv("DB_USER",     "postgres"),
		os.Getenv("DB_PASSWORD"),
		getEnv("DB_NAME",     "appdb"),
		getEnv("DB_SSLMODE",  "disable"),
		getEnv("DB_TIMEZONE", "UTC"),
	)
}

func getEnv(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

func maskDSN(dsn string) string {
	password := os.Getenv("DB_PASSWORD")
	if password == "" {
		return dsn
	}
	return strings.ReplaceAll(dsn, password, "*****")
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> initializers

// initializers/database.go
// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN
// Observed in: Medical-App-Core/initializers/database.go
package initializers

import (
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// InitialDB opens a PostgreSQL connection using environment variables.
// Panics on failure — the app cannot run without a database.
func InitialDB() *gorm.DB {
	dsn := buildDSN()

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		log.Fatalf("database connection failed: %v", err)
	}

	sqlDB, err := db.DB()
	if err != nil {
		log.Fatalf("failed to get underlying sql.DB: %v", err)
	}
	sqlDB.SetMaxOpenConns(25)
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetConnMaxLifetime(5 * time.Minute)

	log.Println("database connected:", maskDSN(dsn))
	return db
}

func buildDSN() string {
	return fmt.Sprintf(
		"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
		getEnv("DB_HOST",     "localhost"),
		getEnv("DB_PORT",     "5432"),
		getEnv("DB_USER",     "postgres"),
		os.Getenv("DB_PASSWORD"),
		getEnv("DB_NAME",     "appdb"),
		getEnv("DB_SSLMODE",  "disable"),
		getEnv("DB_TIMEZONE", "UTC"),
	)
}

func getEnv(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

func maskDSN(dsn string) string {
	password := os.Getenv("DB_PASSWORD")
	if password == "" {
		return dsn
	}
	return strings.ReplaceAll(dsn, password, "*****")
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> contracts

// services/contracts/patientServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// PatientServiceContract is the interface every Patient service implementation must satisfy.
type PatientServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input PatientInputDTO) (uuid.UUID, error)

	FindByCPF(cpf string) (*PatientDTO, error)

	List(page, limit int) ([]PatientDTO, error)

	Update(id uuid.UUID, dto PatientDTO) error

	Delete(id uuid.UUID) error
}

// PatientInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type PatientInputDTO struct {
	OrganizationName string `json:"organization_name"`
	Name    string `json:"name"`
	Contact string `json:"contact"`
	CPF     string `json:"cpf,omitempty"`
}

// PatientDTO is the canonical data transfer object passed between layers.
type PatientDTO struct {
	OrganizationID string `json:"organization_id"`
	Name    string `json:"name"`
	Contact string `json:"contact"`
	CPF     string `json:"cpf,omitempty"`
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> implementations

// services/implementations/patientService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// PatientService implements sc.PatientServiceContract.
type PatientService struct {
	repo         contracts.PatientRepository
	orgRepo      contracts.OrganizationRepository
}

// NewPatientService constructs a PatientService with all required repositories.
func NewPatientService(
	repo    contracts.PatientRepository,
	orgRepo contracts.OrganizationRepository,
) *PatientService {
	return &PatientService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *PatientService) CreateFromInput(input sc.PatientInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.PatientDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *PatientService) FindByCPF(cpf string) (*sc.PatientDTO, error) {
	result, err := s.repo.FindByCPF(cpf)
	if err != nil {
		return nil, fmt.Errorf("patient find by cpf: %w", err)
	}
	return result, nil
}

func (s *PatientService) List(page, limit int) ([]sc.PatientDTO, error) {
	return s.repo.List(page, limit)
}

func (s *PatientService) Update(id uuid.UUID, dto sc.PatientDTO) error {
	return s.repo.Update(id, dto)
}

func (s *PatientService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> contracts

// services/contracts/patientServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// PatientServiceContract is the interface every Patient service implementation must satisfy.
type PatientServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input PatientInputDTO) (uuid.UUID, error)

	FindByCPF(cpf string) (*PatientDTO, error)

	List(page, limit int) ([]PatientDTO, error)

	Update(id uuid.UUID, dto PatientDTO) error

	Delete(id uuid.UUID) error
}

// PatientInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type PatientInputDTO struct {
	OrganizationName string `json:"organization_name"`
	Name    string `json:"name"`
	Contact string `json:"contact"`
	CPF     string `json:"cpf,omitempty"`
}

// PatientDTO is the canonical data transfer object passed between layers.
type PatientDTO struct {
	OrganizationID string `json:"organization_id"`
	Name    string `json:"name"`
	Contact string `json:"contact"`
	CPF     string `json:"cpf,omitempty"`
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> implementations

// services/implementations/patientService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// PatientService implements sc.PatientServiceContract.
type PatientService struct {
	repo         contracts.PatientRepository
	orgRepo      contracts.OrganizationRepository
}

// NewPatientService constructs a PatientService with all required repositories.
func NewPatientService(
	repo    contracts.PatientRepository,
	orgRepo contracts.OrganizationRepository,
) *PatientService {
	return &PatientService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *PatientService) CreateFromInput(input sc.PatientInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.PatientDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *PatientService) FindByCPF(cpf string) (*sc.PatientDTO, error) {
	result, err := s.repo.FindByCPF(cpf)
	if err != nil {
		return nil, fmt.Errorf("patient find by cpf: %w", err)
	}
	return result, nil
}

func (s *PatientService) List(page, limit int) ([]sc.PatientDTO, error) {
	return s.repo.List(page, limit)
}

func (s *PatientService) Update(id uuid.UUID, dto sc.PatientDTO) error {
	return s.repo.Update(id, dto)
}

func (s *PatientService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> contracts

// services/contracts/patientServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// PatientServiceContract is the interface every Patient service implementation must satisfy.
type PatientServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input PatientInputDTO) (uuid.UUID, error)

	FindByCPF(cpf string) (*PatientDTO, error)

	List(page, limit int) ([]PatientDTO, error)

	Update(id uuid.UUID, dto PatientDTO) error

	Delete(id uuid.UUID) error
}

// PatientInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type PatientInputDTO struct {
	OrganizationName string `json:"organization_name"`
	Name    string `json:"name"`
	Contact string `json:"contact"`
	CPF     string `json:"cpf,omitempty"`
}

// PatientDTO is the canonical data transfer object passed between layers.
type PatientDTO struct {
	OrganizationID string `json:"organization_id"`
	Name    string `json:"name"`
	Contact string `json:"contact"`
	CPF     string `json:"cpf,omitempty"`
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> implementations

// services/implementations/patientService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// PatientService implements sc.PatientServiceContract.
type PatientService struct {
	repo         contracts.PatientRepository
	orgRepo      contracts.OrganizationRepository
}

// NewPatientService constructs a PatientService with all required repositories.
func NewPatientService(
	repo    contracts.PatientRepository,
	orgRepo contracts.OrganizationRepository,
) *PatientService {
	return &PatientService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *PatientService) CreateFromInput(input sc.PatientInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.PatientDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *PatientService) FindByCPF(cpf string) (*sc.PatientDTO, error) {
	result, err := s.repo.FindByCPF(cpf)
	if err != nil {
		return nil, fmt.Errorf("patient find by cpf: %w", err)
	}
	return result, nil
}

func (s *PatientService) List(page, limit int) ([]sc.PatientDTO, error) {
	return s.repo.List(page, limit)
}

func (s *PatientService) Update(id uuid.UUID, dto sc.PatientDTO) error {
	return s.repo.Update(id, dto)
}

func (s *PatientService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> contracts

// services/contracts/patientServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// PatientServiceContract is the interface every Patient service implementation must satisfy.
type PatientServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input PatientInputDTO) (uuid.UUID, error)

	FindByCPF(cpf string) (*PatientDTO, error)

	List(page, limit int) ([]PatientDTO, error)

	Update(id uuid.UUID, dto PatientDTO) error

	Delete(id uuid.UUID) error
}

// PatientInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type PatientInputDTO struct {
	OrganizationName string `json:"organization_name"`
	Name    string `json:"name"`
	Contact string `json:"contact"`
	CPF     string `json:"cpf,omitempty"`
}

// PatientDTO is the canonical data transfer object passed between layers.
type PatientDTO struct {
	OrganizationID string `json:"organization_id"`
	Name    string `json:"name"`
	Contact string `json:"contact"`
	CPF     string `json:"cpf,omitempty"`
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> implementations

// services/implementations/patientService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// PatientService implements sc.PatientServiceContract.
type PatientService struct {
	repo         contracts.PatientRepository
	orgRepo      contracts.OrganizationRepository
}

// NewPatientService constructs a PatientService with all required repositories.
func NewPatientService(
	repo    contracts.PatientRepository,
	orgRepo contracts.OrganizationRepository,
) *PatientService {
	return &PatientService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *PatientService) CreateFromInput(input sc.PatientInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.PatientDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *PatientService) FindByCPF(cpf string) (*sc.PatientDTO, error) {
	result, err := s.repo.FindByCPF(cpf)
	if err != nil {
		return nil, fmt.Errorf("patient find by cpf: %w", err)
	}
	return result, nil
}

func (s *PatientService) List(page, limit int) ([]sc.PatientDTO, error) {
	return s.repo.List(page, limit)
}

func (s *PatientService) Update(id uuid.UUID, dto sc.PatientDTO) error {
	return s.repo.Update(id, dto)
}

func (s *PatientService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> contracts

// services/contracts/doctorServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// DoctorServiceContract is the interface every Doctor service implementation must satisfy.
type DoctorServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input DoctorInputDTO) (uuid.UUID, error)

	FindByCRM(crm string) (*DoctorDTO, error)

	List(page, limit int) ([]DoctorDTO, error)

	Update(id uuid.UUID, dto DoctorDTO) error

	Delete(id uuid.UUID) error
}

// DoctorInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type DoctorInputDTO struct {
	OrganizationName string `json:"organization_name"`
	FullName   string `json:"full_name"`
	CPF        string `json:"cpf"`
	CRM        string `json:"crm"`
	Specialty  string `json:"specialty"`
}

// DoctorDTO is the canonical data transfer object passed between layers.
type DoctorDTO struct {
	OrganizationID string `json:"organization_id"`
	FullName   string `json:"full_name"`
	CRM        string `json:"crm"`
	Specialty  string `json:"specialty"`
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> implementations

// services/implementations/doctorService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// DoctorService implements sc.DoctorServiceContract.
type DoctorService struct {
	repo         contracts.DoctorRepository
	orgRepo      contracts.OrganizationRepository
}

// NewDoctorService constructs a DoctorService with all required repositories.
func NewDoctorService(
	repo    contracts.DoctorRepository,
	orgRepo contracts.OrganizationRepository,
) *DoctorService {
	return &DoctorService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *DoctorService) CreateFromInput(input sc.DoctorInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.DoctorDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *DoctorService) FindByCRM(crm string) (*sc.DoctorDTO, error) {
	result, err := s.repo.FindByCRM(crm)
	if err != nil {
		return nil, fmt.Errorf("doctor find by crm: %w", err)
	}
	return result, nil
}

func (s *DoctorService) List(page, limit int) ([]sc.DoctorDTO, error) {
	return s.repo.List(page, limit)
}

func (s *DoctorService) Update(id uuid.UUID, dto sc.DoctorDTO) error {
	return s.repo.Update(id, dto)
}

func (s *DoctorService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> contracts

// services/contracts/doctorServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// DoctorServiceContract is the interface every Doctor service implementation must satisfy.
type DoctorServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input DoctorInputDTO) (uuid.UUID, error)

	FindByCRM(crm string) (*DoctorDTO, error)

	List(page, limit int) ([]DoctorDTO, error)

	Update(id uuid.UUID, dto DoctorDTO) error

	Delete(id uuid.UUID) error
}

// DoctorInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type DoctorInputDTO struct {
	OrganizationName string `json:"organization_name"`
	FullName   string `json:"full_name"`
	CPF        string `json:"cpf"`
	CRM        string `json:"crm"`
	Specialty  string `json:"specialty"`
}

// DoctorDTO is the canonical data transfer object passed between layers.
type DoctorDTO struct {
	OrganizationID string `json:"organization_id"`
	FullName   string `json:"full_name"`
	CRM        string `json:"crm"`
	Specialty  string `json:"specialty"`
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> implementations

// services/implementations/doctorService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// DoctorService implements sc.DoctorServiceContract.
type DoctorService struct {
	repo         contracts.DoctorRepository
	orgRepo      contracts.OrganizationRepository
}

// NewDoctorService constructs a DoctorService with all required repositories.
func NewDoctorService(
	repo    contracts.DoctorRepository,
	orgRepo contracts.OrganizationRepository,
) *DoctorService {
	return &DoctorService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *DoctorService) CreateFromInput(input sc.DoctorInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.DoctorDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *DoctorService) FindByCRM(crm string) (*sc.DoctorDTO, error) {
	result, err := s.repo.FindByCRM(crm)
	if err != nil {
		return nil, fmt.Errorf("doctor find by crm: %w", err)
	}
	return result, nil
}

func (s *DoctorService) List(page, limit int) ([]sc.DoctorDTO, error) {
	return s.repo.List(page, limit)
}

func (s *DoctorService) Update(id uuid.UUID, dto sc.DoctorDTO) error {
	return s.repo.Update(id, dto)
}

func (s *DoctorService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> contracts

// services/contracts/doctorServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// DoctorServiceContract is the interface every Doctor service implementation must satisfy.
type DoctorServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input DoctorInputDTO) (uuid.UUID, error)

	FindByCRM(crm string) (*DoctorDTO, error)

	List(page, limit int) ([]DoctorDTO, error)

	Update(id uuid.UUID, dto DoctorDTO) error

	Delete(id uuid.UUID) error
}

// DoctorInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type DoctorInputDTO struct {
	OrganizationName string `json:"organization_name"`
	FullName   string `json:"full_name"`
	CPF        string `json:"cpf"`
	CRM        string `json:"crm"`
	Specialty  string `json:"specialty"`
}

// DoctorDTO is the canonical data transfer object passed between layers.
type DoctorDTO struct {
	OrganizationID string `json:"organization_id"`
	FullName   string `json:"full_name"`
	CRM        string `json:"crm"`
	Specialty  string `json:"specialty"`
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> implementations

// services/implementations/doctorService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// DoctorService implements sc.DoctorServiceContract.
type DoctorService struct {
	repo         contracts.DoctorRepository
	orgRepo      contracts.OrganizationRepository
}

// NewDoctorService constructs a DoctorService with all required repositories.
func NewDoctorService(
	repo    contracts.DoctorRepository,
	orgRepo contracts.OrganizationRepository,
) *DoctorService {
	return &DoctorService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *DoctorService) CreateFromInput(input sc.DoctorInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.DoctorDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *DoctorService) FindByCRM(crm string) (*sc.DoctorDTO, error) {
	result, err := s.repo.FindByCRM(crm)
	if err != nil {
		return nil, fmt.Errorf("doctor find by crm: %w", err)
	}
	return result, nil
}

func (s *DoctorService) List(page, limit int) ([]sc.DoctorDTO, error) {
	return s.repo.List(page, limit)
}

func (s *DoctorService) Update(id uuid.UUID, dto sc.DoctorDTO) error {
	return s.repo.Update(id, dto)
}

func (s *DoctorService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> contracts

// services/contracts/doctorServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// DoctorServiceContract is the interface every Doctor service implementation must satisfy.
type DoctorServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input DoctorInputDTO) (uuid.UUID, error)

	FindByCRM(crm string) (*DoctorDTO, error)

	List(page, limit int) ([]DoctorDTO, error)

	Update(id uuid.UUID, dto DoctorDTO) error

	Delete(id uuid.UUID) error
}

// DoctorInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type DoctorInputDTO struct {
	OrganizationName string `json:"organization_name"`
	FullName   string `json:"full_name"`
	CPF        string `json:"cpf"`
	CRM        string `json:"crm"`
	Specialty  string `json:"specialty"`
}

// DoctorDTO is the canonical data transfer object passed between layers.
type DoctorDTO struct {
	OrganizationID string `json:"organization_id"`
	FullName   string `json:"full_name"`
	CRM        string `json:"crm"`
	Specialty  string `json:"specialty"`
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> implementations

// services/implementations/doctorService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// DoctorService implements sc.DoctorServiceContract.
type DoctorService struct {
	repo         contracts.DoctorRepository
	orgRepo      contracts.OrganizationRepository
}

// NewDoctorService constructs a DoctorService with all required repositories.
func NewDoctorService(
	repo    contracts.DoctorRepository,
	orgRepo contracts.OrganizationRepository,
) *DoctorService {
	return &DoctorService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *DoctorService) CreateFromInput(input sc.DoctorInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.DoctorDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *DoctorService) FindByCRM(crm string) (*sc.DoctorDTO, error) {
	result, err := s.repo.FindByCRM(crm)
	if err != nil {
		return nil, fmt.Errorf("doctor find by crm: %w", err)
	}
	return result, nil
}

func (s *DoctorService) List(page, limit int) ([]sc.DoctorDTO, error) {
	return s.repo.List(page, limit)
}

func (s *DoctorService) Update(id uuid.UUID, dto sc.DoctorDTO) error {
	return s.repo.Update(id, dto)
}

func (s *DoctorService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> contracts

// services/contracts/appointmentServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// AppointmentServiceContract is the interface every Appointment service implementation must satisfy.
type AppointmentServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input AppointmentInputDTO) (uuid.UUID, error)

	FindByStatus(status string) (*AppointmentDTO, error)

	List(page, limit int) ([]AppointmentDTO, error)

	Update(id uuid.UUID, dto AppointmentDTO) error

	Delete(id uuid.UUID) error
}

// AppointmentInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type AppointmentInputDTO struct {
	OrganizationName string `json:"organization_name"`
	PatientName    string    `json:"patient_name"`
	DoctorFullName string    `json:"doctor_full_name"`
	Specialization string    `json:"specialization"`
	DateTime       time.Time `json:"date_time"`
}

// AppointmentDTO is the canonical data transfer object passed between layers.
type AppointmentDTO struct {
	OrganizationID string `json:"organization_id"`
	PatientID      string    `json:"patient_id"`
	DoctorID       string    `json:"doctor_id"`
	Specialization string    `json:"specialization"`
	DateTime       time.Time `json:"date_time"`
	Status         string    `json:"status"`
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> implementations

// services/implementations/appointmentService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// AppointmentService implements sc.AppointmentServiceContract.
type AppointmentService struct {
	repo         contracts.AppointmentRepository
	orgRepo      contracts.OrganizationRepository
}

// NewAppointmentService constructs a AppointmentService with all required repositories.
func NewAppointmentService(
	repo    contracts.AppointmentRepository,
	orgRepo contracts.OrganizationRepository,
) *AppointmentService {
	return &AppointmentService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *AppointmentService) CreateFromInput(input sc.AppointmentInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.AppointmentDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *AppointmentService) FindByStatus(status string) (*sc.AppointmentDTO, error) {
	result, err := s.repo.FindByStatus(status)
	if err != nil {
		return nil, fmt.Errorf("appointment find by status: %w", err)
	}
	return result, nil
}

func (s *AppointmentService) List(page, limit int) ([]sc.AppointmentDTO, error) {
	return s.repo.List(page, limit)
}

func (s *AppointmentService) Update(id uuid.UUID, dto sc.AppointmentDTO) error {
	return s.repo.Update(id, dto)
}

func (s *AppointmentService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> contracts

// services/contracts/appointmentServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// AppointmentServiceContract is the interface every Appointment service implementation must satisfy.
type AppointmentServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input AppointmentInputDTO) (uuid.UUID, error)

	FindByStatus(status string) (*AppointmentDTO, error)

	List(page, limit int) ([]AppointmentDTO, error)

	Update(id uuid.UUID, dto AppointmentDTO) error

	Delete(id uuid.UUID) error
}

// AppointmentInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type AppointmentInputDTO struct {
	OrganizationName string `json:"organization_name"`
	PatientName    string    `json:"patient_name"`
	DoctorFullName string    `json:"doctor_full_name"`
	Specialization string    `json:"specialization"`
	DateTime       time.Time `json:"date_time"`
}

// AppointmentDTO is the canonical data transfer object passed between layers.
type AppointmentDTO struct {
	OrganizationID string `json:"organization_id"`
	PatientID      string    `json:"patient_id"`
	DoctorID       string    `json:"doctor_id"`
	Specialization string    `json:"specialization"`
	DateTime       time.Time `json:"date_time"`
	Status         string    `json:"status"`
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> implementations

// services/implementations/appointmentService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// AppointmentService implements sc.AppointmentServiceContract.
type AppointmentService struct {
	repo         contracts.AppointmentRepository
	orgRepo      contracts.OrganizationRepository
}

// NewAppointmentService constructs a AppointmentService with all required repositories.
func NewAppointmentService(
	repo    contracts.AppointmentRepository,
	orgRepo contracts.OrganizationRepository,
) *AppointmentService {
	return &AppointmentService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *AppointmentService) CreateFromInput(input sc.AppointmentInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.AppointmentDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *AppointmentService) FindByStatus(status string) (*sc.AppointmentDTO, error) {
	result, err := s.repo.FindByStatus(status)
	if err != nil {
		return nil, fmt.Errorf("appointment find by status: %w", err)
	}
	return result, nil
}

func (s *AppointmentService) List(page, limit int) ([]sc.AppointmentDTO, error) {
	return s.repo.List(page, limit)
}

func (s *AppointmentService) Update(id uuid.UUID, dto sc.AppointmentDTO) error {
	return s.repo.Update(id, dto)
}

func (s *AppointmentService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> contracts

// services/contracts/appointmentServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// AppointmentServiceContract is the interface every Appointment service implementation must satisfy.
type AppointmentServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input AppointmentInputDTO) (uuid.UUID, error)

	FindByStatus(status string) (*AppointmentDTO, error)

	List(page, limit int) ([]AppointmentDTO, error)

	Update(id uuid.UUID, dto AppointmentDTO) error

	Delete(id uuid.UUID) error
}

// AppointmentInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type AppointmentInputDTO struct {
	OrganizationName string `json:"organization_name"`
	PatientName    string    `json:"patient_name"`
	DoctorFullName string    `json:"doctor_full_name"`
	Specialization string    `json:"specialization"`
	DateTime       time.Time `json:"date_time"`
}

// AppointmentDTO is the canonical data transfer object passed between layers.
type AppointmentDTO struct {
	OrganizationID string `json:"organization_id"`
	PatientID      string    `json:"patient_id"`
	DoctorID       string    `json:"doctor_id"`
	Specialization string    `json:"specialization"`
	DateTime       time.Time `json:"date_time"`
	Status         string    `json:"status"`
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> implementations

// services/implementations/appointmentService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// AppointmentService implements sc.AppointmentServiceContract.
type AppointmentService struct {
	repo         contracts.AppointmentRepository
	orgRepo      contracts.OrganizationRepository
}

// NewAppointmentService constructs a AppointmentService with all required repositories.
func NewAppointmentService(
	repo    contracts.AppointmentRepository,
	orgRepo contracts.OrganizationRepository,
) *AppointmentService {
	return &AppointmentService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *AppointmentService) CreateFromInput(input sc.AppointmentInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.AppointmentDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *AppointmentService) FindByStatus(status string) (*sc.AppointmentDTO, error) {
	result, err := s.repo.FindByStatus(status)
	if err != nil {
		return nil, fmt.Errorf("appointment find by status: %w", err)
	}
	return result, nil
}

func (s *AppointmentService) List(page, limit int) ([]sc.AppointmentDTO, error) {
	return s.repo.List(page, limit)
}

func (s *AppointmentService) Update(id uuid.UUID, dto sc.AppointmentDTO) error {
	return s.repo.Update(id, dto)
}

func (s *AppointmentService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> contracts

// services/contracts/appointmentServiceContract.go
// Pattern: service interface decouples controller from implementation
// Observed in: Medical-App-Core/services/contracts/
package contracts

import "github.com/google/uuid"

// AppointmentServiceContract is the interface every Appointment service implementation must satisfy.
type AppointmentServiceContract interface {
	// CreateFromInput resolves human-readable names in the InputDTO to UUIDs,
	// then delegates persistence to the repository.
	CreateFromInput(input AppointmentInputDTO) (uuid.UUID, error)

	FindByStatus(status string) (*AppointmentDTO, error)

	List(page, limit int) ([]AppointmentDTO, error)

	Update(id uuid.UUID, dto AppointmentDTO) error

	Delete(id uuid.UUID) error
}

// AppointmentInputDTO carries fields exactly as received from the HTTP layer.
// Name-based fields (OrganizationName, etc.) are resolved in the service.
type AppointmentInputDTO struct {
	OrganizationName string `json:"organization_name"`
	PatientName    string    `json:"patient_name"`
	DoctorFullName string    `json:"doctor_full_name"`
	Specialization string    `json:"specialization"`
	DateTime       time.Time `json:"date_time"`
}

// AppointmentDTO is the canonical data transfer object passed between layers.
type AppointmentDTO struct {
	OrganizationID string `json:"organization_id"`
	PatientID      string    `json:"patient_id"`
	DoctorID       string    `json:"doctor_id"`
	Specialization string    `json:"specialization"`
	DateTime       time.Time `json:"date_time"`
	Status         string    `json:"status"`
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> implementations

// services/implementations/appointmentService.go
// Pattern: service implementation resolves names→IDs, validates, then calls repo
// Observed in: Medical-App-Core/services/implementations/
package implementations

import (
	"fmt"

	"medical-sas-api/domain/repositories/contracts"
	sc "$module/services/contracts"
	"github.com/google/uuid"
	"golang.org/x/sync/errgroup"
)

// AppointmentService implements sc.AppointmentServiceContract.
type AppointmentService struct {
	repo         contracts.AppointmentRepository
	orgRepo      contracts.OrganizationRepository
}

// NewAppointmentService constructs a AppointmentService with all required repositories.
func NewAppointmentService(
	repo    contracts.AppointmentRepository,
	orgRepo contracts.OrganizationRepository,
) *AppointmentService {
	return &AppointmentService{repo: repo, orgRepo: orgRepo}
}

// CreateFromInput resolves organization name to UUID in parallel with any other
// lookups, validates the result, then inserts via the repository.
func (s *AppointmentService) CreateFromInput(input sc.AppointmentInputDTO) (uuid.UUID, error) {
	var (
		orgID uuid.UUID
	)

	// Parallel resolution of related entities
	g := &errgroup.Group{}
	g.Go(func() error {
		org, err := s.orgRepo.FindByName(input.OrganizationName)
		if err != nil {
			return fmt.Errorf("organization %q not found: %w", input.OrganizationName, err)
		}
		orgID = org.ID
		return nil
	})
	if err := g.Wait(); err != nil {
		return uuid.Nil, err
	}

	dto := sc.AppointmentDTO{
		OrganizationID: orgID.String(),
		// map remaining input fields
	}
	return s.repo.Create(dto)
}

func (s *AppointmentService) FindByStatus(status string) (*sc.AppointmentDTO, error) {
	result, err := s.repo.FindByStatus(status)
	if err != nil {
		return nil, fmt.Errorf("appointment find by status: %w", err)
	}
	return result, nil
}

func (s *AppointmentService) List(page, limit int) ([]sc.AppointmentDTO, error) {
	return s.repo.List(page, limit)
}

func (s *AppointmentService) Update(id uuid.UUID, dto sc.AppointmentDTO) error {
	return s.repo.Update(id, dto)
}

func (s *AppointmentService) Delete(id uuid.UUID) error {
	return s.repo.Delete(id)
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> initializers

// initializers/services.go
// Pattern: dependency injection container — wires repositories → services
// Observed in: Medical-App-Core/initializers/services.go
package initializers

import (
	"medical-sas-api/domain/repositories/contracts"
	implrepos "medical-sas-api/infra/repositories"
	sc "medical-sas-api/services/contracts"
	si "medical-sas-api/services/implementations"

	"gorm.io/gorm"
)

// Services is the application-wide DI container.
// It is constructed once in main() and passed to controllers/middleware.
type Services struct {
	AuthTokenService  sc.AuthTokenServiceContract
	OrganizationService sc.OrganizationServiceContract
	UserService       sc.UserServiceContract
	PatientService    sc.PatientServiceContract
	DoctorService     sc.DoctorServiceContract
	AppointmentService sc.AppointmentServiceContract
	ChargeService     sc.ChargeServiceContract
	MedicalRecordService sc.MedicalRecordServiceContract
}

// InitServices builds and wires all repositories and services.
func InitServices(db *gorm.DB) *Services {
	// Repositories
	orgRepo         := implrepos.NewOrganizationRepository(db)
	userRepo        := implrepos.NewUserRepository(db)
	patientRepo     := implrepos.NewPatientRepository(db)
	doctorRepo      := implrepos.NewDoctorRepository(db)
	appointmentRepo := implrepos.NewAppointmentRepository(db)

	// Services (inject repos as interface values)
	authSvc  := si.NewAuthTokenService()
	orgSvc   := si.NewOrganizationService(orgRepo)
	userSvc  := si.NewUserService(userRepo, orgRepo)
	patSvc   := si.NewPatientService(patientRepo, orgRepo)
	docSvc   := si.NewDoctorService(doctorRepo, orgRepo)
	apptSvc  := si.NewAppointmentService(
		appointmentRepo, orgRepo, patientRepo, userRepo, doctorRepo,
	)
	chargeSvc := si.NewChargeService(orgRepo)

	return &Services{
		AuthTokenService:      authSvc,
		OrganizationService:   orgSvc,
		UserService:           userSvc,
		PatientService:        patSvc,
		DoctorService:         docSvc,
		AppointmentService:    apptSvc,
		ChargeService:         chargeSvc,
	}
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> events

// services/events/doctors/doctorEventConsumer.go
// Pattern: RabbitMQ consumer with reconnect loop + JSON unmarshaling
// Observed in: Medical-App-Core/services/events/
package events

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/streadway/amqp"
)

// DoctorCreatedEvent is the message payload published when a doctor is registered.
type DoctorCreatedEvent struct {
	DoctorID   string    `json:"doctor_id"`
	FullName   string    `json:"full_name"`
	Specialty  string    `json:"specialty"`
	CreatedAt  time.Time `json:"created_at"`
}

// DoctorEventConsumer subscribes to the doctors.created queue.
type DoctorEventConsumer struct {
	conn    *amqp.Connection
	ch      *amqp.Channel
	queue   string
	handler func(DoctorCreatedEvent) error
}

// NewDoctorEventConsumer dials RabbitMQ and declares the queue.
func NewDoctorEventConsumer(handler func(DoctorCreatedEvent) error) (*DoctorEventConsumer, error) {
	url := os.Getenv("RABBITMQ_BASE_URL")
	if url == "" {
		return nil, fmt.Errorf("RABBITMQ_BASE_URL is not set")
	}

	conn, err := amqp.Dial(url)
	if err != nil {
		return nil, fmt.Errorf("rabbitmq dial: %w", err)
	}

	ch, err := conn.Channel()
	if err != nil {
		conn.Close()
		return nil, fmt.Errorf("rabbitmq channel: %w", err)
	}

	queue := "doctors.created"
	_, err = ch.QueueDeclare(queue, true, false, false, false, nil)
	if err != nil {
		ch.Close(); conn.Close()
		return nil, fmt.Errorf("queue declare: %w", err)
	}

	return &DoctorEventConsumer{conn: conn, ch: ch, queue: queue, handler: handler}, nil
}

// Consume starts consuming messages and blocks until ctx is cancelled.
func (c *DoctorEventConsumer) Consume() error {
	msgs, err := c.ch.Consume(c.queue, "", false, false, false, false, nil)
	if err != nil {
		return fmt.Errorf("consume: %w", err)
	}

	log.Printf("consuming from %s", c.queue)
	for msg := range msgs {
		var event DoctorCreatedEvent
		if err := json.Unmarshal(msg.Body, &event); err != nil {
			log.Printf("unmarshal error: %v — nacking", err)
			msg.Nack(false, false) // dead-letter
			continue
		}
		if err := c.handler(event); err != nil {
			log.Printf("handler error: %v — nacking with requeue", err)
			msg.Nack(false, true)
			continue
		}
		msg.Ack(false)
	}
	return nil
}

// Close releases RabbitMQ resources.
func (c *DoctorEventConsumer) Close() {
	c.ch.Close()
	c.conn.Close()
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> initializers

// initializers/services.go
// Pattern: dependency injection container — wires repositories → services
// Observed in: Medical-App-Core/initializers/services.go
package initializers

import (
	"medical-sas-api/domain/repositories/contracts"
	implrepos "medical-sas-api/infra/repositories"
	sc "medical-sas-api/services/contracts"
	si "medical-sas-api/services/implementations"

	"gorm.io/gorm"
)

// Services is the application-wide DI container.
// It is constructed once in main() and passed to controllers/middleware.
type Services struct {
	AuthTokenService  sc.AuthTokenServiceContract
	OrganizationService sc.OrganizationServiceContract
	UserService       sc.UserServiceContract
	PatientService    sc.PatientServiceContract
	DoctorService     sc.DoctorServiceContract
	AppointmentService sc.AppointmentServiceContract
	ChargeService     sc.ChargeServiceContract
	MedicalRecordService sc.MedicalRecordServiceContract
}

// InitServices builds and wires all repositories and services.
func InitServices(db *gorm.DB) *Services {
	// Repositories
	orgRepo         := implrepos.NewOrganizationRepository(db)
	userRepo        := implrepos.NewUserRepository(db)
	patientRepo     := implrepos.NewPatientRepository(db)
	doctorRepo      := implrepos.NewDoctorRepository(db)
	appointmentRepo := implrepos.NewAppointmentRepository(db)

	// Services (inject repos as interface values)
	authSvc  := si.NewAuthTokenService()
	orgSvc   := si.NewOrganizationService(orgRepo)
	userSvc  := si.NewUserService(userRepo, orgRepo)
	patSvc   := si.NewPatientService(patientRepo, orgRepo)
	docSvc   := si.NewDoctorService(doctorRepo, orgRepo)
	apptSvc  := si.NewAppointmentService(
		appointmentRepo, orgRepo, patientRepo, userRepo, doctorRepo,
	)
	chargeSvc := si.NewChargeService(orgRepo)

	return &Services{
		AuthTokenService:      authSvc,
		OrganizationService:   orgSvc,
		UserService:           userSvc,
		PatientService:        patSvc,
		DoctorService:         docSvc,
		AppointmentService:    apptSvc,
		ChargeService:         chargeSvc,
	}
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> events

// services/events/doctors/doctorEventConsumer.go
// Pattern: RabbitMQ consumer with reconnect loop + JSON unmarshaling
// Observed in: Medical-App-Core/services/events/
package events

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/streadway/amqp"
)

// DoctorCreatedEvent is the message payload published when a doctor is registered.
type DoctorCreatedEvent struct {
	DoctorID   string    `json:"doctor_id"`
	FullName   string    `json:"full_name"`
	Specialty  string    `json:"specialty"`
	CreatedAt  time.Time `json:"created_at"`
}

// DoctorEventConsumer subscribes to the doctors.created queue.
type DoctorEventConsumer struct {
	conn    *amqp.Connection
	ch      *amqp.Channel
	queue   string
	handler func(DoctorCreatedEvent) error
}

// NewDoctorEventConsumer dials RabbitMQ and declares the queue.
func NewDoctorEventConsumer(handler func(DoctorCreatedEvent) error) (*DoctorEventConsumer, error) {
	url := os.Getenv("RABBITMQ_BASE_URL")
	if url == "" {
		return nil, fmt.Errorf("RABBITMQ_BASE_URL is not set")
	}

	conn, err := amqp.Dial(url)
	if err != nil {
		return nil, fmt.Errorf("rabbitmq dial: %w", err)
	}

	ch, err := conn.Channel()
	if err != nil {
		conn.Close()
		return nil, fmt.Errorf("rabbitmq channel: %w", err)
	}

	queue := "doctors.created"
	_, err = ch.QueueDeclare(queue, true, false, false, false, nil)
	if err != nil {
		ch.Close(); conn.Close()
		return nil, fmt.Errorf("queue declare: %w", err)
	}

	return &DoctorEventConsumer{conn: conn, ch: ch, queue: queue, handler: handler}, nil
}

// Consume starts consuming messages and blocks until ctx is cancelled.
func (c *DoctorEventConsumer) Consume() error {
	msgs, err := c.ch.Consume(c.queue, "", false, false, false, false, nil)
	if err != nil {
		return fmt.Errorf("consume: %w", err)
	}

	log.Printf("consuming from %s", c.queue)
	for msg := range msgs {
		var event DoctorCreatedEvent
		if err := json.Unmarshal(msg.Body, &event); err != nil {
			log.Printf("unmarshal error: %v — nacking", err)
			msg.Nack(false, false) // dead-letter
			continue
		}
		if err := c.handler(event); err != nil {
			log.Printf("handler error: %v — nacking with requeue", err)
			msg.Nack(false, true)
			continue
		}
		msg.Ack(false)
	}
	return nil
}

// Close releases RabbitMQ resources.
func (c *DoctorEventConsumer) Close() {
	c.ch.Close()
	c.conn.Close()
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> initializers

// initializers/services.go
// Pattern: dependency injection container — wires repositories → services
// Observed in: Medical-App-Core/initializers/services.go
package initializers

import (
	"medical-sas-api/domain/repositories/contracts"
	implrepos "medical-sas-api/infra/repositories"
	sc "medical-sas-api/services/contracts"
	si "medical-sas-api/services/implementations"

	"gorm.io/gorm"
)

// Services is the application-wide DI container.
// It is constructed once in main() and passed to controllers/middleware.
type Services struct {
	AuthTokenService  sc.AuthTokenServiceContract
	OrganizationService sc.OrganizationServiceContract
	UserService       sc.UserServiceContract
	PatientService    sc.PatientServiceContract
	DoctorService     sc.DoctorServiceContract
	AppointmentService sc.AppointmentServiceContract
	ChargeService     sc.ChargeServiceContract
	MedicalRecordService sc.MedicalRecordServiceContract
}

// InitServices builds and wires all repositories and services.
func InitServices(db *gorm.DB) *Services {
	// Repositories
	orgRepo         := implrepos.NewOrganizationRepository(db)
	userRepo        := implrepos.NewUserRepository(db)
	patientRepo     := implrepos.NewPatientRepository(db)
	doctorRepo      := implrepos.NewDoctorRepository(db)
	appointmentRepo := implrepos.NewAppointmentRepository(db)

	// Services (inject repos as interface values)
	authSvc  := si.NewAuthTokenService()
	orgSvc   := si.NewOrganizationService(orgRepo)
	userSvc  := si.NewUserService(userRepo, orgRepo)
	patSvc   := si.NewPatientService(patientRepo, orgRepo)
	docSvc   := si.NewDoctorService(doctorRepo, orgRepo)
	apptSvc  := si.NewAppointmentService(
		appointmentRepo, orgRepo, patientRepo, userRepo, doctorRepo,
	)
	chargeSvc := si.NewChargeService(orgRepo)

	return &Services{
		AuthTokenService:      authSvc,
		OrganizationService:   orgSvc,
		UserService:           userSvc,
		PatientService:        patSvc,
		DoctorService:         docSvc,
		AppointmentService:    apptSvc,
		ChargeService:         chargeSvc,
	}
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> events

// services/events/doctors/doctorEventConsumer.go
// Pattern: RabbitMQ consumer with reconnect loop + JSON unmarshaling
// Observed in: Medical-App-Core/services/events/
package events

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/streadway/amqp"
)

// DoctorCreatedEvent is the message payload published when a doctor is registered.
type DoctorCreatedEvent struct {
	DoctorID   string    `json:"doctor_id"`
	FullName   string    `json:"full_name"`
	Specialty  string    `json:"specialty"`
	CreatedAt  time.Time `json:"created_at"`
}

// DoctorEventConsumer subscribes to the doctors.created queue.
type DoctorEventConsumer struct {
	conn    *amqp.Connection
	ch      *amqp.Channel
	queue   string
	handler func(DoctorCreatedEvent) error
}

// NewDoctorEventConsumer dials RabbitMQ and declares the queue.
func NewDoctorEventConsumer(handler func(DoctorCreatedEvent) error) (*DoctorEventConsumer, error) {
	url := os.Getenv("RABBITMQ_BASE_URL")
	if url == "" {
		return nil, fmt.Errorf("RABBITMQ_BASE_URL is not set")
	}

	conn, err := amqp.Dial(url)
	if err != nil {
		return nil, fmt.Errorf("rabbitmq dial: %w", err)
	}

	ch, err := conn.Channel()
	if err != nil {
		conn.Close()
		return nil, fmt.Errorf("rabbitmq channel: %w", err)
	}

	queue := "doctors.created"
	_, err = ch.QueueDeclare(queue, true, false, false, false, nil)
	if err != nil {
		ch.Close(); conn.Close()
		return nil, fmt.Errorf("queue declare: %w", err)
	}

	return &DoctorEventConsumer{conn: conn, ch: ch, queue: queue, handler: handler}, nil
}

// Consume starts consuming messages and blocks until ctx is cancelled.
func (c *DoctorEventConsumer) Consume() error {
	msgs, err := c.ch.Consume(c.queue, "", false, false, false, false, nil)
	if err != nil {
		return fmt.Errorf("consume: %w", err)
	}

	log.Printf("consuming from %s", c.queue)
	for msg := range msgs {
		var event DoctorCreatedEvent
		if err := json.Unmarshal(msg.Body, &event); err != nil {
			log.Printf("unmarshal error: %v — nacking", err)
			msg.Nack(false, false) // dead-letter
			continue
		}
		if err := c.handler(event); err != nil {
			log.Printf("handler error: %v — nacking with requeue", err)
			msg.Nack(false, true)
			continue
		}
		msg.Ack(false)
	}
	return nil
}

// Close releases RabbitMQ resources.
func (c *DoctorEventConsumer) Close() {
	c.ch.Close()
	c.conn.Close()
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> initializers

// initializers/services.go
// Pattern: dependency injection container — wires repositories → services
// Observed in: Medical-App-Core/initializers/services.go
package initializers

import (
	"medical-sas-api/domain/repositories/contracts"
	implrepos "medical-sas-api/infra/repositories"
	sc "medical-sas-api/services/contracts"
	si "medical-sas-api/services/implementations"

	"gorm.io/gorm"
)

// Services is the application-wide DI container.
// It is constructed once in main() and passed to controllers/middleware.
type Services struct {
	AuthTokenService  sc.AuthTokenServiceContract
	OrganizationService sc.OrganizationServiceContract
	UserService       sc.UserServiceContract
	PatientService    sc.PatientServiceContract
	DoctorService     sc.DoctorServiceContract
	AppointmentService sc.AppointmentServiceContract
	ChargeService     sc.ChargeServiceContract
	MedicalRecordService sc.MedicalRecordServiceContract
}

// InitServices builds and wires all repositories and services.
func InitServices(db *gorm.DB) *Services {
	// Repositories
	orgRepo         := implrepos.NewOrganizationRepository(db)
	userRepo        := implrepos.NewUserRepository(db)
	patientRepo     := implrepos.NewPatientRepository(db)
	doctorRepo      := implrepos.NewDoctorRepository(db)
	appointmentRepo := implrepos.NewAppointmentRepository(db)

	// Services (inject repos as interface values)
	authSvc  := si.NewAuthTokenService()
	orgSvc   := si.NewOrganizationService(orgRepo)
	userSvc  := si.NewUserService(userRepo, orgRepo)
	patSvc   := si.NewPatientService(patientRepo, orgRepo)
	docSvc   := si.NewDoctorService(doctorRepo, orgRepo)
	apptSvc  := si.NewAppointmentService(
		appointmentRepo, orgRepo, patientRepo, userRepo, doctorRepo,
	)
	chargeSvc := si.NewChargeService(orgRepo)

	return &Services{
		AuthTokenService:      authSvc,
		OrganizationService:   orgSvc,
		UserService:           userSvc,
		PatientService:        patSvc,
		DoctorService:         docSvc,
		AppointmentService:    apptSvc,
		ChargeService:         chargeSvc,
	}
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> events

// services/events/doctors/doctorEventConsumer.go
// Pattern: RabbitMQ consumer with reconnect loop + JSON unmarshaling
// Observed in: Medical-App-Core/services/events/
package events

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/streadway/amqp"
)

// DoctorCreatedEvent is the message payload published when a doctor is registered.
type DoctorCreatedEvent struct {
	DoctorID   string    `json:"doctor_id"`
	FullName   string    `json:"full_name"`
	Specialty  string    `json:"specialty"`
	CreatedAt  time.Time `json:"created_at"`
}

// DoctorEventConsumer subscribes to the doctors.created queue.
type DoctorEventConsumer struct {
	conn    *amqp.Connection
	ch      *amqp.Channel
	queue   string
	handler func(DoctorCreatedEvent) error
}

// NewDoctorEventConsumer dials RabbitMQ and declares the queue.
func NewDoctorEventConsumer(handler func(DoctorCreatedEvent) error) (*DoctorEventConsumer, error) {
	url := os.Getenv("RABBITMQ_BASE_URL")
	if url == "" {
		return nil, fmt.Errorf("RABBITMQ_BASE_URL is not set")
	}

	conn, err := amqp.Dial(url)
	if err != nil {
		return nil, fmt.Errorf("rabbitmq dial: %w", err)
	}

	ch, err := conn.Channel()
	if err != nil {
		conn.Close()
		return nil, fmt.Errorf("rabbitmq channel: %w", err)
	}

	queue := "doctors.created"
	_, err = ch.QueueDeclare(queue, true, false, false, false, nil)
	if err != nil {
		ch.Close(); conn.Close()
		return nil, fmt.Errorf("queue declare: %w", err)
	}

	return &DoctorEventConsumer{conn: conn, ch: ch, queue: queue, handler: handler}, nil
}

// Consume starts consuming messages and blocks until ctx is cancelled.
func (c *DoctorEventConsumer) Consume() error {
	msgs, err := c.ch.Consume(c.queue, "", false, false, false, false, nil)
	if err != nil {
		return fmt.Errorf("consume: %w", err)
	}

	log.Printf("consuming from %s", c.queue)
	for msg := range msgs {
		var event DoctorCreatedEvent
		if err := json.Unmarshal(msg.Body, &event); err != nil {
			log.Printf("unmarshal error: %v — nacking", err)
			msg.Nack(false, false) // dead-letter
			continue
		}
		if err := c.handler(event); err != nil {
			log.Printf("handler error: %v — nacking with requeue", err)
			msg.Nack(false, true)
			continue
		}
		msg.Ack(false)
	}
	return nil
}

// Close releases RabbitMQ resources.
func (c *DoctorEventConsumer) Close() {
	c.ch.Close()
	c.conn.Close()
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> implementations

// services/implementations/auth_service.go
// Pattern: JWT HS256 token generation + validation service
// Observed in: Medical-App-Core/services/implementations/auth_service.go
package implementations

import (
	"fmt"
	"os"
	"time"

	"github.com/golang-jwt/jwt/v4"
	"golang.org/x/crypto/bcrypt"
)

// Claims holds the JWT payload.
type Claims struct {
	UserID string `json:"user_id"`
	Email  string `json:"email"`
	Role   string `json:"role"`
	jwt.RegisteredClaims
}

// AuthTokenServiceImpl handles JWT creation and validation.
type AuthTokenServiceImpl struct {
	secret []byte
	ttl    time.Duration
}

// NewAuthTokenService reads JWT_SECRET from the environment and constructs the service.
func NewAuthTokenService() *AuthTokenServiceImpl {
	secret := os.Getenv("JWT_SECRET")
	if secret == "" {
		panic("JWT_SECRET environment variable must be set")
	}
	return &AuthTokenServiceImpl{
		secret: []byte(secret),
		ttl:    24 * time.Hour,
	}
}

// GenerateToken creates a signed HS256 JWT for the given user attributes.
func (s *AuthTokenServiceImpl) GenerateToken(userID, email, role string) (string, error) {
	claims := &Claims{
		UserID: userID,
		Email:  email,
		Role:   role,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.ttl)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "medical-sas-api",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signed, err := token.SignedString(s.secret)
	if err != nil {
		return "", fmt.Errorf("sign token: %w", err)
	}
	return signed, nil
}

// ValidateToken parses and validates a JWT string. Returns nil on success.
func (s *AuthTokenServiceImpl) ValidateToken(tokenString string) error {
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (any, error) {
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
		}
		return s.secret, nil
	})
	if err != nil {
		return fmt.Errorf("invalid token: %w", err)
	}
	if !token.Valid {
		return fmt.Errorf("token is not valid")
	}
	return nil
}

// ParseClaims extracts the Claims from a valid JWT string.
func (s *AuthTokenServiceImpl) ParseClaims(tokenString string) (*Claims, error) {
	claims := &Claims{}
	_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (any, error) {
		return s.secret, nil
	})
	if err != nil {
		return nil, fmt.Errorf("parse claims: %w", err)
	}
	return claims, nil
}

// HashPassword returns a bcrypt hash of the plain-text password.
func HashPassword(plain string) (string, error) {
	hashed, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)
	if err != nil {
		return "", fmt.Errorf("hash password: %w", err)
	}
	return string(hashed), nil
}

// CheckPassword returns nil if plain matches the bcrypt hash.
func CheckPassword(plain, hash string) error {
	if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)); err != nil {
		return fmt.Errorf("invalid credentials")
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> middleware

// middleware/auth.go
// Pattern: Fiber JWT bearer middleware with claims propagation via Locals
// Observed pattern from: Medical-App-Core/cmd/main.go AuthMiddleware
package middleware

import (
	"strings"

	"medical-sas-api/services/implementations"
	"github.com/gofiber/fiber/v2"
)

// BearerAuth validates the Authorization: Bearer header and stores parsed
// claims in fiber.Ctx.Locals("claims") for downstream handlers.
func BearerAuth(authSvc *implementations.AuthTokenServiceImpl) fiber.Handler {
	return func(c *fiber.Ctx) error {
		header := c.Get("Authorization")
		if !strings.HasPrefix(header, "Bearer ") {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error": "missing or malformed Authorization header",
			})
		}

		tokenStr := strings.TrimPrefix(header, "Bearer ")
		claims, err := authSvc.ParseClaims(tokenStr)
		if err != nil {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error":  "invalid or expired token",
				"reason": err.Error(),
			})
		}

		// Make claims available to all downstream handlers
		c.Locals("claims", claims)
		c.Locals("user_id", claims.UserID)
		c.Locals("role", claims.Role)
		return c.Next()
	}
}

// RoleRequired returns a middleware that allows only the specified roles.
// Must be chained after BearerAuth.
func RoleRequired(roles ...string) fiber.Handler {
	allowed := make(map[string]struct{}, len(roles))
	for _, r := range roles {
		allowed[r] = struct{}{}
	}
	return func(c *fiber.Ctx) error {
		role, _ := c.Locals("role").(string)
		if _, ok := allowed[role]; !ok {
			return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
				"error": "insufficient permissions",
			})
		}
		return c.Next()
	}
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> controllers

// controllers/auth_controller.go
// Pattern: login + token generation handler
// Observed in: Medical-App-Core/controllers/auth_controller.go
package controllers

import (
	"net/http"

	"medical-sas-api/services/implementations"
	"github.com/gofiber/fiber/v2"
)

// AuthController handles token generation and validation endpoints.
type AuthController struct {
	authSvc *implementations.AuthTokenServiceImpl
}

func NewAuthController(svc *implementations.AuthTokenServiceImpl) *AuthController {
	return &AuthController{authSvc: svc}
}

type generateTokenRequest struct {
	UserID string `json:"user_id" validate:"required,uuid4"`
	Email  string `json:"email"   validate:"required,email"`
	Role   string `json:"role"    validate:"required"`
}

// GenerateToken godoc
// @Summary     Issue a JWT token
// @Description Generates a signed JWT for the given user credentials
// @Tags        Auth
// @Accept      json
// @Produce     json
// @Param       body body generateTokenRequest true "Token request"
// @Success     200 {object} fiber.Map{token=string}
// @Failure     400 {object} fiber.Map
// @Failure     500 {object} fiber.Map
// @Router      /auth/token [post]
func (ac *AuthController) GenerateToken(c *fiber.Ctx) error {
	var req generateTokenRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid request payload"})
	}
	if req.UserID == "" || req.Email == "" || req.Role == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "user_id, email, and role are required"})
	}

	token, err := ac.authSvc.GenerateToken(req.UserID, req.Email, req.Role)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "failed to generate token"})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{"token": token})
}

// ValidateToken godoc
// @Summary     Validate a JWT token
// @Description Returns 200 if the token is valid, 401 otherwise
// @Tags        Auth
// @Accept      json
// @Produce     json
// @Param       body body fiber.Map{token=string} true "Token to validate"
// @Success     200 {object} fiber.Map{valid=bool}
// @Failure     401 {object} fiber.Map
// @Router      /auth/validate [post]
func (ac *AuthController) ValidateToken(c *fiber.Ctx) error {
	var body struct {
		Token string `json:"token"`
	}
	if err := c.BodyParser(&body); err != nil || body.Token == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "token is required"})
	}

	if err := ac.authSvc.ValidateToken(body.Token); err != nil {
		return c.Status(http.StatusUnauthorized).JSON(fiber.Map{
			"valid":  false,
			"reason": err.Error(),
		})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{"valid": true})
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> initializers

// initializers/validators.go
// Pattern: custom validator registration with go-playground/validator
// Observed in: Medical-App-Core/initializers/validators.go
package initializers

import (
	"strconv"
	"strings"

	"github.com/go-playground/validator/v10"
)

// RegisterCustomValidators adds domain-specific validation rules.
func RegisterCustomValidators(v *validator.Validate) {
	_ = v.RegisterValidation("cpf",  validateCPF)
	_ = v.RegisterValidation("cnpj", validateCNPJ)
	_ = v.RegisterValidation("crm",  validateCRM)
}

// validateCPF validates a Brazilian CPF number (11 digits, checksum).
func validateCPF(fl validator.FieldLevel) bool {
	cpf := strings.ReplaceAll(fl.Field().String(), ".", "")
	cpf  = strings.ReplaceAll(cpf, "-", "")
	if len(cpf) != 11 || allEqual(cpf) {
		return false
	}
	return cpfChecksum(cpf)
}

func cpfChecksum(cpf string) bool {
	digits := make([]int, 11)
	for i, ch := range cpf {
		digits[i], _ = strconv.Atoi(string(ch))
	}
	for pass := 0; pass < 2; pass++ {
		weight := 10 + pass
		sum    := 0
		for i := 0; i < 9+pass; i++ {
			sum += digits[i] * (weight - i)
		}
		rem := (sum * 10) % 11
		if rem == 10 || rem == 11 {
			rem = 0
		}
		if rem != digits[9+pass] {
			return false
		}
	}
	return true
}

// validateCNPJ validates a Brazilian CNPJ number (14 digits, checksum).
func validateCNPJ(fl validator.FieldLevel) bool {
	cnpj := strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' { return r }
		return -1
	}, fl.Field().String())
	if len(cnpj) != 14 || allEqual(cnpj) {
		return false
	}
	return cnpjChecksum(cnpj)
}

func cnpjChecksum(cnpj string) bool {
	weights1 := []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}
	weights2 := []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}
	for pass, weights := range [][]int{weights1, weights2} {
		sum := 0
		for i, w := range weights {
			d, _ := strconv.Atoi(string(cnpj[i]))
			sum += d * w
		}
		rem := sum % 11
		expected := byte('0')
		if rem >= 2 {
			expected = byte('0' + (11 - rem))
		}
		if cnpj[12+pass] != expected {
			return false
		}
	}
	return true
}

// validateCRM validates a Brazilian CRM number (digits optionally followed by UF code).
func validateCRM(fl validator.FieldLevel) bool {
	crm := fl.Field().String()
	return len(crm) >= 4 && len(crm) <= 10
}

func allEqual(s string) bool {
	if s == "" { return false }
	for _, c := range s[1:] {
		if byte(c) != s[0] { return false }
	}
	return true
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> implementations

// services/implementations/auth_service.go
// Pattern: JWT HS256 token generation + validation service
// Observed in: Medical-App-Core/services/implementations/auth_service.go
package implementations

import (
	"fmt"
	"os"
	"time"

	"github.com/golang-jwt/jwt/v4"
	"golang.org/x/crypto/bcrypt"
)

// Claims holds the JWT payload.
type Claims struct {
	UserID string `json:"user_id"`
	Email  string `json:"email"`
	Role   string `json:"role"`
	jwt.RegisteredClaims
}

// AuthTokenServiceImpl handles JWT creation and validation.
type AuthTokenServiceImpl struct {
	secret []byte
	ttl    time.Duration
}

// NewAuthTokenService reads JWT_SECRET from the environment and constructs the service.
func NewAuthTokenService() *AuthTokenServiceImpl {
	secret := os.Getenv("JWT_SECRET")
	if secret == "" {
		panic("JWT_SECRET environment variable must be set")
	}
	return &AuthTokenServiceImpl{
		secret: []byte(secret),
		ttl:    24 * time.Hour,
	}
}

// GenerateToken creates a signed HS256 JWT for the given user attributes.
func (s *AuthTokenServiceImpl) GenerateToken(userID, email, role string) (string, error) {
	claims := &Claims{
		UserID: userID,
		Email:  email,
		Role:   role,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.ttl)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "medical-sas-api",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signed, err := token.SignedString(s.secret)
	if err != nil {
		return "", fmt.Errorf("sign token: %w", err)
	}
	return signed, nil
}

// ValidateToken parses and validates a JWT string. Returns nil on success.
func (s *AuthTokenServiceImpl) ValidateToken(tokenString string) error {
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (any, error) {
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
		}
		return s.secret, nil
	})
	if err != nil {
		return fmt.Errorf("invalid token: %w", err)
	}
	if !token.Valid {
		return fmt.Errorf("token is not valid")
	}
	return nil
}

// ParseClaims extracts the Claims from a valid JWT string.
func (s *AuthTokenServiceImpl) ParseClaims(tokenString string) (*Claims, error) {
	claims := &Claims{}
	_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (any, error) {
		return s.secret, nil
	})
	if err != nil {
		return nil, fmt.Errorf("parse claims: %w", err)
	}
	return claims, nil
}

// HashPassword returns a bcrypt hash of the plain-text password.
func HashPassword(plain string) (string, error) {
	hashed, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)
	if err != nil {
		return "", fmt.Errorf("hash password: %w", err)
	}
	return string(hashed), nil
}

// CheckPassword returns nil if plain matches the bcrypt hash.
func CheckPassword(plain, hash string) error {
	if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)); err != nil {
		return fmt.Errorf("invalid credentials")
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> middleware

// middleware/auth.go
// Pattern: Fiber JWT bearer middleware with claims propagation via Locals
// Observed pattern from: Medical-App-Core/cmd/main.go AuthMiddleware
package middleware

import (
	"strings"

	"medical-sas-api/services/implementations"
	"github.com/gofiber/fiber/v2"
)

// BearerAuth validates the Authorization: Bearer header and stores parsed
// claims in fiber.Ctx.Locals("claims") for downstream handlers.
func BearerAuth(authSvc *implementations.AuthTokenServiceImpl) fiber.Handler {
	return func(c *fiber.Ctx) error {
		header := c.Get("Authorization")
		if !strings.HasPrefix(header, "Bearer ") {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error": "missing or malformed Authorization header",
			})
		}

		tokenStr := strings.TrimPrefix(header, "Bearer ")
		claims, err := authSvc.ParseClaims(tokenStr)
		if err != nil {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error":  "invalid or expired token",
				"reason": err.Error(),
			})
		}

		// Make claims available to all downstream handlers
		c.Locals("claims", claims)
		c.Locals("user_id", claims.UserID)
		c.Locals("role", claims.Role)
		return c.Next()
	}
}

// RoleRequired returns a middleware that allows only the specified roles.
// Must be chained after BearerAuth.
func RoleRequired(roles ...string) fiber.Handler {
	allowed := make(map[string]struct{}, len(roles))
	for _, r := range roles {
		allowed[r] = struct{}{}
	}
	return func(c *fiber.Ctx) error {
		role, _ := c.Locals("role").(string)
		if _, ok := allowed[role]; !ok {
			return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
				"error": "insufficient permissions",
			})
		}
		return c.Next()
	}
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> controllers

// controllers/auth_controller.go
// Pattern: login + token generation handler
// Observed in: Medical-App-Core/controllers/auth_controller.go
package controllers

import (
	"net/http"

	"medical-sas-api/services/implementations"
	"github.com/gofiber/fiber/v2"
)

// AuthController handles token generation and validation endpoints.
type AuthController struct {
	authSvc *implementations.AuthTokenServiceImpl
}

func NewAuthController(svc *implementations.AuthTokenServiceImpl) *AuthController {
	return &AuthController{authSvc: svc}
}

type generateTokenRequest struct {
	UserID string `json:"user_id" validate:"required,uuid4"`
	Email  string `json:"email"   validate:"required,email"`
	Role   string `json:"role"    validate:"required"`
}

// GenerateToken godoc
// @Summary     Issue a JWT token
// @Description Generates a signed JWT for the given user credentials
// @Tags        Auth
// @Accept      json
// @Produce     json
// @Param       body body generateTokenRequest true "Token request"
// @Success     200 {object} fiber.Map{token=string}
// @Failure     400 {object} fiber.Map
// @Failure     500 {object} fiber.Map
// @Router      /auth/token [post]
func (ac *AuthController) GenerateToken(c *fiber.Ctx) error {
	var req generateTokenRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid request payload"})
	}
	if req.UserID == "" || req.Email == "" || req.Role == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "user_id, email, and role are required"})
	}

	token, err := ac.authSvc.GenerateToken(req.UserID, req.Email, req.Role)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "failed to generate token"})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{"token": token})
}

// ValidateToken godoc
// @Summary     Validate a JWT token
// @Description Returns 200 if the token is valid, 401 otherwise
// @Tags        Auth
// @Accept      json
// @Produce     json
// @Param       body body fiber.Map{token=string} true "Token to validate"
// @Success     200 {object} fiber.Map{valid=bool}
// @Failure     401 {object} fiber.Map
// @Router      /auth/validate [post]
func (ac *AuthController) ValidateToken(c *fiber.Ctx) error {
	var body struct {
		Token string `json:"token"`
	}
	if err := c.BodyParser(&body); err != nil || body.Token == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "token is required"})
	}

	if err := ac.authSvc.ValidateToken(body.Token); err != nil {
		return c.Status(http.StatusUnauthorized).JSON(fiber.Map{
			"valid":  false,
			"reason": err.Error(),
		})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{"valid": true})
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> initializers

// initializers/validators.go
// Pattern: custom validator registration with go-playground/validator
// Observed in: Medical-App-Core/initializers/validators.go
package initializers

import (
	"strconv"
	"strings"

	"github.com/go-playground/validator/v10"
)

// RegisterCustomValidators adds domain-specific validation rules.
func RegisterCustomValidators(v *validator.Validate) {
	_ = v.RegisterValidation("cpf",  validateCPF)
	_ = v.RegisterValidation("cnpj", validateCNPJ)
	_ = v.RegisterValidation("crm",  validateCRM)
}

// validateCPF validates a Brazilian CPF number (11 digits, checksum).
func validateCPF(fl validator.FieldLevel) bool {
	cpf := strings.ReplaceAll(fl.Field().String(), ".", "")
	cpf  = strings.ReplaceAll(cpf, "-", "")
	if len(cpf) != 11 || allEqual(cpf) {
		return false
	}
	return cpfChecksum(cpf)
}

func cpfChecksum(cpf string) bool {
	digits := make([]int, 11)
	for i, ch := range cpf {
		digits[i], _ = strconv.Atoi(string(ch))
	}
	for pass := 0; pass < 2; pass++ {
		weight := 10 + pass
		sum    := 0
		for i := 0; i < 9+pass; i++ {
			sum += digits[i] * (weight - i)
		}
		rem := (sum * 10) % 11
		if rem == 10 || rem == 11 {
			rem = 0
		}
		if rem != digits[9+pass] {
			return false
		}
	}
	return true
}

// validateCNPJ validates a Brazilian CNPJ number (14 digits, checksum).
func validateCNPJ(fl validator.FieldLevel) bool {
	cnpj := strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' { return r }
		return -1
	}, fl.Field().String())
	if len(cnpj) != 14 || allEqual(cnpj) {
		return false
	}
	return cnpjChecksum(cnpj)
}

func cnpjChecksum(cnpj string) bool {
	weights1 := []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}
	weights2 := []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}
	for pass, weights := range [][]int{weights1, weights2} {
		sum := 0
		for i, w := range weights {
			d, _ := strconv.Atoi(string(cnpj[i]))
			sum += d * w
		}
		rem := sum % 11
		expected := byte('0')
		if rem >= 2 {
			expected = byte('0' + (11 - rem))
		}
		if cnpj[12+pass] != expected {
			return false
		}
	}
	return true
}

// validateCRM validates a Brazilian CRM number (digits optionally followed by UF code).
func validateCRM(fl validator.FieldLevel) bool {
	crm := fl.Field().String()
	return len(crm) >= 4 && len(crm) <= 10
}

func allEqual(s string) bool {
	if s == "" { return false }
	for _, c := range s[1:] {
		if byte(c) != s[0] { return false }
	}
	return true
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> implementations

// services/implementations/auth_service.go
// Pattern: JWT HS256 token generation + validation service
// Observed in: Medical-App-Core/services/implementations/auth_service.go
package implementations

import (
	"fmt"
	"os"
	"time"

	"github.com/golang-jwt/jwt/v4"
	"golang.org/x/crypto/bcrypt"
)

// Claims holds the JWT payload.
type Claims struct {
	UserID string `json:"user_id"`
	Email  string `json:"email"`
	Role   string `json:"role"`
	jwt.RegisteredClaims
}

// AuthTokenServiceImpl handles JWT creation and validation.
type AuthTokenServiceImpl struct {
	secret []byte
	ttl    time.Duration
}

// NewAuthTokenService reads JWT_SECRET from the environment and constructs the service.
func NewAuthTokenService() *AuthTokenServiceImpl {
	secret := os.Getenv("JWT_SECRET")
	if secret == "" {
		panic("JWT_SECRET environment variable must be set")
	}
	return &AuthTokenServiceImpl{
		secret: []byte(secret),
		ttl:    24 * time.Hour,
	}
}

// GenerateToken creates a signed HS256 JWT for the given user attributes.
func (s *AuthTokenServiceImpl) GenerateToken(userID, email, role string) (string, error) {
	claims := &Claims{
		UserID: userID,
		Email:  email,
		Role:   role,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.ttl)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "medical-sas-api",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signed, err := token.SignedString(s.secret)
	if err != nil {
		return "", fmt.Errorf("sign token: %w", err)
	}
	return signed, nil
}

// ValidateToken parses and validates a JWT string. Returns nil on success.
func (s *AuthTokenServiceImpl) ValidateToken(tokenString string) error {
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (any, error) {
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
		}
		return s.secret, nil
	})
	if err != nil {
		return fmt.Errorf("invalid token: %w", err)
	}
	if !token.Valid {
		return fmt.Errorf("token is not valid")
	}
	return nil
}

// ParseClaims extracts the Claims from a valid JWT string.
func (s *AuthTokenServiceImpl) ParseClaims(tokenString string) (*Claims, error) {
	claims := &Claims{}
	_, err := jwt.ParseWithClaims(tokenString, claims, func(t *jwt.Token) (any, error) {
		return s.secret, nil
	})
	if err != nil {
		return nil, fmt.Errorf("parse claims: %w", err)
	}
	return claims, nil
}

// HashPassword returns a bcrypt hash of the plain-text password.
func HashPassword(plain string) (string, error) {
	hashed, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)
	if err != nil {
		return "", fmt.Errorf("hash password: %w", err)
	}
	return string(hashed), nil
}

// CheckPassword returns nil if plain matches the bcrypt hash.
func CheckPassword(plain, hash string) error {
	if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)); err != nil {
		return fmt.Errorf("invalid credentials")
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> middleware

// middleware/auth.go
// Pattern: Fiber JWT bearer middleware with claims propagation via Locals
// Observed pattern from: Medical-App-Core/cmd/main.go AuthMiddleware
package middleware

import (
	"strings"

	"medical-sas-api/services/implementations"
	"github.com/gofiber/fiber/v2"
)

// BearerAuth validates the Authorization: Bearer header and stores parsed
// claims in fiber.Ctx.Locals("claims") for downstream handlers.
func BearerAuth(authSvc *implementations.AuthTokenServiceImpl) fiber.Handler {
	return func(c *fiber.Ctx) error {
		header := c.Get("Authorization")
		if !strings.HasPrefix(header, "Bearer ") {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error": "missing or malformed Authorization header",
			})
		}

		tokenStr := strings.TrimPrefix(header, "Bearer ")
		claims, err := authSvc.ParseClaims(tokenStr)
		if err != nil {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error":  "invalid or expired token",
				"reason": err.Error(),
			})
		}

		// Make claims available to all downstream handlers
		c.Locals("claims", claims)
		c.Locals("user_id", claims.UserID)
		c.Locals("role", claims.Role)
		return c.Next()
	}
}

// RoleRequired returns a middleware that allows only the specified roles.
// Must be chained after BearerAuth.
func RoleRequired(roles ...string) fiber.Handler {
	allowed := make(map[string]struct{}, len(roles))
	for _, r := range roles {
		allowed[r] = struct{}{}
	}
	return func(c *fiber.Ctx) error {
		role, _ := c.Locals("role").(string)
		if _, ok := allowed[role]; !ok {
			return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
				"error": "insufficient permissions",
			})
		}
		return c.Next()
	}
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> controllers

// controllers/auth_controller.go
// Pattern: login + token generation handler
// Observed in: Medical-App-Core/controllers/auth_controller.go
package controllers

import (
	"net/http"

	"medical-sas-api/services/implementations"
	"github.com/gofiber/fiber/v2"
)

// AuthController handles token generation and validation endpoints.
type AuthController struct {
	authSvc *implementations.AuthTokenServiceImpl
}

func NewAuthController(svc *implementations.AuthTokenServiceImpl) *AuthController {
	return &AuthController{authSvc: svc}
}

type generateTokenRequest struct {
	UserID string `json:"user_id" validate:"required,uuid4"`
	Email  string `json:"email"   validate:"required,email"`
	Role   string `json:"role"    validate:"required"`
}

// GenerateToken godoc
// @Summary     Issue a JWT token
// @Description Generates a signed JWT for the given user credentials
// @Tags        Auth
// @Accept      json
// @Produce     json
// @Param       body body generateTokenRequest true "Token request"
// @Success     200 {object} fiber.Map{token=string}
// @Failure     400 {object} fiber.Map
// @Failure     500 {object} fiber.Map
// @Router      /auth/token [post]
func (ac *AuthController) GenerateToken(c *fiber.Ctx) error {
	var req generateTokenRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid request payload"})
	}
	if req.UserID == "" || req.Email == "" || req.Role == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "user_id, email, and role are required"})
	}

	token, err := ac.authSvc.GenerateToken(req.UserID, req.Email, req.Role)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "failed to generate token"})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{"token": token})
}

// ValidateToken godoc
// @Summary     Validate a JWT token
// @Description Returns 200 if the token is valid, 401 otherwise
// @Tags        Auth
// @Accept      json
// @Produce     json
// @Param       body body fiber.Map{token=string} true "Token to validate"
// @Success     200 {object} fiber.Map{valid=bool}
// @Failure     401 {object} fiber.Map
// @Router      /auth/validate [post]
func (ac *AuthController) ValidateToken(c *fiber.Ctx) error {
	var body struct {
		Token string `json:"token"`
	}
	if err := c.BodyParser(&body); err != nil || body.Token == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "token is required"})
	}

	if err := ac.authSvc.ValidateToken(body.Token); err != nil {
		return c.Status(http.StatusUnauthorized).JSON(fiber.Map{
			"valid":  false,
			"reason": err.Error(),
		})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{"valid": true})
}
</go_file>