using Meezi.API.Models.Crm;
using Meezi.Core.Entities;
using Meezi.Core.Utilities;
using Meezi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace Meezi.API.Services;

public interface ICustomerService
{
    Task<IReadOnlyList<CustomerDto>> SearchAsync(string cafeId, string? query, CancellationToken cancellationToken = default);
    Task<CustomerDto?> GetAsync(string cafeId, string id, CancellationToken cancellationToken = default);
    Task<CustomerDto?> CreateAsync(string cafeId, CreateCustomerRequest request, CancellationToken cancellationToken = default);
    Task<CustomerDto?> UpdateAsync(string cafeId, string id, UpdateCustomerRequest request, CancellationToken cancellationToken = default);
    Task<bool> DeleteAsync(string cafeId, string id, CancellationToken cancellationToken = default);
}

public class CustomerService : ICustomerService
{
    private readonly AppDbContext _db;

    public CustomerService(AppDbContext db)
    {
        _db = db;
    }

    public async Task<IReadOnlyList<CustomerDto>> SearchAsync(
        string cafeId,
        string? query,
        CancellationToken cancellationToken = default)
    {
        var q = _db.Customers.Where(c => c.CafeId == cafeId && c.DeletedAt == null);

        if (!string.IsNullOrWhiteSpace(query))
        {
            var term = query.Trim();
            var normalizedPhone = PhoneNormalizer.Normalize(term);
            q = q.Where(c =>
                c.Name.Contains(term) ||
                c.Phone.Contains(term) ||
                (c.NationalId != null && c.NationalId.Contains(term)) ||
                (normalizedPhone.Length >= 10 && c.Phone.Contains(normalizedPhone)));
        }

        var list = await q
            .OrderByDescending(c => c.CreatedAt)
            .Take(100)
            .ToListAsync(cancellationToken);
        return list.Select(ToDto).ToList();
    }

    public async Task<CustomerDto?> GetAsync(string cafeId, string id, CancellationToken cancellationToken = default)
    {
        var entity = await _db.Customers
            .FirstOrDefaultAsync(c => c.Id == id && c.CafeId == cafeId && c.DeletedAt == null, cancellationToken);
        return entity is null ? null : ToDto(entity);
    }

    public async Task<CustomerDto?> CreateAsync(
        string cafeId,
        CreateCustomerRequest request,
        CancellationToken cancellationToken = default)
    {
        var phone = PhoneNormalizer.Normalize(request.Phone);
        var exists = await _db.Customers.AnyAsync(
            c => c.CafeId == cafeId && c.Phone == phone, cancellationToken);
        if (exists) return null;

        var entity = new Customer
        {
            CafeId = cafeId,
            Name = request.Name,
            Phone = phone,
            NationalId = request.NationalId,
            BirthDateJalali = request.BirthDateJalali,
            Group = request.Group,
            ReferredBy = request.ReferredBy
        };

        _db.Customers.Add(entity);
        await _db.SaveChangesAsync(cancellationToken);
        return ToDto(entity);
    }

    public async Task<CustomerDto?> UpdateAsync(
        string cafeId,
        string id,
        UpdateCustomerRequest request,
        CancellationToken cancellationToken = default)
    {
        var entity = await _db.Customers
            .FirstOrDefaultAsync(c => c.Id == id && c.CafeId == cafeId && c.DeletedAt == null, cancellationToken);
        if (entity is null) return null;

        if (request.Name is not null) entity.Name = request.Name;
        if (request.Phone is not null)
        {
            var phone = PhoneNormalizer.Normalize(request.Phone);
            var phoneTaken = await _db.Customers.AnyAsync(
                c => c.CafeId == cafeId && c.Phone == phone && c.Id != id && c.DeletedAt == null,
                cancellationToken);
            if (phoneTaken) return null;
            entity.Phone = phone;
        }
        if (request.NationalId is not null) entity.NationalId = request.NationalId;
        if (request.BirthDateJalali is not null) entity.BirthDateJalali = request.BirthDateJalali;
        if (request.Group.HasValue) entity.Group = request.Group.Value;
        if (request.LoyaltyPoints.HasValue) entity.LoyaltyPoints = request.LoyaltyPoints.Value;
        if (request.ReferredBy is not null) entity.ReferredBy = request.ReferredBy;

        await _db.SaveChangesAsync(cancellationToken);
        return ToDto(entity);
    }

    public async Task<bool> DeleteAsync(string cafeId, string id, CancellationToken cancellationToken = default)
    {
        var entity = await _db.Customers
            .FirstOrDefaultAsync(c => c.Id == id && c.CafeId == cafeId, cancellationToken);
        if (entity is null) return false;

        entity.DeletedAt = DateTime.UtcNow;
        await _db.SaveChangesAsync(cancellationToken);
        return true;
    }

    private static CustomerDto ToDto(Customer c) => new(
        c.Id,
        c.Name,
        c.Phone,
        c.NationalId,
        c.BirthDateJalali,
        c.Group,
        c.LoyaltyPoints,
        c.ReferredBy,
        c.CreatedAt);
}
