using Data.SeaHavenIndustries; using FluentValidation; using SeaHaven.DataServices.Interfaces; using SeaHaven.Services.DTOs; using SeaHaven.Services.Interfaces; using SeaHaven.Services.Validation; namespace SeaHaven.Services.Implementation { /// /// Contact service implementation /// Contains business logic, validation, and orchestration for Contact operations /// public class ContactService : IContactService { private readonly IContactDataService _contactDataService; private readonly ICreateContactValidation _createValidator; private readonly IUpdateContactValidation _updateValidator; public ContactService( IContactDataService contactDataService, ICreateContactValidation createValidator, IUpdateContactValidation updateValidator) { _contactDataService = contactDataService; _createValidator = createValidator; _updateValidator = updateValidator; } // Query operations public async Task GetContactByIdAsync(int id) { var contact = await _contactDataService.GetByIdAsync(id); return contact == null ? null : MapToDTO(contact); } public async Task GetContactByIdWithDetailsAsync(int id) { var contact = await _contactDataService.GetByIdWithDetailsAsync(id); return contact == null ? null : MapToDTO(contact); } public async Task> GetAllContactsAsync() { var contacts = await _contactDataService.GetAllAsync(); return contacts.Select(MapToDTO); } public async Task> GetContactsByAccountIdAsync(int accountId) { var contacts = await _contactDataService.GetByAccountIdAsync(accountId); return contacts.Select(MapToDTO); } public async Task> GetContactsByTypeAsync(string type) { var contacts = await _contactDataService.GetByTypeAsync(type); return contacts.Select(MapToDTO); } public async Task GetContactByEmailAsync(string email) { var contact = await _contactDataService.GetByEmailAsync(email); return contact == null ? null : MapToDTO(contact); } // Paged results public async Task> GetContactsPagedAsync( int page, int pageSize, string? search = null, string? type = null) { var (items, totalCount) = await _contactDataService.GetPagedAsync(page, pageSize, search, type); return new PagedResult { Items = items.Select(MapToDTO), TotalCount = totalCount, Page = page, PageSize = pageSize }; } // Command operations public async Task CreateContactAsync(CreateContactDTO dto, string userId) { var validationResult = await _createValidator.ValidateAsync(dto); if (!validationResult.IsValid) { throw new ValidationException(validationResult.Errors); } if (await _contactDataService.EmailExistsAsync(dto.Email)) throw new InvalidOperationException($"Email '{dto.Email}' already exists"); var contact = new Contacts { FirstName = dto.FirstName, LastName = dto.LastName, Email = dto.Email, PhoneNumber = dto.Phone ?? dto.Mobile, Title = dto.Title, ContactType = dto.Type, AccountId = dto.AccountId, createdby = userId, CreatedDate = DateTime.Now }; var savedContact = await _contactDataService.AddAsync(contact); return MapToDTO(savedContact); } public async Task UpdateContactAsync(int id, UpdateContactDTO dto, string userId) { var validationResult = await _updateValidator.ValidateAsync(dto); if (!validationResult.IsValid) { throw new ValidationException(validationResult.Errors); } var existingContact = await _contactDataService.GetByIdAsync(id); if (existingContact == null) throw new KeyNotFoundException($"Contact with ID {id} not found"); if (!string.IsNullOrWhiteSpace(dto.Email) && dto.Email != existingContact.Email) { if (await _contactDataService.EmailExistsAsync(dto.Email, id)) throw new InvalidOperationException($"Email '{dto.Email}' already exists"); } if (!string.IsNullOrWhiteSpace(dto.FirstName)) existingContact.FirstName = dto.FirstName; if (!string.IsNullOrWhiteSpace(dto.LastName)) existingContact.LastName = dto.LastName; if (!string.IsNullOrWhiteSpace(dto.Email)) existingContact.Email = dto.Email; if (dto.Phone != null || dto.Mobile != null) existingContact.PhoneNumber = dto.Phone ?? dto.Mobile; if (dto.Title != null) existingContact.Title = dto.Title; if (dto.Type != null) existingContact.ContactType = dto.Type; if (dto.AccountId.HasValue) existingContact.AccountId = dto.AccountId; existingContact.LastModificationTime = DateTime.Now; await _contactDataService.UpdateAsync(existingContact); return MapToDTO(existingContact); } public async Task DeleteContactAsync(int id, string userId) { // Check if exists if (!await _contactDataService.ExistsAsync(id)) throw new KeyNotFoundException($"Contact with ID {id} not found"); // Business logic: Check if contact is being used (add your logic here) // For example, check if contact is linked to work orders, etc. await _contactDataService.DeleteAsync(id); } // Validation operations public async Task ContactExistsAsync(int id) { return await _contactDataService.ExistsAsync(id); } public async Task EmailExistsAsync(string email, int? excludeId = null) { return await _contactDataService.EmailExistsAsync(email, excludeId); } public async Task PhoneExistsAsync(string phone, int? excludeId = null) { return await _contactDataService.PhoneExistsAsync(phone, excludeId); } // Statistics public async Task GetTotalContactCountAsync() { return await _contactDataService.CountAsync(); } public async Task GetContactCountByAccountAsync(int accountId) { return await _contactDataService.CountByAccountIdAsync(accountId); } public async Task> GetContactCountByTypeAsync() { var contacts = await _contactDataService.GetAllAsync(); return contacts .Where(c => !string.IsNullOrWhiteSpace(c.ContactType)) .GroupBy(c => c.ContactType!) .ToDictionary(g => g.Key, g => g.Count()); } // Private mapping methods private static ContactDTO MapToDTO(Contacts contact) { return new ContactDTO { Id = contact.Id, FirstName = contact.FirstName, LastName = contact.LastName, Email = contact.Email, Phone = contact.PhoneNumber, Mobile = contact.PhoneNumber, // Contacts only has one PhoneNumber field Title = contact.Title, Type = contact.ContactType, AccountId = contact.AccountId, AccountName = contact.Account?.Name, CreatedDate = contact.CreatedDate, CreatedBy = contact.createdby, ModifiedDate = contact.LastModificationTime, ModifiedBy = null // Not tracked in FullAuditEntity }; } } }