using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Common.Enums;
using TakeoutSaaS.Domain.Merchants.Entities;
using TakeoutSaaS.Domain.Merchants.Enums;
using TakeoutSaaS.Domain.Merchants.Repositories;
using TakeoutSaaS.Infrastructure.App.Persistence;
using TakeoutSaaS.Infrastructure.Logs.Persistence;

namespace TakeoutSaaS.Infrastructure.App.Repositories;

/// <summary>
/// 商户聚合的 EF Core 仓储实现。
/// </summary>
/// <remarks>
/// 初始化仓储。
/// </remarks>
public sealed class EfMerchantRepository(TakeoutAppDbContext context, TakeoutLogsDbContext logsContext) : IMerchantRepository
{
    /// <inheritdoc />
    public Task<Merchant?> FindByIdAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
    {
        return context.Merchants
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.Id == merchantId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task<Merchant?> GetForUpdateAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
    {
        return context.Merchants
            .Where(x => x.TenantId == tenantId && x.Id == merchantId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<Merchant>> SearchAsync(long tenantId, MerchantStatus? status, CancellationToken cancellationToken = default)
    {
        var query = context.Merchants
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId);

        if (status.HasValue)
        {
            query = query.Where(x => x.Status == status.Value);
        }

        return await query
            .OrderByDescending(x => x.CreatedAt)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantStaff>> GetStaffAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
    {
        var staffs = await context.MerchantStaff
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
            .OrderBy(x => x.Name)
            .ToListAsync(cancellationToken);

        return staffs;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantStaff>> GetStaffByStoreAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
    {
        var staffs = await context.MerchantStaff
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.StoreId == storeId)
            .OrderBy(x => x.Name)
            .ToListAsync(cancellationToken);

        return staffs;
    }

    /// <inheritdoc />
    public Task<MerchantStaff?> FindStaffByIdAsync(long staffId, long tenantId, CancellationToken cancellationToken = default)
    {
        return context.MerchantStaff
            .Where(x => x.TenantId == tenantId && x.Id == staffId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantContract>> GetContractsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
    {
        var contracts = await context.MerchantContracts
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
            .OrderByDescending(x => x.CreatedAt)
            .ToListAsync(cancellationToken);

        return contracts;
    }

    /// <inheritdoc />
    public Task<MerchantContract?> FindContractByIdAsync(long merchantId, long tenantId, long contractId, CancellationToken cancellationToken = default)
    {
        return context.MerchantContracts
            .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId && x.Id == contractId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantDocument>> GetDocumentsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
    {
        var documents = await context.MerchantDocuments
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
            .OrderBy(x => x.CreatedAt)
            .ToListAsync(cancellationToken);

        return documents;
    }

    /// <inheritdoc />
    public Task<MerchantDocument?> FindDocumentByIdAsync(long merchantId, long tenantId, long documentId, CancellationToken cancellationToken = default)
    {
        return context.MerchantDocuments
            .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId && x.Id == documentId)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task AddMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default)
    {
        return context.Merchants.AddAsync(merchant, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task AddStaffAsync(MerchantStaff staff, CancellationToken cancellationToken = default)
    {
        return context.MerchantStaff.AddAsync(staff, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task AddContractAsync(MerchantContract contract, CancellationToken cancellationToken = default)
    {
        return context.MerchantContracts.AddAsync(contract, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task UpdateContractAsync(MerchantContract contract, CancellationToken cancellationToken = default)
    {
        context.MerchantContracts.Update(contract);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public Task AddDocumentAsync(MerchantDocument document, CancellationToken cancellationToken = default)
    {
        return context.MerchantDocuments.AddAsync(document, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public Task UpdateDocumentAsync(MerchantDocument document, CancellationToken cancellationToken = default)
    {
        context.MerchantDocuments.Update(document);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        // 1. 保存业务库变更
        await context.SaveChangesAsync(cancellationToken);

        // 2. 保存日志库变更
        await logsContext.SaveChangesAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task UpdateMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default)
    {
        context.Merchants.Update(merchant);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public async Task DeleteMerchantAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
    {
        var existing = await context.Merchants
            .Where(x => x.TenantId == tenantId && x.Id == merchantId)
            .FirstOrDefaultAsync(cancellationToken);

        if (existing == null)
        {
            return;
        }

        context.Merchants.Remove(existing);
    }

    /// <inheritdoc />
    public async Task DeleteStaffAsync(long staffId, long tenantId, CancellationToken cancellationToken = default)
    {
        var existing = await context.MerchantStaff
            .Where(x => x.TenantId == tenantId && x.Id == staffId)
            .FirstOrDefaultAsync(cancellationToken);

        if (existing == null)
        {
            return;
        }

        context.MerchantStaff.Remove(existing);
    }

    /// <inheritdoc />
    public Task AddAuditLogAsync(MerchantAuditLog log, CancellationToken cancellationToken = default)
    {
        return logsContext.MerchantAuditLogs.AddAsync(log, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantAuditLog>> GetAuditLogsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
    {
        return await logsContext.MerchantAuditLogs
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
            .OrderByDescending(x => x.CreatedAt)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<Merchant>> SearchAsync(
        long tenantId,
        MerchantStatus? status,
        OperatingMode? operatingMode,
        string? keyword,
        CancellationToken cancellationToken = default)
    {
        var query = context.Merchants
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId)
            .AsQueryable();

        if (status.HasValue)
        {
            query = query.Where(x => x.Status == status.Value);
        }

        if (operatingMode.HasValue)
        {
            query = query.Where(x => x.OperatingMode == operatingMode.Value);
        }

        if (!string.IsNullOrWhiteSpace(keyword))
        {
            var normalized = keyword.Trim();
            query = query.Where(x =>
                EF.Functions.ILike(x.BrandName, $"%{normalized}%") ||
                EF.Functions.ILike(x.BusinessLicenseNumber ?? string.Empty, $"%{normalized}%"));
        }

        return await query
            .OrderByDescending(x => x.CreatedAt)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Task AddChangeLogAsync(MerchantChangeLog log, CancellationToken cancellationToken = default)
    {
        return logsContext.MerchantChangeLogs.AddAsync(log, cancellationToken).AsTask();
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantChangeLog>> GetChangeLogsAsync(
        long merchantId,
        long tenantId,
        string? fieldName = null,
        CancellationToken cancellationToken = default)
    {
        var query = logsContext.MerchantChangeLogs
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId);

        if (!string.IsNullOrWhiteSpace(fieldName))
        {
            query = query.Where(x => x.FieldName == fieldName);
        }

        return await query
            .OrderByDescending(x => x.CreatedAt)
            .ToListAsync(cancellationToken);
    }
}
