using System.Security.Claims;
using JobsMedical.Web.Data;
using JobsMedical.Web.Models;
using JobsMedical.Web.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;

namespace JobsMedical.Web.Pages.Employer;

[Authorize]
public class ListingsModel : PageModel
{
    private readonly AppDbContext _db;
    private readonly NotificationService _notify;
    public ListingsModel(AppDbContext db, NotificationService notify)
    {
        _db = db;
        _notify = notify;
    }

    public record Applicant(string? Name, string Phone, DateTime When, long EventId, int UserId,
        ApplicationStatus Status, bool HasAvatar, bool HasResume);
    public record ShiftRow(Shift Shift, List<Applicant> Applicants, int Guests);
    public record JobRow(JobOpening Job, List<Applicant> Applicants, int Guests);

    public Facility? Facility { get; private set; }
    public List<ShiftRow> Shifts { get; private set; } = new();
    public List<JobRow> Jobs { get; private set; } = new();

    [BindProperty(SupportsGet = true)] public int FacilityId { get; set; }

    public async Task<IActionResult> OnGetAsync()
    {
        if (!await OwnsAsync(FacilityId)) return Forbid();
        await LoadAsync();
        return Page();
    }

    // --- Lifecycle actions (all ownership-checked) ---
    public Task<IActionResult> OnPostCloseShiftAsync(int id) => MutateShift(id, s => s.Status = ShiftStatus.Filled);
    public Task<IActionResult> OnPostReopenShiftAsync(int id) => MutateShift(id, s => s.Status = ShiftStatus.Open);
    public Task<IActionResult> OnPostDeleteShiftAsync(int id) => MutateShift(id, s => _db.Shifts.Remove(s));
    public Task<IActionResult> OnPostCloseJobAsync(int id) => MutateJob(id, j => j.Status = ShiftStatus.Filled);
    public Task<IActionResult> OnPostReopenJobAsync(int id) => MutateJob(id, j => j.Status = ShiftStatus.Open);
    public Task<IActionResult> OnPostDeleteJobAsync(int id) => MutateJob(id, j => _db.JobOpenings.Remove(j));

    private async Task<IActionResult> MutateShift(int id, Action<Shift> apply)
    {
        var s = await _db.Shifts.FirstOrDefaultAsync(x => x.Id == id);
        if (s is null || !await OwnsAsync(s.FacilityId)) return Forbid();
        apply(s);
        await _db.SaveChangesAsync();
        return RedirectToPage(new { FacilityId = s.FacilityId });
    }

    private async Task<IActionResult> MutateJob(int id, Action<JobOpening> apply)
    {
        var j = await _db.JobOpenings.FirstOrDefaultAsync(x => x.Id == id);
        if (j is null || !await OwnsAsync(j.FacilityId)) return Forbid();
        apply(j);
        await _db.SaveChangesAsync();
        return RedirectToPage(new { FacilityId = j.FacilityId });
    }

    // --- Applicant decisions ---
    public Task<IActionResult> OnPostAcceptAsync(long eventId) => SetStatus(eventId, ApplicationStatus.Accepted, true);
    public Task<IActionResult> OnPostRejectAsync(long eventId) => SetStatus(eventId, ApplicationStatus.Rejected, false);

    private async Task<IActionResult> SetStatus(long eventId, ApplicationStatus status, bool accepted)
    {
        var ev = await _db.InterestEvents
            .Include(e => e.Shift).ThenInclude(s => s!.Facility)
            .Include(e => e.Shift).ThenInclude(s => s!.Role)
            .Include(e => e.JobOpening).ThenInclude(j => j!.Facility)
            .FirstOrDefaultAsync(e => e.Id == eventId && e.EventType == InterestEventType.Apply);
        if (ev is null) return NotFound();

        var facilityId = ev.Shift?.FacilityId ?? ev.JobOpening?.FacilityId ?? 0;
        if (!await OwnsAsync(facilityId)) return Forbid();

        ev.Status = status;
        await _db.SaveChangesAsync();

        // Notify the applicant (only when they're a registered user we can reach).
        var applicantId = await _db.Visitors.Where(v => v.Id == ev.VisitorId).Select(v => v.UserId).FirstOrDefaultAsync();
        if (applicantId is int uid)
        {
            var (title, url) = ev.JobOpening is not null
                ? (ev.JobOpening.Title, $"/Jobs/Details/{ev.JobOpeningId}")
                : ($"شیفت {ev.Shift?.Role?.Name} — {ev.Shift?.Facility?.Name}", $"/Shifts/Details/{ev.ShiftId}");
            await _notify.NotifyApplicantStatusAsync(uid, title, accepted, url);
        }
        return RedirectToPage(new { FacilityId = facilityId });
    }

    private async Task<bool> OwnsAsync(int facilityId)
    {
        var userId = int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
        return await _db.Facilities.AnyAsync(f => f.Id == facilityId && f.OwnerUserId == userId);
    }

    private async Task LoadAsync()
    {
        Facility = await _db.Facilities.Include(f => f.City).FirstOrDefaultAsync(f => f.Id == FacilityId);

        var shifts = await _db.Shifts.Include(s => s.Role)
            .Where(s => s.FacilityId == FacilityId)
            .OrderByDescending(s => s.Date).ToListAsync();
        var jobs = await _db.JobOpenings.Include(j => j.Role)
            .Where(j => j.FacilityId == FacilityId)
            .OrderByDescending(j => j.CreatedAt).ToListAsync();

        // Pull all "Apply" events for these listings, then resolve applicant identities.
        var shiftIds = shifts.Select(s => s.Id).ToList();
        var jobIds = jobs.Select(j => j.Id).ToList();
        var events = await _db.InterestEvents
            .Where(e => e.EventType == InterestEventType.Apply &&
                ((e.ShiftId != null && shiftIds.Contains(e.ShiftId.Value)) ||
                 (e.JobOpeningId != null && jobIds.Contains(e.JobOpeningId.Value))))
            .ToListAsync();

        var visitorIds = events.Select(e => e.VisitorId).Distinct().ToList();
        var visitorUser = await _db.Visitors.Where(v => visitorIds.Contains(v.Id))
            .ToDictionaryAsync(v => v.Id, v => v.UserId);
        var userIds = visitorUser.Values.Where(u => u != null).Select(u => u!.Value).Distinct().ToList();
        var users = await _db.Users.Where(u => userIds.Contains(u.Id))
            .Select(u => new { u.Id, u.FullName, u.Phone, HasAvatar = u.Avatar != null, HasResume = u.Resume != null })
            .ToDictionaryAsync(u => u.Id);

        (List<Applicant> applicants, int guests) Resolve(IEnumerable<InterestEvent> evs)
        {
            var applicants = new List<Applicant>();
            var guests = 0;
            var seen = new HashSet<int>();
            foreach (var e in evs.OrderByDescending(e => e.CreatedAt))
            {
                var uid = visitorUser.GetValueOrDefault(e.VisitorId);
                if (uid is int id && users.TryGetValue(id, out var u))
                {
                    if (seen.Add(id)) applicants.Add(new Applicant(u.FullName, u.Phone, e.CreatedAt, e.Id, id, e.Status, u.HasAvatar, u.HasResume));
                }
                else guests++;
            }
            return (applicants, guests);
        }

        Shifts = shifts.Select(s =>
        {
            var (a, g) = Resolve(events.Where(e => e.ShiftId == s.Id));
            return new ShiftRow(s, a, g);
        }).ToList();
        Jobs = jobs.Select(j =>
        {
            var (a, g) = Resolve(events.Where(e => e.JobOpeningId == j.Id));
            return new JobRow(j, a, g);
        }).ToList();
    }
}
