using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Library;
using TeleWave.Application.Library.AddEpisode;
using TeleWave.Application.Library.CreateShow;
using TeleWave.Application.Library.RemoveEpisode;
using TeleWave.Application.Library.RenameShow;
using TeleWave.Application.Library.SetShowOriginalName;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using Xunit;

namespace TeleWave.Application.Tests.Library;

public class ShowHandlersTests
{
    [Fact]
    public async Task CreateShow_PersistsShow()
    {
        var fixture = new TestDb();
        await using var db = fixture.New();

        var result = await new CreateShowCommandHandler(db).Handle(
            new CreateShowCommand("Симпсоны", ShowKind.Series, "desc", "The Simpsons"),
            CancellationToken.None
        );
        Assert.True(result.IsSuccess);
        await db.SaveChangesAsync(CancellationToken.None);

        await using var verify = fixture.New();
        var stored = await verify.Shows.FindAsync(result.Value);
        Assert.Equal("Симпсоны", stored!.Name);
        Assert.Equal("The Simpsons", stored.OriginalName);
    }

    [Fact]
    public async Task RenameShow_ChangesName_OrNotFound()
    {
        var fixture = new TestDb();
        var show = Show.Create("Old", ShowKind.Series);
        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var missing = await new RenameShowCommandHandler(db).Handle(
            new RenameShowCommand(Guid.NewGuid(), "New"),
            CancellationToken.None
        );
        Assert.Equal(ShowErrors.NotFound, missing.Error);

        var ok = await new RenameShowCommandHandler(db).Handle(
            new RenameShowCommand(show.Id, "  New  "),
            CancellationToken.None
        );
        Assert.True(ok.IsSuccess);
        await db.SaveChangesAsync(CancellationToken.None);

        await using var verify = fixture.New();
        Assert.Equal("New", (await verify.Shows.FindAsync(show.Id))!.Name);
    }

    [Fact]
    public async Task SetOriginalName_TrimsAndClears()
    {
        var fixture = new TestDb();
        var show = Show.Create("A", ShowKind.Series);
        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var ok = await new SetShowOriginalNameCommandHandler(db).Handle(
            new SetShowOriginalNameCommand(show.Id, "  Orig "),
            CancellationToken.None
        );
        Assert.True(ok.IsSuccess);
        await db.SaveChangesAsync(CancellationToken.None);

        await using var verify = fixture.New();
        Assert.Equal("Orig", (await verify.Shows.FindAsync(show.Id))!.OriginalName);
    }

    [Fact]
    public async Task AddEpisode_ParsesNumbers_AndGuardsSingle()
    {
        var fixture = new TestDb();
        var series = Show.Create("Series", ShowKind.Series);
        var single = Show.Create("Movie", ShowKind.Single);
        single.AddEpisode(Guid.NewGuid()); // уже занята единственная серия
        var asset = MediaAsset.Register("The.Show.S03E07.WEBDL.mkv", ".mkv", MediaSource.Upload);
        await using (var seed = fixture.New())
        {
            seed.Shows.Add(series);
            seed.Shows.Add(single);
            seed.MediaAssets.Add(asset);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var handler = new AddEpisodeCommandHandler(db);

        // неизвестный ассет
        var noAsset = await handler.Handle(
            new AddEpisodeCommand(series.Id, Guid.NewGuid()),
            CancellationToken.None
        );
        Assert.Equal(ShowErrors.AssetNotFound, noAsset.Error);

        // single уже имеет серию
        var singleGuard = await handler.Handle(
            new AddEpisodeCommand(single.Id, asset.Id),
            CancellationToken.None
        );
        Assert.Equal(ShowErrors.SingleAlreadyHasEpisode, singleGuard.Error);

        // ок + распознавание S03E07
        var ok = await handler.Handle(
            new AddEpisodeCommand(series.Id, asset.Id),
            CancellationToken.None
        );
        Assert.True(ok.IsSuccess);
        await db.SaveChangesAsync(CancellationToken.None);

        await using var verify = fixture.New();
        var stored = await verify.Shows.Include(s => s.Episodes).FirstAsync(s => s.Id == series.Id);
        var episode = Assert.Single(stored.Episodes);
        Assert.Equal(3, episode.Season);
        Assert.Equal(7, episode.Episode);
    }

    [Fact]
    public async Task RemoveEpisode_RemovesOrReportsNotFound()
    {
        var fixture = new TestDb();
        var show = Show.Create("Series", ShowKind.Series);
        var ep = show.AddEpisode(Guid.NewGuid());
        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var handler = new RemoveEpisodeCommandHandler(db);

        var missing = await handler.Handle(
            new RemoveEpisodeCommand(show.Id, Guid.NewGuid()),
            CancellationToken.None
        );
        Assert.Equal(ShowErrors.EpisodeNotFound, missing.Error);

        var ok = await handler.Handle(
            new RemoveEpisodeCommand(show.Id, ep.Id),
            CancellationToken.None
        );
        Assert.True(ok.IsSuccess);
    }
}
