using JobTrackerApi.Controllers;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using JobTrackerApi.Tests.TestSupport;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Moq;
using Xunit;

namespace JobTrackerApi.Tests;

// JobApplication.HasResume/HasCoverLetter/HasPortfolio/HasOtherAttachment are derived from
// Attachment rows (backlog Wave 3), not manually settable. These tests exercise the single
// place they're written: AttachmentsController's Purpose-change and Delete paths.
public sealed class AttachmentFlagsRecomputeTests
{
    [Fact]
    public async Task Changing_purpose_to_resume_sets_HasResume()
    {
        await using var db = TestHostFactory.CreateInMemoryDb();
        var (job, attachment) = await SeedJobWithAttachmentAsync(db, purpose: "other");
        var controller = CreateController(db, Path.GetDirectoryName(attachment.FilePath));

        var result = await controller.Rename(attachment.Id, new AttachmentsController.UpdateAttachmentRequest(null, "resume", null), CancellationToken.None);

        Assert.IsType<NoContentResult>(result);
        var updated = await db.JobApplications.SingleAsync(j => j.Id == job.Id);
        Assert.True(updated.HasResume);
        Assert.True(updated.HasOtherAttachment == false);
    }

    [Fact]
    public async Task Deleting_the_only_resume_attachment_clears_HasResume()
    {
        await using var db = TestHostFactory.CreateInMemoryDb();
        var (job, attachment) = await SeedJobWithAttachmentAsync(db, purpose: "resume");
        var controller = CreateController(db, Path.GetDirectoryName(attachment.FilePath));

        var result = await controller.Delete(attachment.Id, CancellationToken.None);

        Assert.IsType<NoContentResult>(result);
        var updated = await db.JobApplications.SingleAsync(j => j.Id == job.Id);
        Assert.False(updated.HasResume);
    }

    [Fact]
    public async Task Attachment_with_case_study_purpose_counts_as_other()
    {
        await using var db = TestHostFactory.CreateInMemoryDb();
        var (job, attachment) = await SeedJobWithAttachmentAsync(db, purpose: "resume");
        var controller = CreateController(db, Path.GetDirectoryName(attachment.FilePath));

        await controller.Rename(attachment.Id, new AttachmentsController.UpdateAttachmentRequest(null, "case-study", null), CancellationToken.None);

        var updated = await db.JobApplications.SingleAsync(j => j.Id == job.Id);
        Assert.False(updated.HasResume);
        Assert.True(updated.HasOtherAttachment);
    }

    private static async Task<(JobApplication Job, Attachment Attachment)> SeedJobWithAttachmentAsync(JobTrackerContext db, string purpose)
    {
        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" };
        db.JobApplications.Add(job);
        await db.SaveChangesAsync();

        var attachment = new Attachment
        {
            JobApplicationId = job.Id,
            FileName = "file.pdf",
            FilePath = Path.Combine(Path.GetTempPath(), $"jobtracker-attachment-test-{Guid.NewGuid():N}.pdf"),
            FileType = "application/pdf",
            FileSize = 100,
            Purpose = purpose,
        };
        db.Attachments.Add(attachment);
        await db.SaveChangesAsync();

        job.HasResume = purpose == "resume";
        job.HasOtherAttachment = purpose is not ("resume" or "cover-letter" or "portfolio");
        await db.SaveChangesAsync();

        return (job, attachment);
    }

    private static AttachmentsController CreateController(JobTrackerContext db, string? attachmentsRoot)
    {
        var tempRoot = Path.Combine(Path.GetTempPath(), $"jobtracker-attachments-tests-{Guid.NewGuid():N}");
        Directory.CreateDirectory(tempRoot);

        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["Data:Root"] = tempRoot,
                ["Data:AttachmentsRoot"] = attachmentsRoot,
            })
            .Build();

        var env = new Mock<IHostEnvironment>();
        env.SetupGet(x => x.ContentRootPath).Returns(tempRoot);
        var paths = new AppPaths(config, env.Object);

        return new AttachmentsController(paths, db)
        {
            ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }
        };
    }
}
