using FlatRender.IdentitySvc.Application.Services.Interfaces;
using FlatRender.IdentitySvc.Domain.Enums;
using FlatRender.IdentitySvc.Infrastructure.Data;
using FlatRender.IdentitySvc.Models.Requests;
using FlatRender.IdentitySvc.Models.Responses;
using Microsoft.EntityFrameworkCore;

namespace FlatRender.IdentitySvc.Application.Services;

public class UserService(IdentityDbContext db) : IUserService
{
    public async Task<UserResponse> GetMeAsync(Guid userId)
    {
        var user = await db.Users.FindAsync(userId)
            ?? throw new KeyNotFoundException("User not found");
        return AuthService.MapUserResponse(user);
    }

    public async Task<UserResponse> UpdateMeAsync(Guid userId, UpdateUserRequest request)
    {
        var user = await db.Users.FindAsync(userId)
            ?? throw new KeyNotFoundException("User not found");

        if (request.FullName != null) user.FullName = request.FullName;
        if (request.Slogan != null) user.Slogan = request.Slogan;
        if (request.AboutMe != null) user.AboutMe = request.AboutMe;
        if (request.CompanyName != null) user.CompanyName = request.CompanyName;
        if (request.WebsiteName != null) user.WebsiteName = request.WebsiteName;
        if (request.BirthDate.HasValue) user.BirthDate = request.BirthDate;
        if (request.Gender != null && Enum.TryParse<GenderKind>(request.Gender, true, out var gender))
            user.Gender = gender;
        if (request.EmailTellMe.HasValue) user.EmailTellMe = request.EmailTellMe.Value;
        if (request.SmsTellMe.HasValue) user.SmsTellMe = request.SmsTellMe.Value;
        if (request.PushTellMe.HasValue) user.PushTellMe = request.PushTellMe.Value;
        if (request.TelegramTellMe.HasValue) user.TelegramTellMe = request.TelegramTellMe.Value;
        if (request.CountryCode != null) user.CountryCode = request.CountryCode;
        if (request.NationalCode != null) user.NationalCode = request.NationalCode;
        if (request.MethodOfIntroduction != null) user.MethodOfIntroduction = request.MethodOfIntroduction;

        user.UpdatedAt = DateTime.UtcNow;
        await db.SaveChangesAsync();
        return AuthService.MapUserResponse(user);
    }

    public async Task<BalanceResponse> GetBalanceAsync(Guid userId)
    {
        var user = await db.Users.FindAsync(userId)
            ?? throw new KeyNotFoundException("User not found");
        return new BalanceResponse(
            user.BalanceMinor,
            user.AffiliateBalanceMinor,
            "IRR",
            user.DailyRemainRenderCount,
            user.ParallelRenderingCeiling
        );
    }

    public async Task UpdateAvatarAsync(Guid userId, Guid? avatarId, string? avatarUrl)
    {
        var user = await db.Users.FindAsync(userId)
            ?? throw new KeyNotFoundException("User not found");

        if (avatarId.HasValue)
        {
            var avatar = await db.Avatars.FindAsync(avatarId.Value);
            if (avatar != null) user.AvatarUrl = avatar.Url;
        }
        else if (!string.IsNullOrEmpty(avatarUrl))
        {
            user.AvatarUrl = avatarUrl;
        }

        user.UpdatedAt = DateTime.UtcNow;
        await db.SaveChangesAsync();
    }

    public async Task<UserResponse> GetByIdAsync(Guid userId)
    {
        var user = await db.Users.FindAsync(userId)
            ?? throw new KeyNotFoundException("User not found");
        return AuthService.MapUserResponse(user);
    }

    public async Task<PagedResponse<UserResponse>> SearchAsync(string? q, Guid? tenantId, int page, int pageSize)
    {
        var query = db.Users.Where(u => u.DeletedAt == null);

        if (tenantId.HasValue)
            query = query.Where(u => u.TenantId == tenantId.Value);

        if (!string.IsNullOrWhiteSpace(q))
            query = query.Where(u =>
                (u.Email != null && u.Email.Contains(q)) ||
                (u.FullName != null && EF.Functions.ILike(u.FullName, $"%{q}%")) ||
                (u.PhoneNumber != null && u.PhoneNumber.Contains(q)));

        var total = await query.LongCountAsync();
        var users = await query
            .OrderByDescending(u => u.RegisterDate)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        return new PagedResponse<UserResponse>(
            users.Select(AuthService.MapUserResponse).ToList(),
            new PaginationMeta(page, pageSize, total, total > (long)page * pageSize)
        );
    }

    public async Task BanAsync(Guid userId, string reason, DateTime? unblockDate)
    {
        var user = await db.Users.FindAsync(userId)
            ?? throw new KeyNotFoundException("User not found");
        user.BanAccount = true;
        user.BanReason = reason;
        user.UnblockDate = unblockDate;
        user.UpdatedAt = DateTime.UtcNow;
        await db.SaveChangesAsync();
    }

    public async Task UnbanAsync(Guid userId)
    {
        var user = await db.Users.FindAsync(userId)
            ?? throw new KeyNotFoundException("User not found");
        user.BanAccount = false;
        user.BanReason = null;
        user.UnblockDate = null;
        user.UpdatedAt = DateTime.UtcNow;
        await db.SaveChangesAsync();
    }
}
