using JobsMedical.Web.Data;
using JobsMedical.Web.Models;
using Microsoft.EntityFrameworkCore;

namespace JobsMedical.Web.Services;

/// <summary>
/// Persists visitor preferences and behavioral events. Creates the <see cref="Visitor"/> row
/// lazily on first write, so anonymous browsing doesn't hit the DB on every request.
/// </summary>
public class InterestService
{
    private readonly AppDbContext _db;
    private readonly VisitorContext _visitor;
    private readonly NotificationService _notify;

    public InterestService(AppDbContext db, VisitorContext visitor, NotificationService notify)
    {
        _db = db;
        _visitor = visitor;
        _notify = notify;
    }

    public string VisitorId => _visitor.VisitorId;

    private async Task EnsureVisitorAsync()
    {
        var id = VisitorId;
        if (string.IsNullOrEmpty(id)) return;
        var exists = await _db.Visitors.AnyAsync(v => v.Id == id);
        if (!exists)
        {
            _db.Visitors.Add(new Visitor { Id = id });
            await _db.SaveChangesAsync();
        }
        else
        {
            await _db.Visitors.Where(v => v.Id == id)
                .ExecuteUpdateAsync(s => s.SetProperty(v => v.LastSeenAt, DateTime.UtcNow));
        }
    }

    public async Task LogAsync(InterestEventType type, int shiftId)
    {
        if (string.IsNullOrEmpty(VisitorId)) return;
        // Don't record the same applicant applying to the same shift twice (garbage prevention).
        if (type == InterestEventType.Apply && await _db.InterestEvents.AnyAsync(e =>
                e.VisitorId == VisitorId && e.ShiftId == shiftId && e.EventType == InterestEventType.Apply))
            return;
        await EnsureVisitorAsync();
        _db.InterestEvents.Add(new InterestEvent
        {
            VisitorId = VisitorId,
            ShiftId = shiftId,
            EventType = type,
        });
        await _db.SaveChangesAsync();
        if (type == InterestEventType.Apply) await _notify.NotifyShiftApplicationAsync(shiftId);   // ping the employer
    }

    public async Task LogJobAsync(InterestEventType type, int jobOpeningId)
    {
        if (string.IsNullOrEmpty(VisitorId)) return;
        if (type == InterestEventType.Apply && await _db.InterestEvents.AnyAsync(e =>
                e.VisitorId == VisitorId && e.JobOpeningId == jobOpeningId && e.EventType == InterestEventType.Apply))
            return;
        await EnsureVisitorAsync();
        _db.InterestEvents.Add(new InterestEvent
        {
            VisitorId = VisitorId,
            JobOpeningId = jobOpeningId,
            EventType = type,
        });
        await _db.SaveChangesAsync();
        if (type == InterestEventType.Apply) await _notify.NotifyJobApplicationAsync(jobOpeningId);   // ping the employer
    }

    public Task<UserPreferences?> GetPreferencesAsync()
    {
        var id = VisitorId;
        if (string.IsNullOrEmpty(id)) return Task.FromResult<UserPreferences?>(null);
        return _db.UserPreferences.AsNoTracking().FirstOrDefaultAsync(p => p.VisitorId == id);
    }

    public async Task SavePreferencesAsync(int? roleId, int? cityId, ShiftType? shiftType, long? minPay,
        Gender gender = Gender.Any)
    {
        await EnsureVisitorAsync();
        var prefs = await _db.UserPreferences.FirstOrDefaultAsync(p => p.VisitorId == VisitorId);
        if (prefs is null)
        {
            prefs = new UserPreferences { VisitorId = VisitorId };
            _db.UserPreferences.Add(prefs);
        }
        prefs.RoleId = roleId;
        prefs.CityId = cityId;
        prefs.PreferredShiftType = shiftType;
        prefs.MinPay = minPay;
        prefs.Gender = gender;
        prefs.UpdatedAt = DateTime.UtcNow;
        await _db.SaveChangesAsync();
    }

    /// <summary>Recent events for this visitor (newest first) — the behavioral signal.</summary>
    public Task<List<InterestEvent>> RecentEventsAsync(int take = 100)
    {
        var id = VisitorId;
        if (string.IsNullOrEmpty(id)) return Task.FromResult(new List<InterestEvent>());
        return _db.InterestEvents.AsNoTracking()
            .Where(e => e.VisitorId == id)
            .OrderByDescending(e => e.CreatedAt)
            .Take(take)
            .ToListAsync();
    }
}
