using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace JobTrackerApi.Controllers;

// Phase 3: the master career profile API. /career reads and writes the structured profile through
// here. The relational children (via CareerProfileService) are the source of truth; the
// ApplicationUser.ProfileCvStructureJson blob is kept in sync as a derived projection so the legacy
// read paths (CV rendering, tailoring, match-score) keep working. See
// docs/architecture/career-profile-model.md.
[ApiController]
[Route("api/career/profile")]
[Authorize]
public sealed class CareerProfileController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _users;
    private readonly ICareerProfileService _career;
    private readonly JobTrackerContext _db;

    public CareerProfileController(UserManager<ApplicationUser> users, ICareerProfileService career, JobTrackerContext db)
    {
        _users = users;
        _career = career;
        _db = db;
    }

    /// <summary>The master career profile, assembled from the relational children.</summary>
    [HttpGet]
    public async Task<ActionResult<CareerProfileDto>> Get(CancellationToken cancellationToken)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();

        var profile = await _career.LoadStructuredAsync(user.Id, cancellationToken);
        return Ok(new CareerProfileDto(profile, CareerCompleteness.Evaluate(profile), user.ProfileCvText));
    }

    /// <summary>
    /// Replaces the master career profile. Persists the relational children + an append-only
    /// version, then serializes the result back into the ProfileCvStructureJson projection so the
    /// legacy readers stay consistent. Identity fields are untouched (they belong to /profile).
    /// </summary>
    [HttpPut]
    [Authorize(AuthenticationSchemes = "local")]
    public async Task<ActionResult<CareerProfileDto>> Put([FromBody] CareerProfileSaveRequest? request, CancellationToken cancellationToken)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return StatusCode(501, "The career profile can only be edited on local accounts.");

        var profile = StructuredCvProfileJson.NormalizeForPersistence(request?.Profile);
        var error = CareerProfileValidator.Validate(profile);
        if (error is not null) return BadRequest(error);

        var saved = await _career.SaveVersionAsync(user.Id, profile, "manual", cancellationToken);

        // Keep the derived projection in sync for legacy readers. CvText (the raw imported text) is
        // part of the career profile and is set here too; identity fields are never touched.
        user.ProfileCvStructureJson = StructuredCvProfileJson.SerializePersisted(saved);
        if (request?.CvText is not null) user.ProfileCvText = string.IsNullOrWhiteSpace(request.CvText) ? null : request.CvText;
        var res = await _users.UpdateAsync(user);
        if (!res.Succeeded) return BadRequest(string.Join("; ", res.Errors.Select(e => e.Description)));

        return Ok(new CareerProfileDto(saved, CareerCompleteness.Evaluate(saved), user.ProfileCvText));
    }

    /// <summary>Just the completeness scorecard, for the /career overview.</summary>
    [HttpGet("completeness")]
    public async Task<ActionResult<CareerCompletenessDto>> Completeness(CancellationToken cancellationToken)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();
        var profile = await _career.LoadStructuredAsync(user.Id, cancellationToken);
        return Ok(CareerCompleteness.Evaluate(profile));
    }

    /// <summary>The append-only version history, newest first.</summary>
    [HttpGet("versions")]
    public async Task<ActionResult<IReadOnlyList<CareerProfileVersionInfo>>> Versions(CancellationToken cancellationToken)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();
        return Ok(await _career.ListVersionsAsync(user.Id, cancellationToken));
    }

    /// <summary>
    /// Restores a past version, non-destructively (re-saved as a new version so the restore is
    /// itself reversible). Returns the restored profile + refreshed completeness.
    /// </summary>
    [HttpPost("versions/{version:int}/restore")]
    [Authorize(AuthenticationSchemes = "local")]
    public async Task<ActionResult<CareerProfileDto>> Restore([FromRoute] int version, CancellationToken cancellationToken)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return StatusCode(501, "The career profile can only be edited on local accounts.");

        var restored = await _career.RestoreVersionAsync(user.Id, version, cancellationToken);
        if (restored is null) return NotFound($"Version {version} was not found.");

        user.ProfileCvStructureJson = StructuredCvProfileJson.SerializePersisted(restored);
        var res = await _users.UpdateAsync(user);
        if (!res.Succeeded) return BadRequest(string.Join("; ", res.Errors.Select(e => e.Description)));

        return Ok(new CareerProfileDto(restored, CareerCompleteness.Evaluate(restored), user.ProfileCvText));
    }
}

public sealed record CareerProfileSaveRequest(StructuredCvProfile? Profile, string? CvText);

public sealed record CareerProfileDto(StructuredCvProfile Profile, CareerCompletenessDto Completeness, string? CvText);

public sealed record CareerCompletenessDto(int Percent, List<string> Missing, List<CareerSectionStatusDto> Sections);

public sealed record CareerSectionStatusDto(string Key, string Label, bool Complete, int Count);
