using Data.Managers; using DeepDrftData.Repositories; using DeepDrftModels.DTOs; using DeepDrftModels.Entities; using Microsoft.Extensions.Logging; using Models.Common; using NetBlocks.Models; namespace DeepDrftData; /// /// SQL-side track service built on the BlazorBlocks Manager base. The layer boundary: /// TrackRepository outputs entities; this service outputs DTOs via TrackConverter — the /// single authoritative entity↔DTO conversion path. The ITrackService surface is DTO-typed /// throughout; the entity never escapes the service layer. /// /// The base Manager<> surface does not line up with ITrackService by signature (base /// Add vs Create, base Update→Result vs Update→DTO, base Get/GetPage vs GetAll/GetPaged, /// base GetById→TDto vs GetById→TDto?), so the query and mutation methods are implemented /// here over Repository + TrackConverter. Only Delete(long)→Result is inherited unchanged. /// public class TrackManager : Manager, ITrackService { public TrackManager( TrackRepository repository, ILogger> logger) : base(repository, logger) { } // Explicit impl: base GetById returns ResultContainer (fails on miss); the // service contract is ResultContainer (pass with null on miss). Return types // differ, so this cannot be a public overload of the inherited member. async Task> ITrackService.GetById(long id) { try { var entity = await Repository.GetByIdAsync(id); return ResultContainer.CreatePassResult( entity is null ? null : TrackConverter.Convert(entity)); } catch (Exception e) { return ResultContainer.CreateFailResult(e.Message); } } // Lookup by vault entry key. No base-name conflict (unlike GetById), so this is a plain // public method. Mirrors the nullable-on-miss shape of ITrackService.GetById. public async Task> GetByEntryKey(string entryKey) { try { var entity = await Repository.GetByEntryKeyAsync(entryKey); return ResultContainer.CreatePassResult( entity is null ? null : TrackConverter.Convert(entity)); } catch (Exception e) { return ResultContainer.CreateFailResult(e.Message); } } // No base-name conflict, so this is a plain public method. Mirrors the nullable-on-empty // shape of GetById: pass with null when the library has no tracks. public async Task> GetRandom(CancellationToken cancellationToken = default) { try { var entity = await Repository.GetRandomAsync(cancellationToken); return ResultContainer.CreatePassResult( entity is null ? null : TrackConverter.Convert(entity)); } catch (Exception e) { return ResultContainer.CreateFailResult(e.Message); } } public async Task>> GetAll() { try { var entities = await Repository.GetAllAsync(); return ResultContainer>.CreatePassResult( entities.Select(TrackConverter.Convert).ToList()); } catch (Exception e) { return ResultContainer>.CreateFailResult(e.Message); } } public async Task>> GetPaged( int pageNumber, int pageSize, string? sortColumn, bool sortDescending, CancellationToken cancellationToken = default) { try { var parameters = new PagingParameters { Page = pageNumber, PageSize = pageSize, IsDescending = sortDescending, OrderBy = sortColumn switch { "TrackName" => e => e.TrackName, "Artist" => e => e.Artist, "Album" => e => (object)(e.Album ?? string.Empty), "Genre" => e => (object)(e.Genre ?? string.Empty), "ReleaseDate" => e => (object)(e.ReleaseDate ?? DateOnly.MaxValue), _ => e => e.Id } }; var page = await Repository.GetPagedAsync(parameters); var dtoPage = PagedResult.From(page, page.Items.Select(TrackConverter.Convert)); return ResultContainer>.CreatePassResult(dtoPage); } catch (Exception e) { return ResultContainer>.CreateFailResult(e.Message); } } public async Task> Create(TrackDto newTrack) { try { var added = await Repository.AddAsync(TrackConverter.Convert(newTrack)); return ResultContainer.CreatePassResult(TrackConverter.Convert(added)); } catch (Exception e) { return ResultContainer.CreateFailResult(e.Message); } } // Explicit impl: base Update returns Result; the service contract returns the persisted // DTO so the CMS edit flow reads back DB-authoritative values. async Task> ITrackService.Update(TrackDto track) { try { await Repository.UpdateAsync(TrackConverter.Convert(track)); var updated = await Repository.GetByIdAsync(track.Id); return updated is not null ? ResultContainer.CreatePassResult(TrackConverter.Convert(updated)) : ResultContainer.CreateFailResult("Track not found after update."); } catch (Exception e) { return ResultContainer.CreateFailResult(e.Message); } } // Delete(long) → Result is inherited from Manager<> and satisfies ITrackService.Delete // by signature. No override. }