using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json; namespace JobTrackerApi.Models; // Phase 3: relational children of CareerProfile — the editable master career profile. // See docs/architecture/career-profile-model.md. // // Design notes shared by all child entities: // - OwnerUserId is denormalized so the tenant global query filter applies directly (same pattern // as every other owned entity). // - ItemKey carries the stable item id from the StructuredCvProfile blob, so a row keeps its // identity across imports/edits and future CV variants can reference "this experience". // - SortOrder makes ordering explicit (the blob relied on array position). // - List fields persist as JSON string columns (plain TEXT — reconciler-friendly on both SQLite // and MySQL) exposed via [NotMapped] accessors. Children are replaced wholesale on save, so // fine-grained change tracking of the lists is not needed. /// Base fields every CareerProfile child shares. public abstract class CareerChildEntity { public int Id { get; set; } public int CareerProfileId { get; set; } public CareerProfile? CareerProfile { get; set; } public string OwnerUserId { get; set; } = string.Empty; /// Stable identity carried from StructuredCvProfile so references survive edits. public string ItemKey { get; set; } = string.Empty; public int SortOrder { get; set; } } internal static class CareerJson { public static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web); public static List ReadList(string? json) { if (string.IsNullOrWhiteSpace(json)) return new(); try { return JsonSerializer.Deserialize>(json, Options) ?? new(); } catch { return new(); } } public static string WriteList(List items) => JsonSerializer.Serialize(items ?? new(), Options); } public sealed class CareerExperience : CareerChildEntity { public string? Title { get; set; } public string? Company { get; set; } public string? Location { get; set; } // Free-text period kept alongside best-effort "YYYY-MM" normalization; neither replaces the other. public string? Start { get; set; } public string? End { get; set; } public string? StartDate { get; set; } public string? EndDate { get; set; } public bool IsCurrent { get; set; } public string BulletsJson { get; set; } = "[]"; public string SkillsJson { get; set; } = "[]"; [NotMapped] public List Bullets { get => CareerJson.ReadList(BulletsJson); set => BulletsJson = CareerJson.WriteList(value); } [NotMapped] public List Skills { get => CareerJson.ReadList(SkillsJson); set => SkillsJson = CareerJson.WriteList(value); } } public sealed class CareerEducation : CareerChildEntity { public string? Qualification { get; set; } public string? QualificationLevel { get; set; } public string? Institution { get; set; } public string? Location { get; set; } public string? Start { get; set; } public string? End { get; set; } public string? StartDate { get; set; } public string? EndDate { get; set; } public string DetailsJson { get; set; } = "[]"; [NotMapped] public List Details { get => CareerJson.ReadList(DetailsJson); set => DetailsJson = CareerJson.WriteList(value); } } public sealed class CareerSkill : CareerChildEntity { public string? Name { get; set; } public string? Category { get; set; } public string? Proficiency { get; set; } } public sealed class CareerProject : CareerChildEntity { public string? Name { get; set; } public string? Role { get; set; } public string? Location { get; set; } public string? Start { get; set; } public string? End { get; set; } public string? StartDate { get; set; } public string? EndDate { get; set; } public string BulletsJson { get; set; } = "[]"; public string SkillsJson { get; set; } = "[]"; public string LinksJson { get; set; } = "[]"; [NotMapped] public List Bullets { get => CareerJson.ReadList(BulletsJson); set => BulletsJson = CareerJson.WriteList(value); } [NotMapped] public List Skills { get => CareerJson.ReadList(SkillsJson); set => SkillsJson = CareerJson.WriteList(value); } [NotMapped] public List Links { get => CareerJson.ReadList(LinksJson); set => LinksJson = CareerJson.WriteList(value); } } public sealed class CareerCertification : CareerChildEntity { public string? Name { get; set; } public string? Issuer { get; set; } public string? Location { get; set; } public string? Date { get; set; } public string? DateNormalized { get; set; } public string DetailsJson { get; set; } = "[]"; [NotMapped] public List Details { get => CareerJson.ReadList(DetailsJson); set => DetailsJson = CareerJson.WriteList(value); } } public sealed class CareerLanguage : CareerChildEntity { public string? Name { get; set; } public string? Level { get; set; } public string? Notes { get; set; } }