using System.Text.Json;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace JobTrackerApi.Controllers;

// Phase 5 — the AI Workspace for one job application. Every module runs through ISummarizerService and
// is stored as append-only history; nothing is applied automatically. docs/architecture/ai-career-assistant.md.
[ApiController]
[Route("api/jobapplications/{jobId:int}/ai")]
[Authorize(AuthenticationSchemes = "local")]
public sealed class AiWorkspaceController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _users;
    private readonly IAiWorkspaceService _workspace;
    private readonly IConfiguration _config;
    private readonly JobTrackerApi.Data.JobTrackerContext? _db;
    private readonly AiUsageMeter? _usage;
    private readonly AiUsageExecutionScope? _usageScope;

    public AiWorkspaceController(UserManager<ApplicationUser> users, IAiWorkspaceService workspace, IConfiguration config, JobTrackerApi.Data.JobTrackerContext? db = null, AiUsageMeter? usage = null, AiUsageExecutionScope? usageScope = null)
    {
        _users = users;
        _workspace = workspace;
        _config = config;
        _db = db;
        _usage = usage;
        _usageScope = usageScope;
    }

    public sealed record GenerateRequest(
        string Module,
        string? Mode,
        string? ExtraContext,
        string? TargetLanguage = null,
        string? CurrentText = null,
        string? Action = null);
    public sealed record InteractionDto(int Id, string Module, string? Mode, string Title, string Provider, JsonElement Result, int InputCharacterCount, int OutputCharacterCount, int EstimatedTokenCount, DateTimeOffset CreatedAtUtc);

    [HttpGet("modules")]
    public ActionResult<object> Modules() => Ok(new { modules = _workspace.Modules, provider = ResolveProvider() });

    [HttpPost("generate")]
    [Authorize(Policy = ProEntitlement.Policy)]
    public async Task<ActionResult<InteractionDto>> Generate(int jobId, [FromBody] GenerateRequest request, CancellationToken ct)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();
        if (string.IsNullOrWhiteSpace(request?.Module)) return BadRequest("Choose an AI module.");

        AiUsageReservation? reservation = null;
        if (_db is not null)
        {
            var roles = await _users.GetRolesAsync(user);
            var entitlements = AccountPlans.ForRoles(roles);
            var usage = _usage ?? new AiUsageMeter(_db, TimeProvider.System);
            var estimate = AiUsageMeter.ReservationFor($"workspace.{request.Module.Trim().ToLowerInvariant()}");
            try
            {
                reservation = await usage.ReserveAsync(
                    user.Id,
                    entitlements,
                    "workspace",
                    Guid.NewGuid().ToString("D"),
                    $"workspace.{request.Module.Trim().ToLowerInvariant()}",
                    estimate.InputCharacters,
                    estimate.EstimatedTokens,
                    ct);
            }
            catch (AiUsageLimitException ex)
            {
                return StatusCode(StatusCodes.Status429TooManyRequests, ex.Message);
            }
        }

        try
        {
            using var metering = _usageScope?.Suppress();
            var interaction = await _workspace.GenerateAsync(
                user.Id, jobId, user.ProfileCvText ?? string.Empty, ResolveName(user),
                new AiGenerateRequest(request.Module, request.Mode, request.ExtraContext, request.TargetLanguage, request.CurrentText, request.Action), ResolveProvider(), ct);
            if (interaction is null)
            {
                if (reservation is not null) await (_usage ?? new AiUsageMeter(_db!, TimeProvider.System)).ReleaseAsync(reservation.Record.Id, ct);
                return NotFound();
            }
            if (reservation is not null)
                await (_usage ?? new AiUsageMeter(_db!, TimeProvider.System)).FinalizeAsync(
                    reservation.Record.Id, interaction.InputCharacterCount, interaction.OutputCharacterCount, ct);
            return Ok(ToDto(interaction));
        }
        catch (ArgumentException ex)
        {
            if (reservation is not null) await (_usage ?? new AiUsageMeter(_db!, TimeProvider.System)).ReleaseAsync(reservation.Record.Id, ct);
            return BadRequest(ex.Message);
        }
        catch (AiUnavailableException ex)
        {
            return StatusCode(StatusCodes.Status502BadGateway, ex.Message);
        }
    }

    [HttpGet("history")]
    public async Task<ActionResult<IEnumerable<InteractionDto>>> History(int jobId, [FromQuery] string? module, CancellationToken ct)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();
        var history = await _workspace.HistoryAsync(user.Id, jobId, module, ct);
        return Ok(history.Select(ToDto));
    }

    [HttpDelete("history/{id:int}")]
    public async Task<IActionResult> Delete(int jobId, int id, CancellationToken ct)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();
        return await _workspace.DeleteAsync(user.Id, id, ct) ? NoContent() : NotFound();
    }

    private string ResolveProvider() =>
        _config["Ai:Provider"] ?? Environment.GetEnvironmentVariable("AI_PROVIDER") ?? "ai-service";

    private static string ResolveName(ApplicationUser user)
    {
        var name = string.Join(" ", new[] { user.FirstName?.Trim(), user.LastName?.Trim() }.Where(x => !string.IsNullOrWhiteSpace(x)));
        if (string.IsNullOrWhiteSpace(name)) name = user.DisplayName?.Trim();
        if (string.IsNullOrWhiteSpace(name)) name = user.UserName?.Trim();
        return name ?? string.Empty;
    }

    private static InteractionDto ToDto(AiInteraction x) => new(
        x.Id, x.Module, x.Mode, x.Title, x.Provider,
        JsonSerializer.Deserialize<JsonElement>(string.IsNullOrWhiteSpace(x.ResultJson) ? "{}" : x.ResultJson),
        x.InputCharacterCount, x.OutputCharacterCount, x.EstimatedTokenCount, x.CreatedAtUtc);
}
