using System.Security.Claims;
using JobTrackerApi.Controllers;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using JobTrackerApi.Tests.TestSupport;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;

namespace JobTrackerApi.Tests;

// Interview prep persistence (career-workspace-implementation-roadmap.md Phase F5): the AI call
// used to re-run on every tab open. These tests lock in that a note is generated once, reused on
// subsequent reads, and only regenerated when the attachment context changes or a refresh is
// explicitly requested.
public sealed class InterviewPrepPersistenceTests
{
    [Fact]
    public async Task GetInterviewPrep_persists_and_reuses_the_generated_note()
    {
        await using var db = TestHostFactory.CreateInMemoryDb();
        var job = await SeedJobAsync(db);

        var summarizer = new Mock<ISummarizerService>();
        summarizer.SetupSequence(x => x.SummarizeSectionAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
            .ReturnsAsync("First generated brief.")
            .ReturnsAsync("Second generated brief.");

        var controller = CreateController(db, summarizer.Object, "user-1");

        var first = await controller.GetInterviewPrep(job.Id, null, false, CancellationToken.None);
        Assert.Equal("First generated brief.", GetDto(first).Summary);

        var second = await controller.GetInterviewPrep(job.Id, null, false, CancellationToken.None);
        Assert.Equal("First generated brief.", GetDto(second).Summary);

        summarizer.Verify(x => x.SummarizeSectionAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()), Times.Once);

        var stored = Assert.Single(db.InterviewPrepNotes.IgnoreQueryFilters().Where(x => x.JobApplicationId == job.Id));
        Assert.Equal("First generated brief.", stored.Summary);
    }

    [Fact]
    public async Task GetInterviewPrep_regenerates_when_refresh_is_requested()
    {
        await using var db = TestHostFactory.CreateInMemoryDb();
        var job = await SeedJobAsync(db);

        var summarizer = new Mock<ISummarizerService>();
        summarizer.SetupSequence(x => x.SummarizeSectionAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
            .ReturnsAsync("First generated brief.")
            .ReturnsAsync("Refreshed brief.");

        var controller = CreateController(db, summarizer.Object, "user-1");

        await controller.GetInterviewPrep(job.Id, null, false, CancellationToken.None);
        var refreshed = await controller.GetInterviewPrep(job.Id, null, true, CancellationToken.None);

        Assert.Equal("Refreshed brief.", GetDto(refreshed).Summary);
        summarizer.Verify(x => x.SummarizeSectionAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(2));

        var stored = Assert.Single(db.InterviewPrepNotes.IgnoreQueryFilters().Where(x => x.JobApplicationId == job.Id));
        Assert.Equal("Refreshed brief.", stored.Summary);
    }

    [Fact]
    public async Task GetInterviewPrep_regenerates_when_attachment_selection_changes()
    {
        await using var db = TestHostFactory.CreateInMemoryDb();
        var job = await SeedJobAsync(db);

        var summarizer = new Mock<ISummarizerService>();
        summarizer.SetupSequence(x => x.SummarizeSectionAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
            .ReturnsAsync("No-attachment brief.")
            .ReturnsAsync("With-attachment brief.");

        var controller = CreateController(db, summarizer.Object, "user-1");

        await controller.GetInterviewPrep(job.Id, null, false, CancellationToken.None);
        var withAttachment = await controller.GetInterviewPrep(job.Id, "7", false, CancellationToken.None);

        Assert.Equal("With-attachment brief.", GetDto(withAttachment).Summary);
        summarizer.Verify(x => x.SummarizeSectionAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(2));
    }

    private static InterviewPrepDto GetDto(ActionResult<InterviewPrepDto> result)
        => (InterviewPrepDto)Assert.IsType<OkObjectResult>(result.Result).Value!;

    private static async Task<JobApplication> SeedJobAsync(JobTrackerContext db)
    {
        var company = new Company { Name = "Acme", OwnerUserId = "user-1" };
        db.Companies.Add(company);
        await db.SaveChangesAsync();

        var job = new JobApplication
        {
            JobTitle = "Backend Developer",
            CompanyId = company.Id,
            OwnerUserId = "user-1",
            Description = "Needs .NET and SQL experience.",
        };
        db.JobApplications.Add(job);
        await db.SaveChangesAsync();
        return job;
    }

    private static JobApplicationsController CreateController(JobTrackerContext db, ISummarizerService summarizer, string userId)
    {
        var controller = new JobApplicationsController(
            db,
            summarizer,
            TestHostFactory.CreateUserManager(null).Object,
            Mock.Of<ICvTemplateRenderer>(),
            Mock.Of<ICvPdfExporter>());
        controller.ControllerContext = new ControllerContext
        {
            HttpContext = new DefaultHttpContext
            {
                User = new ClaimsPrincipal(new ClaimsIdentity(new[]
                {
                    new Claim(ClaimTypes.NameIdentifier, userId)
                }, "test"))
            }
        };
        return controller;
    }
}
