# 🐾 IMPLEMENTACIÓN BREEDS MODULE

## **📋 OBJETIVO ESPECÍFICO**

Implementar únicamente la gestión de **Breeds (Razas)** que depende exclusivamente de **Species**, incluyendo validaciones de integridad referencial y lógica de negocio específica para razas.

---

## **🎯 ANÁLISIS BREEDS MODULE**

### **🔹 ENTIDAD A IMPLEMENTAR:**

**Breeds (Razas)**
- **Depende exclusivamente de Species** - Validación de integridad referencial
- **Subtipos específicos**: Bulldog Francés, Pastor Alemán, Siamés, etc.
- **CRUD con validaciones**: Crear, listar, editar y eliminar razas
- **Unicidad por especie**: Nombres únicos dentro de la misma especie

### **🔹 RELACIÓN DEFINIDA:**
```
Species (1) -----> (N) Breeds
```

### **🔹 CARACTERÍSTICAS CLAVE:**
- ✅ **Dependiente de Species**: Cada raza debe tener una especie válida
- ✅ **Unicidad compuesta**: Nombre único por especie (no globalmente)
- ✅ **Validación referencial**: No se puede crear raza sin especie válida
- ✅ **Filtrado por especie**: Listar razas de una especie específica
- ✅ **Cascading rules**: Preparado para cuando se eliminen especies

### **🔹 REGLAS DE NEGOCIO:**
- ✅ El species_id es obligatorio y debe existir
- ✅ El nombre es único dentro de la misma especie (no globalmente)
- ✅ Una raza puede tener descripción específica
- ✅ Las razas pueden estar activas o inactivas
- ✅ Se puede cambiar la especie de una raza (con validaciones)

---

## **🏗️ PLAN DE IMPLEMENTACIÓN PASO A PASO**

### **FASE 1: PREPARACIÓN Y ESTRUCTURA BASE**

#### **PASO 1.1: Crear Estructura de Directorios para Breeds**

**🔧 COMANDOS PHP ARTISAN:**

```powershell
# 1. Los directorios base ya existen desde Species, solo agregar archivos específicos

# 2. Crear controlador de Breeds
php artisan make:controller Animal/Catalog/BreedController --api

# 3. Crear Form Requests para Breeds
php artisan make:request Animal/Catalog/StoreBreedRequest
php artisan make:request Animal/Catalog/UpdateBreedRequest

# 4. Crear excepciones personalizadas para Breeds
php artisan make:exception Animal/Catalog/BreedNotFoundException
php artisan make:exception Animal/Catalog/BreedValidationException

# 5. Crear tests para Breeds
php artisan make:test Animal/BreedRepositoryTest --unit
php artisan make:test Animal/BreedServiceTest --unit
php artisan make:test Animal/BreedValidatorTest --unit
php artisan make:test Animal/BreedManagementTest
php artisan make:test Animal/SpeciesBreedIntegrationTest
```

**📁 Estructura resultante para Breeds:**
```
app/
├── Interfaces/
│   └── Animal/
│       └── Catalog/
│           ├── SpeciesRepositoryInterface.php ✅
│           ├── SpeciesServiceInterface.php ✅
│           ├── BreedRepositoryInterface.php 🆕
│           └── BreedServiceInterface.php 🆕
├── Repositories/
│   └── Animal/
│       └── Catalog/
│           ├── SpeciesRepository.php ✅
│           └── BreedRepository.php 🆕
├── Services/
│   └── Animal/
│       └── Catalog/
│           ├── SpeciesService.php ✅
│           └── BreedService.php 🆕
├── Http/
│   ├── Controllers/
│   │   └── Animal/
│   │       └── Catalog/
│   │           ├── SpeciesController.php ✅
│   │           └── BreedController.php 🆕
│   └── Requests/
│       └── Animal/
│           └── Catalog/
│               ├── StoreSpeciesRequest.php ✅
│               ├── UpdateSpeciesRequest.php ✅
│               ├── StoreBreedRequest.php 🆕
│               └── UpdateBreedRequest.php 🆕
├── Exceptions/
│   └── Animal/
│       └── Catalog/
│           ├── SpeciesNotFoundException.php ✅
│           ├── SpeciesValidationException.php ✅
│           ├── BreedNotFoundException.php 🆕
│           └── BreedValidationException.php 🆕
└── Validators/
    └── Animal/
        └── Catalog/
            ├── SpeciesValidator.php ✅
            └── BreedValidator.php 🆕
```

#### **PASO 1.2: Verificar Modelo Breed Existente**

**Verificar que el modelo Breed.php tenga:**
```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Breed extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = [
        'name',
        'species_id',
        'description',
        'is_active'
    ];

    protected $casts = [
        'is_active' => 'boolean',
        'deleted_at' => 'datetime'
    ];

    // Relación con Species (obligatoria)
    public function species()
    {
        return $this->belongsTo(Species::class);
    }

    // Relación con Animals (preparación futura)
    public function animals()
    {
        return $this->hasMany(Animal::class);
    }
}
```

---

### **FASE 2: IMPLEMENTACIÓN COMPLETA DE BREEDS**

#### **PASO 2.1: Interfaces de Breeds**

**BreedRepositoryInterface.php:**
```php
<?php

namespace App\Interfaces\Animal\Catalog;

interface BreedRepositoryInterface
{
    public function findAll();
    public function findActive();
    public function findById(int $id);
    public function findByName(string $name);
    public function findBySpeciesId(int $speciesId);
    public function create(array $data);
    public function update(int $id, array $data);
    public function delete(int $id);
    public function forceDelete(int $id);
    public function restore(int $id);
    public function exists(int $id): bool;
    public function hasAnimals(int $id): bool;
    public function validateSpeciesAssociation(int $breedId, int $speciesId): bool;
    public function isNameUniqueInSpecies(string $name, int $speciesId, ?int $excludeId = null): bool;
    public function countBySpecies(int $speciesId): int;
}
```

**BreedServiceInterface.php:**
```php
<?php

namespace App\Interfaces\Animal\Catalog;

interface BreedServiceInterface
{
    public function getAllBreeds(bool $includeInactive = false);
    public function getActiveBreeds();
    public function getBreedById(int $id);
    public function getBreedsBySpecies(int $speciesId, bool $includeInactive = false);
    public function searchBreedsByName(string $name);
    public function createBreed(array $data);
    public function updateBreed(int $id, array $data);
    public function deleteBreed(int $id);
    public function restoreBreed(int $id);
    public function forceDeleteBreed(int $id);
    public function validateBreedExists(int $id): bool;
    public function validateBreedBelongsToSpecies(int $breedId, int $speciesId): bool;
    public function canDeleteBreed(int $id): bool;
    public function getBreedStats(): array;
}
```

#### **PASO 2.2: Repositorio de Breeds**

**BreedRepository.php:**
```php
<?php

namespace App\Repositories\Animal\Catalog;

use App\Interfaces\Animal\Catalog\BreedRepositoryInterface;
use App\Models\Breed;
use App\Exceptions\Animal\Catalog\BreedNotFoundException;

class BreedRepository implements BreedRepositoryInterface
{
    public function findAll()
    {
        return Breed::with('species')
                    ->withTrashed()
                    ->orderBy('name')
                    ->get();
    }

    public function findActive()
    {
        return Breed::with('species')
                    ->where('is_active', true)
                    ->orderBy('name')
                    ->get();
    }

    public function findById(int $id)
    {
        $breed = Breed::with('species')->withTrashed()->find($id);
        
        if (!$breed) {
            throw new BreedNotFoundException("Breed with ID {$id} not found");
        }
        
        return $breed;
    }

    public function findByName(string $name)
    {
        return Breed::with('species')
                    ->where('name', 'like', "%{$name}%")
                    ->orderBy('name')
                    ->get();
    }

    public function findBySpeciesId(int $speciesId)
    {
        return Breed::with('species')
                    ->where('species_id', $speciesId)
                    ->orderBy('name')
                    ->get();
    }

    public function create(array $data)
    {
        // Formatear nombre (Primera letra mayúscula)
        $data['name'] = ucwords(strtolower(trim($data['name'])));
        
        return Breed::create($data);
    }

    public function update(int $id, array $data)
    {
        $breed = $this->findById($id);
        
        // Formatear nombre si se está actualizando
        if (isset($data['name'])) {
            $data['name'] = ucwords(strtolower(trim($data['name'])));
        }
        
        $breed->update($data);
        return $breed->fresh('species');
    }

    public function delete(int $id)
    {
        $breed = $this->findById($id);
        return $breed->delete(); // Soft delete
    }

    public function forceDelete(int $id)
    {
        $breed = $this->findById($id);
        return $breed->forceDelete(); // Hard delete
    }

    public function restore(int $id)
    {
        $breed = Breed::withTrashed()->find($id);
        
        if (!$breed) {
            throw new BreedNotFoundException("Breed with ID {$id} not found");
        }
        
        return $breed->restore();
    }

    public function exists(int $id): bool
    {
        return Breed::where('id', $id)->exists();
    }

    public function hasAnimals(int $id): bool
    {
        // Preparado para cuando implementemos Animals
        // return Breed::where('id', $id)->has('animals')->exists();
        return false; // Por ahora siempre false
    }

    public function validateSpeciesAssociation(int $breedId, int $speciesId): bool
    {
        return Breed::where('id', $breedId)
                    ->where('species_id', $speciesId)
                    ->exists();
    }

    public function isNameUniqueInSpecies(string $name, int $speciesId, ?int $excludeId = null): bool
    {
        $formattedName = ucwords(strtolower(trim($name)));
        
        $query = Breed::withTrashed()
                      ->where('name', $formattedName)
                      ->where('species_id', $speciesId);
        
        if ($excludeId) {
            $query->where('id', '!=', $excludeId);
        }
        
        return !$query->exists();
    }

    public function countBySpecies(int $speciesId): int
    {
        return Breed::where('species_id', $speciesId)->count();
    }
}
```

#### **PASO 2.3: Servicio de Breeds**

**BreedService.php:**
```php
<?php

namespace App\Services\Animal\Catalog;

use App\Interfaces\Animal\Catalog\BreedServiceInterface;
use App\Interfaces\Animal\Catalog\BreedRepositoryInterface;
use App\Interfaces\Animal\Catalog\SpeciesServiceInterface;
use App\Validators\Animal\Catalog\BreedValidator;
use Illuminate\Support\Facades\DB;

class BreedService implements BreedServiceInterface
{
    protected $breedRepository;
    protected $speciesService;
    protected $validator;

    public function __construct(
        BreedRepositoryInterface $breedRepository,
        SpeciesServiceInterface $speciesService,
        BreedValidator $validator
    ) {
        $this->breedRepository = $breedRepository;
        $this->speciesService = $speciesService;
        $this->validator = $validator;
    }

    public function getAllBreeds(bool $includeInactive = false)
    {
        return $includeInactive 
            ? $this->breedRepository->findAll()
            : $this->breedRepository->findActive();
    }

    public function getActiveBreeds()
    {
        return $this->breedRepository->findActive();
    }

    public function getBreedById(int $id)
    {
        return $this->breedRepository->findById($id);
    }

    public function getBreedsBySpecies(int $speciesId, bool $includeInactive = false)
    {
        // Validar que la especie existe primero
        $this->speciesService->getSpeciesById($speciesId);
        
        $breeds = $this->breedRepository->findBySpeciesId($speciesId);
        
        if (!$includeInactive) {
            $breeds = $breeds->where('is_active', true);
        }
        
        return $breeds;
    }

    public function searchBreedsByName(string $name)
    {
        $this->validator->validateSearchTerm($name);
        return $this->breedRepository->findByName($name);
    }

    public function createBreed(array $data)
    {
        $this->validator->validateForCreation($data);
        
        DB::beginTransaction();
        try {
            $breed = $this->breedRepository->create($data);
            DB::commit();
            
            return $breed->load('species');
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function updateBreed(int $id, array $data)
    {
        $this->validator->validateForUpdate($id, $data);
        
        DB::beginTransaction();
        try {
            $breed = $this->breedRepository->update($id, $data);
            DB::commit();
            
            return $breed;
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function deleteBreed(int $id)
    {
        $this->validator->validateForDeletion($id);
        
        DB::beginTransaction();
        try {
            $result = $this->breedRepository->delete($id);
            DB::commit();
            
            return $result;
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function restoreBreed(int $id)
    {
        $this->validator->validateForRestore($id);
        
        DB::beginTransaction();
        try {
            $result = $this->breedRepository->restore($id);
            DB::commit();
            
            return $result;
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function forceDeleteBreed(int $id)
    {
        $this->validator->validateForForceDelete($id);
        
        DB::beginTransaction();
        try {
            $result = $this->breedRepository->forceDelete($id);
            DB::commit();
            
            return $result;
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function validateBreedExists(int $id): bool
    {
        return $this->breedRepository->exists($id);
    }

    public function validateBreedBelongsToSpecies(int $breedId, int $speciesId): bool
    {
        return $this->breedRepository->validateSpeciesAssociation($breedId, $speciesId);
    }

    public function canDeleteBreed(int $id): bool
    {
        return !$this->breedRepository->hasAnimals($id);
    }

    public function getBreedStats(): array
    {
        $allBreeds = $this->breedRepository->findAll();
        $activeBreeds = $this->breedRepository->findActive();
        
        return [
            'total_breeds' => $allBreeds->count(),
            'active_breeds' => $activeBreeds->count(),
            'inactive_breeds' => $allBreeds->count() - $activeBreeds->count(),
            'breeds_by_species' => $allBreeds->groupBy('species.name')->map->count()
        ];
    }
}
```

#### **PASO 2.4: Validador de Breeds**

**BreedValidator.php:**
```php
<?php

namespace App\Validators\Animal\Catalog;

use App\Interfaces\Animal\Catalog\BreedRepositoryInterface;
use App\Interfaces\Animal\Catalog\SpeciesServiceInterface;
use App\Exceptions\Animal\Catalog\BreedValidationException;

class BreedValidator
{
    protected $breedRepository;
    protected $speciesService;

    public function __construct(
        BreedRepositoryInterface $breedRepository,
        SpeciesServiceInterface $speciesService
    ) {
        $this->breedRepository = $breedRepository;
        $this->speciesService = $speciesService;
    }

    public function validateForCreation(array $data)
    {
        $this->validateRequired($data);
        $this->validateNameFormat($data['name']);
        $this->validateSpeciesExists($data['species_id']);
        $this->validateUniqueNameInSpecies($data['name'], $data['species_id']);
        $this->validateDescriptionLength($data);
    }

    public function validateForUpdate(int $id, array $data)
    {
        $this->validateExists($id);
        
        if (isset($data['name'])) {
            $this->validateNameFormat($data['name']);
        }
        
        if (isset($data['species_id'])) {
            $this->validateSpeciesExists($data['species_id']);
        }
        
        // Si se cambia nombre o especie, validar unicidad
        if (isset($data['name']) || isset($data['species_id'])) {
            $currentBreed = $this->breedRepository->findById($id);
            
            $name = $data['name'] ?? $currentBreed->name;
            $speciesId = $data['species_id'] ?? $currentBreed->species_id;
            
            $this->validateUniqueNameInSpecies($name, $speciesId, $id);
        }
        
        if (isset($data['description'])) {
            $this->validateDescriptionLength($data);
        }
    }

    public function validateForDeletion(int $id)
    {
        $this->validateExists($id);
        $this->validateNoRelatedRecords($id);
    }

    public function validateForRestore(int $id)
    {
        // Validar que la raza existe en soft delete
        $breed = \App\Models\Breed::withTrashed()->find($id);
        
        if (!$breed) {
            throw new BreedValidationException("Breed with ID {$id} not found");
        }
        
        if (!$breed->trashed()) {
            throw new BreedValidationException("Breed with ID {$id} is not deleted");
        }
        
        // Validar que la especie asociada sigue existiendo
        $this->validateSpeciesExists($breed->species_id);
    }

    public function validateForForceDelete(int $id)
    {
        $this->validateExists($id);
        $this->validateNoRelatedRecords($id);
    }

    public function validateSearchTerm(string $term)
    {
        if (strlen(trim($term)) < 2) {
            throw new BreedValidationException('Search term must be at least 2 characters long');
        }
    }

    private function validateRequired(array $data)
    {
        if (empty($data['name']) || trim($data['name']) === '') {
            throw new BreedValidationException('Breed name is required');
        }
        
        if (empty($data['species_id'])) {
            throw new BreedValidationException('Species ID is required');
        }
    }

    private function validateNameFormat(string $name)
    {
        $trimmedName = trim($name);
        
        if (strlen($trimmedName) < 2) {
            throw new BreedValidationException('Breed name must be at least 2 characters long');
        }
        
        if (strlen($trimmedName) > 255) {
            throw new BreedValidationException('Breed name cannot exceed 255 characters');
        }
        
        if (!preg_match('/^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s\-\.]+$/', $trimmedName)) {
            throw new BreedValidationException('Breed name can only contain letters, spaces, hyphens and dots');
        }
    }

    private function validateDescriptionLength(array $data)
    {
        if (isset($data['description']) && strlen($data['description']) > 1000) {
            throw new BreedValidationException('Description cannot exceed 1000 characters');
        }
    }

    private function validateExists(int $id)
    {
        if (!$this->breedRepository->exists($id)) {
            throw new BreedValidationException("Breed with ID {$id} does not exist");
        }
    }

    private function validateSpeciesExists(int $speciesId)
    {
        if (!$this->speciesService->validateSpeciesExists($speciesId)) {
            throw new BreedValidationException("Species with ID {$speciesId} does not exist");
        }
    }

    private function validateUniqueNameInSpecies(string $name, int $speciesId, ?int $excludeId = null)
    {
        if (!$this->breedRepository->isNameUniqueInSpecies($name, $speciesId, $excludeId)) {
            $formattedName = ucwords(strtolower(trim($name)));
            throw new BreedValidationException("Breed name '{$formattedName}' already exists in this species");
        }
    }

    private function validateNoRelatedRecords(int $id)
    {
        if ($this->breedRepository->hasAnimals($id)) {
            throw new BreedValidationException('Cannot delete breed with associated animals');
        }
    }
}
```

#### **PASO 2.5: Excepciones de Breeds**

**BreedNotFoundException.php:**
```php
<?php

namespace App\Exceptions\Animal\Catalog;

use Exception;

class BreedNotFoundException extends Exception
{
    protected $message = 'Breed not found';
    
    public function __construct($message = null, $code = 404, Exception $previous = null)
    {
        $message = $message ?: $this->message;
        parent::__construct($message, $code, $previous);
    }
    
    public function render($request)
    {
        return response()->json([
            'success' => false,
            'error' => 'Breed Not Found',
            'message' => $this->getMessage(),
            'code' => $this->getCode()
        ], $this->getCode());
    }
}
```

**BreedValidationException.php:**
```php
<?php

namespace App\Exceptions\Animal\Catalog;

use Exception;

class BreedValidationException extends Exception
{
    protected $message = 'Breed validation failed';
    
    public function __construct($message = null, $code = 422, Exception $previous = null)
    {
        $message = $message ?: $this->message;
        parent::__construct($message, $code, $previous);
    }
    
    public function render($request)
    {
        return response()->json([
            'success' => false,
            'error' => 'Validation Error',
            'message' => $this->getMessage(),
            'code' => $this->getCode()
        ], $this->getCode());
    }
}
```

#### **PASO 2.6: Form Requests de Breeds**

**StoreBreedRequest.php:**
```php
<?php

namespace App\Http\Requests\Animal\Catalog;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreBreedRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'name' => [
                'required',
                'string',
                'min:2',
                'max:255',
                'regex:/^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s\-\.]+$/',
                Rule::unique('breeds')->where(function ($query) {
                    return $query->where('species_id', $this->species_id);
                })
            ],
            'species_id' => 'required|integer|exists:species,id',
            'description' => 'nullable|string|max:1000',
            'is_active' => 'boolean'
        ];
    }

    public function messages()
    {
        return [
            'name.required' => 'El nombre de la raza es obligatorio',
            'name.min' => 'El nombre debe tener al menos 2 caracteres',
            'name.max' => 'El nombre no puede exceder 255 caracteres',
            'name.regex' => 'El nombre solo puede contener letras, espacios, guiones y puntos',
            'name.unique' => 'Ya existe una raza con este nombre en la especie seleccionada',
            'species_id.required' => 'La especie es obligatoria',
            'species_id.exists' => 'La especie seleccionada no existe',
            'description.max' => 'La descripción no puede exceder 1000 caracteres',
            'is_active.boolean' => 'El estado activo debe ser verdadero o falso'
        ];
    }

    protected function prepareForValidation()
    {
        // Formatear el nombre antes de validar
        if ($this->has('name')) {
            $this->merge([
                'name' => ucwords(strtolower(trim($this->name)))
            ]);
        }
        
        // Establecer is_active por defecto como true
        if (!$this->has('is_active')) {
            $this->merge(['is_active' => true]);
        }
    }
}
```

**UpdateBreedRequest.php:**
```php
<?php

namespace App\Http\Requests\Animal\Catalog;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateBreedRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        $breedId = $this->route('breed');
        $currentBreed = \App\Models\Breed::find($breedId);
        $speciesId = $this->species_id ?? ($currentBreed ? $currentBreed->species_id : null);

        return [
            'name' => [
                'sometimes',
                'required',
                'string',
                'min:2',
                'max:255',
                'regex:/^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s\-\.]+$/',
                Rule::unique('breeds')->where(function ($query) use ($speciesId) {
                    return $query->where('species_id', $speciesId);
                })->ignore($breedId)
            ],
            'species_id' => 'sometimes|required|integer|exists:species,id',
            'description' => 'sometimes|nullable|string|max:1000',
            'is_active' => 'sometimes|boolean'
        ];
    }

    public function messages()
    {
        return [
            'name.required' => 'El nombre de la raza es obligatorio',
            'name.min' => 'El nombre debe tener al menos 2 caracteres',
            'name.max' => 'El nombre no puede exceder 255 caracteres',
            'name.regex' => 'El nombre solo puede contener letras, espacios, guiones y puntos',
            'name.unique' => 'Ya existe una raza con este nombre en la especie seleccionada',
            'species_id.required' => 'La especie es obligatoria',
            'species_id.exists' => 'La especie seleccionada no existe',
            'description.max' => 'La descripción no puede exceder 1000 caracteres',
            'is_active.boolean' => 'El estado activo debe ser verdadero o falso'
        ];
    }

    protected function prepareForValidation()
    {
        // Formatear el nombre antes de validar si se está actualizando
        if ($this->has('name')) {
            $this->merge([
                'name' => ucwords(strtolower(trim($this->name)))
            ]);
        }
    }
}
```

#### **PASO 2.7: Controlador de Breeds**

**BreedController.php:**
```php
<?php

namespace App\Http\Controllers\Animal\Catalog;

use App\Http\Controllers\Controller;
use App\Interfaces\Animal\Catalog\BreedServiceInterface;
use App\Http\Requests\Animal\Catalog\StoreBreedRequest;
use App\Http\Requests\Animal\Catalog\UpdateBreedRequest;
use App\Exceptions\Animal\Catalog\BreedNotFoundException;
use App\Exceptions\Animal\Catalog\BreedValidationException;
use App\Exceptions\Animal\Catalog\SpeciesNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class BreedController extends Controller
{
    protected $breedService;

    public function __construct(BreedServiceInterface $breedService)
    {
        $this->breedService = $breedService;
    }

    /**
     * Display a listing of breeds
     */
    public function index(Request $request): JsonResponse
    {
        try {
            $includeInactive = $request->boolean('include_inactive', false);
            $breeds = $this->breedService->getAllBreeds($includeInactive);
            
            return response()->json([
                'success' => true,
                'data' => $breeds,
                'message' => 'Breeds retrieved successfully',
                'meta' => [
                    'total' => $breeds->count(),
                    'include_inactive' => $includeInactive
                ]
            ], 200);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error retrieving breeds',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Store a newly created breed
     */
    public function store(StoreBreedRequest $request): JsonResponse
    {
        try {
            $breed = $this->breedService->createBreed($request->validated());
            
            return response()->json([
                'success' => true,
                'data' => $breed,
                'message' => 'Breed created successfully'
            ], 201);
            
        } catch (BreedValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'validation'
            ], 422);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error creating breed',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Display the specified breed
     */
    public function show(int $id): JsonResponse
    {
        try {
            $breed = $this->breedService->getBreedById($id);
            
            return response()->json([
                'success' => true,
                'data' => $breed,
                'message' => 'Breed retrieved successfully'
            ], 200);
            
        } catch (BreedNotFoundException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'not_found'
            ], 404);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error retrieving breed',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Update the specified breed
     */
    public function update(UpdateBreedRequest $request, int $id): JsonResponse
    {
        try {
            $breed = $this->breedService->updateBreed($id, $request->validated());
            
            return response()->json([
                'success' => true,
                'data' => $breed,
                'message' => 'Breed updated successfully'
            ], 200);
            
        } catch (BreedNotFoundException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'not_found'
            ], 404);
            
        } catch (BreedValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'validation'
            ], 422);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error updating breed',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Remove the specified breed (soft delete)
     */
    public function destroy(int $id): JsonResponse
    {
        try {
            $this->breedService->deleteBreed($id);
            
            return response()->json([
                'success' => true,
                'message' => 'Breed deleted successfully'
            ], 200);
            
        } catch (BreedNotFoundException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'not_found'
            ], 404);
            
        } catch (BreedValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'validation'
            ], 422);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error deleting breed',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get breeds by specific species
     */
    public function getBySpecies(int $speciesId, Request $request): JsonResponse
    {
        try {
            $includeInactive = $request->boolean('include_inactive', false);
            $breeds = $this->breedService->getBreedsBySpecies($speciesId, $includeInactive);
            
            return response()->json([
                'success' => true,
                'data' => $breeds,
                'message' => 'Breeds retrieved successfully',
                'meta' => [
                    'species_id' => $speciesId,
                    'total' => $breeds->count(),
                    'include_inactive' => $includeInactive
                ]
            ], 200);
            
        } catch (SpeciesNotFoundException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'not_found'
            ], 404);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error retrieving breeds by species',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Search breeds by name
     */
    public function search(Request $request): JsonResponse
    {
        try {
            $searchTerm = $request->input('q', '');
            
            if (empty($searchTerm)) {
                return response()->json([
                    'success' => false,
                    'message' => 'Search term is required'
                ], 400);
            }
            
            $breeds = $this->breedService->searchBreedsByName($searchTerm);
            
            return response()->json([
                'success' => true,
                'data' => $breeds,
                'message' => 'Search completed successfully',
                'meta' => [
                    'search_term' => $searchTerm,
                    'results_count' => $breeds->count()
                ]
            ], 200);
            
        } catch (BreedValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'validation'
            ], 422);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error searching breeds',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Restore a soft deleted breed
     */
    public function restore(int $id): JsonResponse
    {
        try {
            $this->breedService->restoreBreed($id);
            
            return response()->json([
                'success' => true,
                'message' => 'Breed restored successfully'
            ], 200);
            
        } catch (BreedValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
                'error_type' => 'validation'
            ], 422);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error restoring breed',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get breed statistics
     */
    public function stats(): JsonResponse
    {
        try {
            $stats = $this->breedService->getBreedStats();
            
            return response()->json([
                'success' => true,
                'data' => $stats,
                'message' => 'Breed statistics retrieved successfully'
            ], 200);
            
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Error retrieving breed statistics',
                'error' => $e->getMessage()
            ], 500);
        }
    }
}
```

---

### **FASE 3: CONFIGURACIÓN E INTEGRACIÓN**

#### **PASO 3.1: Actualizar Inyección de Dependencias**

**AppServiceProvider.php (agregar a lo existente):**
```php
public function register()
{
    // Species Module Bindings (ya existentes)
    $this->app->bind(
        \App\Interfaces\Animal\Catalog\SpeciesRepositoryInterface::class,
        \App\Repositories\Animal\Catalog\SpeciesRepository::class
    );
    
    $this->app->bind(
        \App\Interfaces\Animal\Catalog\SpeciesServiceInterface::class,
        \App\Services\Animal\Catalog\SpeciesService::class
    );

    // Breed Module Bindings (nuevos)
    $this->app->bind(
        \App\Interfaces\Animal\Catalog\BreedRepositoryInterface::class,
        \App\Repositories\Animal\Catalog\BreedRepository::class
    );
    
    $this->app->bind(
        \App\Interfaces\Animal\Catalog\BreedServiceInterface::class,
        \App\Services\Animal\Catalog\BreedService::class
    );
}
```

#### **PASO 3.2: Actualizar Definición de Rutas**

**routes/bully.php (agregar a las existentes):**
```php
<?php

use App\Http\Controllers\Animal\Catalog\SpeciesController;
use App\Http\Controllers\Animal\Catalog\BreedController;
use Illuminate\Support\Facades\Route;

Route::prefix('animal')->group(function () {
    Route::prefix('catalog')->group(function () {
        
        // Species Routes (ya existentes)
        Route::apiResource('species', SpeciesController::class);
        Route::get('species/search', [SpeciesController::class, 'search']);
        Route::post('species/{id}/restore', [SpeciesController::class, 'restore']);
        
        // Breeds Routes (nuevos)
        Route::apiResource('breeds', BreedController::class);
        Route::get('breeds/search', [BreedController::class, 'search']);
        Route::get('breeds/by-species/{speciesId}', [BreedController::class, 'getBySpecies']);
        Route::post('breeds/{id}/restore', [BreedController::class, 'restore']);
        Route::get('breeds/stats', [BreedController::class, 'stats']);
        
    });
});
```

---

### **FASE 4: TESTING ESPECÍFICO BREEDS**

#### **PASO 4.1: Unit Tests**

**BreedRepositoryTest.php:**
```php
<?php

namespace Tests\Unit\Animal;

use Tests\TestCase;
use App\Models\Breed;
use App\Models\Species;
use App\Repositories\Animal\Catalog\BreedRepository;
use App\Exceptions\Animal\Catalog\BreedNotFoundException;
use Illuminate\Foundation\Testing\RefreshDatabase;

class BreedRepositoryTest extends TestCase
{
    use RefreshDatabase;
    
    protected $repository;
    protected $species;

    protected function setUp(): void
    {
        parent::setUp();
        $this->repository = new BreedRepository();
        $this->species = Species::factory()->create(['name' => 'Perro']);
    }

    public function test_can_create_breed()
    {
        $data = [
            'name' => 'bulldog francés',
            'species_id' => $this->species->id,
            'description' => 'Raza pequeña y compacta',
            'is_active' => true
        ];

        $breed = $this->repository->create($data);

        $this->assertInstanceOf(Breed::class, $breed);
        $this->assertEquals('Bulldog Francés', $breed->name); // Verifica formato
        $this->assertEquals($this->species->id, $breed->species_id);
    }

    public function test_can_find_breeds_by_species()
    {
        Breed::factory()->count(3)->create(['species_id' => $this->species->id]);
        $otherSpecies = Species::factory()->create(['name' => 'Gato']);
        Breed::factory()->count(2)->create(['species_id' => $otherSpecies->id]);

        $breeds = $this->repository->findBySpeciesId($this->species->id);

        $this->assertCount(3, $breeds);
        $breeds->each(function ($breed) {
            $this->assertEquals($this->species->id, $breed->species_id);
        });
    }

    public function test_validates_species_association()
    {
        $breed = Breed::factory()->create(['species_id' => $this->species->id]);

        $this->assertTrue(
            $this->repository->validateSpeciesAssociation($breed->id, $this->species->id)
        );
        
        $this->assertFalse(
            $this->repository->validateSpeciesAssociation($breed->id, 999)
        );
    }

    public function test_checks_name_uniqueness_in_species()
    {
        Breed::factory()->create([
            'name' => 'Bulldog Francés',
            'species_id' => $this->species->id
        ]);

        $this->assertFalse(
            $this->repository->isNameUniqueInSpecies('bulldog francés', $this->species->id)
        );
        
        $this->assertTrue(
            $this->repository->isNameUniqueInSpecies('Pastor Alemán', $this->species->id)
        );
    }

    public function test_allows_same_name_in_different_species()
    {
        $otherSpecies = Species::factory()->create(['name' => 'Gato']);
        
        Breed::factory()->create([
            'name' => 'Común',
            'species_id' => $this->species->id
        ]);

        // Debe permitir el mismo nombre en otra especie
        $this->assertTrue(
            $this->repository->isNameUniqueInSpecies('Común', $otherSpecies->id)
        );
    }
}
```

#### **PASO 4.2: Integration Tests**

**SpeciesBreedIntegrationTest.php:**
```php
<?php

namespace Tests\Feature\Animal;

use Tests\TestCase;
use App\Models\Species;
use App\Models\Breed;
use Illuminate\Foundation\Testing\RefreshDatabase;

class SpeciesBreedIntegrationTest extends TestCase
{
    use RefreshDatabase;

    public function test_complete_species_breed_workflow()
    {
        // 1. Crear especie
        $speciesResponse = $this->postJson('/api/animal/catalog/species', [
            'name' => 'perro',
            'description' => 'Canis lupus familiaris'
        ]);
        
        $speciesResponse->assertStatus(201);
        $speciesId = $speciesResponse->json('data.id');

        // 2. Crear razas para la especie
        $breeds = ['Bulldog Francés', 'Pastor Alemán', 'Labrador Retriever'];
        
        foreach ($breeds as $breedName) {
            $breedResponse = $this->postJson('/api/animal/catalog/breeds', [
                'name' => $breedName,
                'species_id' => $speciesId
            ]);
            
            $breedResponse->assertStatus(201);
        }

        // 3. Listar razas por especie
        $breedsListResponse = $this->getJson("/api/animal/catalog/breeds/by-species/{$speciesId}");
        
        $breedsListResponse->assertStatus(200)
                          ->assertJsonCount(3, 'data');

        // 4. Intentar eliminar especie con razas (debe fallar)
        $deleteSpeciesResponse = $this->deleteJson("/api/animal/catalog/species/{$speciesId}");
        
        $deleteSpeciesResponse->assertStatus(422); // Validation error

        // 5. Eliminar razas primero
        $breedsList = $breedsListResponse->json('data');
        foreach ($breedsList as $breed) {
            $this->deleteJson("/api/animal/catalog/breeds/{$breed['id']}")
                 ->assertStatus(200);
        }

        // 6. Ahora sí se puede eliminar la especie
        $deleteSpeciesResponse = $this->deleteJson("/api/animal/catalog/species/{$speciesId}");
        $deleteSpeciesResponse->assertStatus(200);
    }

    public function test_breed_uniqueness_per_species()
    {
        // Crear dos especies
        $species1 = Species::factory()->create(['name' => 'Perro']);
        $species2 = Species::factory()->create(['name' => 'Gato']);

        // Crear raza "Común" en primera especie
        $this->postJson('/api/animal/catalog/breeds', [
            'name' => 'Común',
            'species_id' => $species1->id
        ])->assertStatus(201);

        // Debe permitir crear "Común" en segunda especie
        $this->postJson('/api/animal/catalog/breeds', [
            'name' => 'Común', 
            'species_id' => $species2->id
        ])->assertStatus(201);

        // Pero no debe permitir duplicar en la misma especie
        $this->postJson('/api/animal/catalog/breeds', [
            'name' => 'Común',
            'species_id' => $species1->id
        ])->assertStatus(422);
    }

    public function test_breed_species_validation()
    {
        // Intentar crear raza con especie inexistente
        $this->postJson('/api/animal/catalog/breeds', [
            'name' => 'Test Breed',
            'species_id' => 999
        ])->assertStatus(422);

        // Crear especie válida
        $species = Species::factory()->create();

        // Ahora debe funcionar
        $this->postJson('/api/animal/catalog/breeds', [
            'name' => 'Test Breed',
            'species_id' => $species->id
        ])->assertStatus(201);
    }
}
```

---

## **📊 CRITERIOS DE ÉXITO BREEDS MODULE**

### **✅ Funcionalidades que deben funcionar perfectamente:**

1. **CRUD Completo con Validaciones:**
   - ✅ Crear razas con species_id válido
   - ✅ Validar que la especie existe antes de crear raza
   - ✅ Nombres únicos por especie (no globalmente)
   - ✅ Listar razas (todas, por especie, activas)
   - ✅ Actualizar razas con validaciones
   - ✅ Eliminar razas (soft delete)
   - ✅ Restaurar razas eliminadas

2. **Validaciones de Integridad Referencial:**
   - ✅ No permitir razas sin especie válida
   - ✅ Validar consistencia en actualizaciones
   - ✅ Permitir mismo nombre en diferentes especies
   - ✅ Evitar duplicados dentro de la misma especie

3. **Funcionalidades Específicas:**
   - ✅ Listar razas por especie específica
   - ✅ Búsqueda por nombre de raza
   - ✅ Estadísticas de razas por especie
   - ✅ Filtro por estado activo/inactivo

4. **Testing Coverage:**
   - ✅ Unit tests > 90% coverage
   - ✅ Feature tests para todos los endpoints
   - ✅ Integration tests para flujos Species-Breed
   - ✅ Validaciones de integridad testeadas

---

## **🚀 ENDPOINTS RESULTANTES**

```
GET    /api/animal/catalog/breeds                    # Listar razas
POST   /api/animal/catalog/breeds                    # Crear raza
GET    /api/animal/catalog/breeds/{id}               # Obtener raza
PUT    /api/animal/catalog/breeds/{id}               # Actualizar raza
DELETE /api/animal/catalog/breeds/{id}               # Eliminar raza
GET    /api/animal/catalog/breeds/search             # Buscar razas
GET    /api/animal/catalog/breeds/by-species/{id}    # Razas por especie
POST   /api/animal/catalog/breeds/{id}/restore       # Restaurar raza
GET    /api/animal/catalog/breeds/stats              # Estadísticas
```

---

## **🔄 SIGUIENTE PASO**

Una vez completado **Breeds Module** exitosamente:

1. **Testing completo** de integración Species + Breeds
2. **Corrección de bugs** y refinamientos
3. **Documentación de API** endpoints completa
4. **Merge a main** cuando ambos módulos estén perfectos
5. **Preparación** para **Animals Module** (siguiente fase)

**¡Esta implementación nos dará el catálogo completo Species + Breeds como base sólida para Animals!** 🐾✨
