using System.Text.Json;
using JobTrackerApi.Data;
using JobTrackerApi.Models;

namespace JobTrackerApi.Services;

// Phase 3: two-way projection between the relational CareerProfile children and the
// StructuredCvProfile shape (the derived blob + the frontend/AI edit unit).
// See docs/architecture/career-profile-model.md.
//
// Relational holds: Experience, Education, Skill, Project, Certification, Language.
// LongTailJson holds the rest of StructuredCvProfile: Contact, Summary, Interests, OtherSections,
// Sections, Metadata (and future achievements/orgs/pubs/courses).
public static class CareerProfileMapper
{
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);

    /// <summary>The non-relational parts of a StructuredCvProfile, stored as CareerProfile.LongTailJson.</summary>
    public sealed class CareerLongTail
    {
        public string Version { get; set; } = "1";
        public StructuredCvMetadata Metadata { get; set; } = new();
        public StructuredCvContact Contact { get; set; } = new();
        public List<string> Summary { get; set; } = new();
        public List<string> Interests { get; set; } = new();
        public List<string> Awards { get; set; } = new();
        public List<string> Publications { get; set; } = new();
        public List<string> Organisations { get; set; } = new();
        public List<string> References { get; set; } = new();
        public List<StructuredCvOtherSection> OtherSections { get; set; } = new();
        public List<StructuredCvSection> Sections { get; set; } = new();
    }

    public static string SerializeLongTail(StructuredCvProfile p)
        => JsonSerializer.Serialize(new CareerLongTail
        {
            Version = p.Version,
            Metadata = p.Metadata,
            Contact = p.Contact,
            Summary = p.Summary,
            Interests = p.Interests,
            Awards = p.Awards,
            Publications = p.Publications,
            Organisations = p.Organisations,
            References = p.References,
            OtherSections = p.OtherSections,
            Sections = p.Sections,
        }, JsonOptions);

    private static CareerLongTail DeserializeLongTail(string? json)
    {
        if (string.IsNullOrWhiteSpace(json)) return new CareerLongTail();
        try { return JsonSerializer.Deserialize<CareerLongTail>(json, JsonOptions) ?? new CareerLongTail(); }
        catch { return new CareerLongTail(); }
    }

    // --- StructuredCvProfile -> relational rows (insert) ------------------------------------------

    public static void AddChildren(JobTrackerContext db, int careerProfileId, string ownerUserId, StructuredCvProfile p)
    {
        int order = 0;
        foreach (var j in p.Jobs)
            db.CareerExperiences.Add(new CareerExperience
            {
                CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(j.Id), SortOrder = order++,
                Title = j.Title, Company = j.Company, Location = j.Location,
                Start = j.Start, End = j.End, StartDate = j.StartDate, EndDate = j.EndDate, IsCurrent = j.IsCurrent,
                Bullets = j.Bullets, Skills = j.Skills,
            });

        order = 0;
        foreach (var e in p.Education)
            db.CareerEducations.Add(new CareerEducation
            {
                CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(e.Id), SortOrder = order++,
                Qualification = e.Qualification, QualificationLevel = e.QualificationLevel, Institution = e.Institution,
                Location = e.Location, Start = e.Start, End = e.End, StartDate = e.StartDate, EndDate = e.EndDate,
                Details = e.Details,
            });

        order = 0;
        foreach (var s in p.Skills)
            db.CareerSkills.Add(new CareerSkill
            {
                CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(null), SortOrder = order++,
                Name = s,
            });

        order = 0;
        foreach (var pr in p.Projects)
            db.CareerProjects.Add(new CareerProject
            {
                CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(pr.Id), SortOrder = order++,
                Name = pr.Name, Role = pr.Role, Location = pr.Location,
                Start = pr.Start, End = pr.End, StartDate = pr.StartDate, EndDate = pr.EndDate,
                Bullets = pr.Bullets, Skills = pr.Skills,
            });

        order = 0;
        foreach (var c in p.Certifications)
            db.CareerCertifications.Add(new CareerCertification
            {
                CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(c.Id), SortOrder = order++,
                Name = c.Name, Issuer = c.Issuer, Location = c.Location, Date = c.Date, DateNormalized = c.DateNormalized,
                Details = c.Details,
            });

        order = 0;
        foreach (var l in p.Languages)
            db.CareerLanguages.Add(new CareerLanguage
            {
                CareerProfileId = careerProfileId, OwnerUserId = ownerUserId, ItemKey = KeyOf(null), SortOrder = order++,
                Name = l.Name, Level = l.Level, Notes = l.Notes,
            });
    }

    // --- relational rows -> StructuredCvProfile (read) -------------------------------------------

    public static StructuredCvProfile ToStructured(
        string? longTailJson,
        List<CareerExperience> experiences,
        List<CareerEducation> education,
        List<CareerSkill> skills,
        List<CareerProject> projects,
        List<CareerCertification> certifications,
        List<CareerLanguage> languages)
    {
        var tail = DeserializeLongTail(longTailJson);
        return new StructuredCvProfile
        {
            Version = tail.Version,
            Metadata = tail.Metadata,
            Contact = tail.Contact,
            Summary = tail.Summary,
            Interests = tail.Interests,
            Awards = tail.Awards,
            Publications = tail.Publications,
            Organisations = tail.Organisations,
            References = tail.References,
            OtherSections = tail.OtherSections,
            Sections = tail.Sections,
            Jobs = experiences.Select(x => new StructuredCvJob
            {
                Id = NullIfEmpty(x.ItemKey), Title = x.Title, Company = x.Company, Location = x.Location,
                Start = x.Start, End = x.End, StartDate = x.StartDate, EndDate = x.EndDate, IsCurrent = x.IsCurrent,
                Bullets = x.Bullets, Skills = x.Skills,
            }).ToList(),
            Education = education.Select(x => new StructuredCvEducation
            {
                Id = NullIfEmpty(x.ItemKey), Qualification = x.Qualification, QualificationLevel = x.QualificationLevel,
                Institution = x.Institution, Location = x.Location, Start = x.Start, End = x.End,
                StartDate = x.StartDate, EndDate = x.EndDate, Details = x.Details,
            }).ToList(),
            Skills = skills.Select(x => x.Name ?? string.Empty).Where(x => x.Length > 0).ToList(),
            Projects = projects.Select(x => new StructuredCvProject
            {
                Id = NullIfEmpty(x.ItemKey), Name = x.Name, Role = x.Role, Location = x.Location,
                Start = x.Start, End = x.End, StartDate = x.StartDate, EndDate = x.EndDate,
                Bullets = x.Bullets, Skills = x.Skills,
            }).ToList(),
            Certifications = certifications.Select(x => new StructuredCvCertification
            {
                Id = NullIfEmpty(x.ItemKey), Name = x.Name, Issuer = x.Issuer, Location = x.Location,
                Date = x.Date, DateNormalized = x.DateNormalized, Details = x.Details,
            }).ToList(),
            Languages = languages.Select(x => new StructuredCvLanguage
            {
                Name = x.Name, Level = x.Level, Notes = x.Notes,
            }).ToList(),
        };
    }

    private static string KeyOf(string? id) => string.IsNullOrWhiteSpace(id) ? Guid.NewGuid().ToString("N")[..12] : id;
    private static string? NullIfEmpty(string? s) => string.IsNullOrWhiteSpace(s) ? null : s;
}
