using JobsMedical.Web.Data;
using JobsMedical.Web.Models;
using JobsMedical.Web.Services.Scraping;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;

namespace JobsMedical.Web.Pages;

/// <summary>Site-wide rich search across shifts, hiring openings, and applicants with keyword
/// highlighting. Every query term must match somewhere (Postgres ILIKE over the relevant fields).</summary>
public class SearchModel : PageModel
{
    private readonly AppDbContext _db;
    public SearchModel(AppDbContext db) => _db = db;

    [BindProperty(SupportsGet = true)] public string? Q { get; set; }

    public List<Shift> Shifts { get; private set; } = new();
    public List<JobOpening> Jobs { get; private set; } = new();
    public List<TalentListing> Talent { get; private set; } = new();
    public int Total => Shifts.Count + Jobs.Count + Talent.Count;
    public bool HasQuery => !string.IsNullOrWhiteSpace(Q);

    public async Task OnGetAsync()
    {
        if (!HasQuery) return;
        var terms = Q!.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        var today = DateOnly.FromDateTime(DateTime.UtcNow);
        var jobCut = ListingPolicy.JobCutoffUtc;
        var talentCut = ListingPolicy.TalentCutoffUtc;

        var sq = _db.Shifts.Include(s => s.Facility).ThenInclude(f => f.City)
            .Include(s => s.Facility).ThenInclude(f => f.District).Include(s => s.Role)
            .Where(s => s.Status == ShiftStatus.Open && s.Date >= today);
        foreach (var t in terms)
        {
            var like = $"%{t}%";
            sq = sq.Where(s => EF.Functions.ILike(s.Facility.Name, like) || EF.Functions.ILike(s.Facility.City.Name, like)
                || EF.Functions.ILike(s.Role.Name, like) || EF.Functions.ILike(s.SpecialtyRequired, like)
                || EF.Functions.ILike(s.Description ?? "", like));
        }
        var shiftPool = await sq.OrderByDescending(s => s.CreatedAt).Take(60).ToListAsync();
        Shifts = shiftPool
            .OrderByDescending(s => Rank(terms, 3, s.Role?.Name, s.Facility?.Name, s.Facility?.City?.Name, s.SpecialtyRequired)
                                  + Rank(terms, 1, s.Description))
            .ThenByDescending(s => s.CreatedAt).Take(30).ToList();

        var jq = _db.JobOpenings.Include(j => j.Facility).ThenInclude(f => f.City)
            .Include(j => j.Facility).ThenInclude(f => f.District).Include(j => j.Role)
            .Where(j => j.Status == ShiftStatus.Open && j.CreatedAt >= jobCut);
        foreach (var t in terms)
        {
            var like = $"%{t}%";
            jq = jq.Where(j => EF.Functions.ILike(j.Title, like) || EF.Functions.ILike(j.Facility.Name, like)
                || EF.Functions.ILike(j.Facility.City.Name, like) || EF.Functions.ILike(j.Role.Name, like)
                || EF.Functions.ILike(j.Description ?? "", like));
        }
        var jobPool = await jq.OrderByDescending(j => j.CreatedAt).Take(60).ToListAsync();
        Jobs = jobPool
            .OrderByDescending(j => Rank(terms, 3, j.Title, j.Role?.Name, j.Facility?.Name, j.Facility?.City?.Name)
                                  + Rank(terms, 1, j.Description))
            .ThenByDescending(j => j.CreatedAt).Take(30).ToList();

        var tq = _db.TalentListings.Include(t => t.City).Include(t => t.District).Include(t => t.Role)
            .Where(t => t.Status == ShiftStatus.Open && t.CreatedAt >= talentCut);
        foreach (var t in terms)
        {
            var like = $"%{t}%";
            tq = tq.Where(x => EF.Functions.ILike(x.Tags ?? "", like) || EF.Functions.ILike(x.Description ?? "", like)
                || EF.Functions.ILike(x.PersonName ?? "", like) || EF.Functions.ILike(x.AreaNote ?? "", like)
                || EF.Functions.ILike(x.Role.Name, like) || EF.Functions.ILike(x.City.Name, like));
        }
        var talentPool = await tq.OrderByDescending(x => x.CreatedAt).Take(60).ToListAsync();
        Talent = talentPool
            .OrderByDescending(x => Rank(terms, 3, x.Role?.Name, x.City?.Name, x.PersonName, x.Tags)
                                  + Rank(terms, 1, x.Description, x.AreaNote))
            .ThenByDescending(x => x.CreatedAt).Take(30).ToList();
    }

    /// <summary>Relevance score: +weight per term found in any of the given fields.</summary>
    private static int Rank(string[] terms, int weight, params string?[] fields)
    {
        var score = 0;
        foreach (var term in terms)
            foreach (var f in fields)
                if (!string.IsNullOrEmpty(f) && f.Contains(term, StringComparison.OrdinalIgnoreCase))
                { score += weight; break; }
        return score;
    }
}
