using System.Net;
using System.Net.Http.Json;
using BigRegister.Api.Contracts;
using BigRegister.Api.Data;
using Microsoft.AspNetCore.Mvc.Testing;

namespace BigRegister.Tests;

public class ApplicationTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
{
  private readonly HttpClient _client = factory.CreateClient();

  private async Task<ApplicationDetailDto> Create(string type = "registratie")
  {
    var res = await _client.PostAsJsonAsync("/api/v1/applications", new { type });
    Assert.Equal(HttpStatusCode.Created, res.StatusCode);
    return (await res.Content.ReadFromJsonAsync<ApplicationDetailDto>())!;
  }

  private Task<List<ApplicationSummaryDto>?> List() =>
      _client.GetFromJsonAsync<List<ApplicationSummaryDto>>("/api/v1/applications");

  // --- Lifecycle over HTTP ---

  [Fact]
  public async Task Create_then_list_shows_a_concept_with_step_progress()
  {
    var a = await Create();
    await _client.PutAsJsonAsync($"/api/v1/applications/{a.Id}",
        new { draft = new { beroep = "arts" }, stepIndex = 1, stepCount = 4 });

    var mine = (await List())!.Single(x => x.Id == a.Id);
    Assert.Equal("Concept", mine.Status.Tag);
    Assert.Equal(1, mine.Status.StepIndex);
    Assert.Equal(4, mine.Status.StepCount);
  }

  [Fact]
  public async Task Draft_sync_is_readable_back_from_detail()
  {
    var a = await Create();
    await _client.PutAsJsonAsync($"/api/v1/applications/{a.Id}",
        new { draft = new { beroep = "verpleegkundige" }, stepIndex = 2, stepCount = 4 });

    var detail = await _client.GetFromJsonAsync<ApplicationDetailDto>($"/api/v1/applications/{a.Id}");
    Assert.NotNull(detail!.Draft);
    Assert.Equal("verpleegkundige", detail.Draft!.Value.GetProperty("beroep").GetString());
  }

  [Fact]
  public async Task Submit_duo_registratie_is_in_behandeling_and_auto()
  {
    var a = await Create("registratie");
    var res = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" });
    res.EnsureSuccessStatusCode();
    var body = (await res.Content.ReadFromJsonAsync<SubmitApplicationResponse>())!;
    Assert.StartsWith("BIG-2026-", body.Referentie);
    Assert.Equal("InBehandeling", body.Status.Tag);
    Assert.False(body.Status.Manual);   // auto-approvable → not a manual case
  }

  [Fact]
  public async Task Submit_handmatig_registratie_succeeds_as_manual_case()
  {
    var a = await Create("registratie");
    var res = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "handmatig" });
    res.EnsureSuccessStatusCode();   // no longer a 422
    var body = (await res.Content.ReadFromJsonAsync<SubmitApplicationResponse>())!;
    Assert.Equal("InBehandeling", body.Status.Tag);
    Assert.True(body.Status.Manual);
  }

  [Fact]
  public async Task Submit_herregistratie_with_zero_uren_is_afgewezen()
  {
    var a = await Create("herregistratie");
    var res = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { uren = 0 });
    res.EnsureSuccessStatusCode();   // the submission is accepted...
    var body = (await res.Content.ReadFromJsonAsync<SubmitApplicationResponse>())!;
    Assert.Equal("Afgewezen", body.Status.Tag);   // ...but resolves to rejected
    Assert.NotNull(body.Status.Reden);
  }

  [Fact]
  public async Task Submitting_twice_conflicts()
  {
    var a = await Create("registratie");
    (await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" })).EnsureSuccessStatusCode();
    var again = await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" });
    Assert.Equal(HttpStatusCode.Conflict, again.StatusCode);
  }

  [Fact]
  public async Task Cancel_concept_removes_it()
  {
    var a = await Create();
    Assert.Equal(HttpStatusCode.NoContent, (await _client.DeleteAsync($"/api/v1/applications/{a.Id}")).StatusCode);
    Assert.Equal(HttpStatusCode.NotFound, (await _client.GetAsync($"/api/v1/applications/{a.Id}")).StatusCode);
  }

  [Fact]
  public async Task Cancel_submitted_aanvraag_conflicts()
  {
    var a = await Create("registratie");
    (await _client.PostAsJsonAsync($"/api/v1/applications/{a.Id}/submit", new { diplomaHerkomst = "duo" })).EnsureSuccessStatusCode();
    Assert.Equal(HttpStatusCode.Conflict, (await _client.DeleteAsync($"/api/v1/applications/{a.Id}")).StatusCode);
  }

  // --- Auto-approval is computed on read: exercise the window boundary without waiting. ---

  private static Aanvraag Accepted(bool autoApprovable) => new()
  {
    Id = "x",
    Type = "registratie",
    Owner = "test",
    Submitted = true,
    AutoApprovable = autoApprovable,
    Referentie = "BIG-2026-1",
    SubmittedAt = DateTimeOffset.UtcNow,
    CreatedAt = DateTimeOffset.UtcNow,
    UpdatedAt = DateTimeOffset.UtcNow,
  };

  [Fact]
  public void AutoApprovable_flips_to_goedgekeurd_after_the_window()
  {
    var a = Accepted(autoApprovable: true);
    var t0 = a.SubmittedAt!.Value;
    Assert.Equal("InBehandeling", a.ToStatusDto(t0 + ApplicationStore.ProcessingWindow - TimeSpan.FromSeconds(1)).Tag);
    Assert.Equal("Goedgekeurd", a.ToStatusDto(t0 + ApplicationStore.ProcessingWindow + TimeSpan.FromSeconds(1)).Tag);
  }

  [Fact]
  public void Manual_case_never_auto_advances()
  {
    var a = Accepted(autoApprovable: false);
    var far = a.SubmittedAt!.Value + ApplicationStore.ProcessingWindow + TimeSpan.FromDays(1);
    var status = a.ToStatusDto(far);
    Assert.Equal("InBehandeling", status.Tag);
    Assert.True(status.Manual);
  }
}
