using Meezi.API.Models.Orders;
using Meezi.API.Models.Tables;
using Meezi.API.Services;
using Meezi.Core.Entities;
using Meezi.Core.Enums;
using Meezi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Xunit;

namespace Meezi.API.Tests;

public class BranchTableTests
{
    private sealed class NullKdsNotifier : IKdsNotifier
    {
        public Task NotifyOrderCreatedAsync(string cafeId, LiveOrderDto order, CancellationToken cancellationToken = default) =>
            Task.CompletedTask;
        public Task NotifyOrderStatusChangedAsync(string cafeId, string orderId, OrderStatus status, CancellationToken cancellationToken = default) =>
            Task.CompletedTask;
        public Task NotifyTableStatusChangedAsync(string cafeId, CancellationToken cancellationToken = default) =>
            Task.CompletedTask;
    }

    private static (AppDbContext Db, TableService Tables, string CafeId, string BranchA, string BranchB, string ManagerId)
        CreateFixture()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .Options;
        var db = new AppDbContext(options);
        var config = new Microsoft.Extensions.Configuration.ConfigurationBuilder().Build();
        var tables = new TableService(db, config, new NullKdsNotifier(), new BranchIdentityService(db));

        var cafeId = "cafe-1";
        var branchA = "branch-a";
        var branchB = "branch-b";
        var managerId = "mgr-1";

        db.Cafes.Add(new Cafe { Id = cafeId, Name = "Test", Slug = "test", PlanTier = PlanTier.Pro });
        db.Branches.AddRange(
            new Branch { Id = branchA, CafeId = cafeId, Name = "A", IsActive = true, UpdatedAt = DateTime.UtcNow },
            new Branch { Id = branchB, CafeId = cafeId, Name = "B", IsActive = true, UpdatedAt = DateTime.UtcNow });
        db.Employees.Add(new Employee
        {
            Id = managerId,
            CafeId = cafeId,
            BranchId = branchA,
            Name = "Manager",
            Phone = "09121111111",
            Role = EmployeeRole.Manager
        });
        db.Tables.AddRange(
            new Table { Id = "t-a", CafeId = cafeId, BranchId = branchA, Number = "1", QrCode = "qr-a" },
            new Table { Id = "t-b", CafeId = cafeId, BranchId = branchB, Number = "2", QrCode = "qr-b" });
        db.SaveChanges();

        return (db, tables, cafeId, branchA, branchB, managerId);
    }

    [Fact]
    public async Task GetTables_ReturnsBranchTablesOnly()
    {
        var (_, tables, cafeId, branchA, _, _) = CreateFixture();
        var result = await tables.GetBranchTablesAsync(cafeId, branchA);
        Assert.NotNull(result);
        Assert.Single(result);
        Assert.Equal("t-a", result[0].Id);
    }

    [Fact]
    public async Task GetTables_DoesNotReturnOtherBranchTables()
    {
        var (_, tables, cafeId, branchA, _, _) = CreateFixture();
        var result = await tables.GetBranchTablesAsync(cafeId, branchA);
        Assert.NotNull(result);
        Assert.DoesNotContain(result, t => t.Id == "t-b");
    }

    [Fact]
    public async Task CreateTable_AssignsToBranch()
    {
        var (db, tables, cafeId, branchA, _, _) = CreateFixture();
        var created = await tables.CreateBranchTableAsync(
            cafeId,
            branchA,
            new CreateBranchTableRequest("9", 6));
        Assert.True(created.Success);
        Assert.Equal(branchA, created.Data!.BranchId);
        Assert.Equal(1, await db.Tables.CountAsync(t => t.BranchId == branchA && t.Number == "9"));
    }

    [Fact]
    public async Task DeleteTable_WithOpenOrder_ReturnsTableHasOpenOrder()
    {
        var (db, tables, cafeId, branchA, _, _) = CreateFixture();
        db.Orders.Add(new Order
        {
            Id = "ord-1",
            CafeId = cafeId,
            BranchId = branchA,
            TableId = "t-a",
            Status = OrderStatus.Preparing,
            OrderType = OrderType.DineIn,
            Subtotal = 100,
            Total = 100
        });
        await db.SaveChangesAsync();

        var result = await tables.DeleteBranchTableAsync(cafeId, branchA, "t-a");
        Assert.False(result.Success);
        Assert.Equal("TABLE_HAS_OPEN_ORDER", result.ErrorCode);
    }

    [Fact]
    public async Task DeleteSection_WithTables_ReturnsTableSectionHasTables()
    {
        var (db, tables, cafeId, branchA, _, _) = CreateFixture();
        var section = new TableSection
        {
            Id = "sec-1",
            CafeId = cafeId,
            BranchId = branchA,
            Name = "Hall"
        };
        db.TableSections.Add(section);
        var table = await db.Tables.FirstAsync(t => t.Id == "t-a");
        table.SectionId = section.Id;
        await db.SaveChangesAsync();

        var result = await tables.DeleteBranchSectionAsync(cafeId, branchA, section.Id);
        Assert.False(result.Success);
        Assert.Equal("TABLE_SECTION_HAS_TABLES", result.ErrorCode);
    }

    [Fact]
    public async Task ManagerCannotAccessOtherBranchTables()
    {
        var (_, tables, cafeId, _, branchB, managerId) = CreateFixture();
        var allowed = await tables.CanAccessBranchAsync(
            cafeId, branchB, managerId, EmployeeRole.Manager);
        Assert.False(allowed);
    }
}
