using System.Net;
using System.Text.RegularExpressions;
using JobsMedical.Web.Models;

namespace JobsMedical.Web.Services.Scraping;

/// <summary>
/// Generic website source: fetches each admin-configured URL and extracts readable text
/// (JobPosting/Product JSON-LD description, common content containers, or og:title+description).
/// Lets the admin point the engine at any job page / channel archive / classifieds listing.
/// </summary>
public class WebsiteListingSource : IListingSource
{
    private readonly ScrapeHttpClients _clients;
    private readonly ILogger<WebsiteListingSource> _log;

    public WebsiteListingSource(ScrapeHttpClients clients, ILogger<WebsiteListingSource> log)
    {
        _clients = clients;
        _log = log;
    }

    public string Name => "وب‌سایت‌ها";

    public async Task<IReadOnlyList<ScrapedItem>> FetchAsync(AppSetting s, CancellationToken ct = default)
    {
        var urls = AppSetting.SplitList(s.WebsiteUrls);
        if (!s.WebsitesEnabled || urls.Count == 0) return Array.Empty<ScrapedItem>();

        var client = _clients.For(s, s.WebsitesUseProxy);
        var items = new List<ScrapedItem>();
        foreach (var url in urls.Where(u => u.StartsWith("http")))
        {
            try
            {
                var html = await client.GetStringAsync(url, ct);
                var text = Extract(html);
                if (text.Length >= 25) items.Add(new ScrapedItem($"وب‌سایت ({Host(url)})", text, url));
            }
            catch (Exception ex) { _log.LogWarning(ex, "Website fetch failed for {Url}", url); }
        }
        return items;
    }

    private static string Host(string url) => Uri.TryCreate(url, UriKind.Absolute, out var u) ? u.Host : "web";

    private static string Extract(string html)
    {
        string? title = Meta(html, "og:title");
        if (title is not null) { var bar = title.IndexOf('|'); if (bar > 10) title = title[..bar].Trim(); }
        string? body = Between(html, "rtcl-description") ?? Between(html, "entry-content")
                    ?? Between(html, "job-description") ?? Meta(html, "og:description");
        var text = HtmlUtil.ToPlainText(string.Join("\n", new[] { title, body }.Where(x => !string.IsNullOrWhiteSpace(x))));
        if (text.Length > 1800) text = text[..1800];

        // Harvest a number from the ad's own content region only (not the whole page, which would
        // pick up the site's header/footer number on every listing).
        var phones = HtmlUtil.HarvestPhones(body ?? "");
        if (phones.Count > 0 && !phones.Any(text.Contains))
            text += "\nشماره تماس: " + string.Join("، ", phones);
        return text;
    }

    private static string? Meta(string html, string prop)
    {
        var m = Regex.Match(html, $"<meta[^>]+property=[\"']{Regex.Escape(prop)}[\"'][^>]+content=[\"']([^\"']*)[\"']");
        return m.Success ? WebUtility.HtmlDecode(m.Groups[1].Value) : null;
    }

    private static string? Between(string html, string cls)
    {
        var m = Regex.Match(html, $"<(?:div|article)[^>]+class=[\"'][^\"']*{Regex.Escape(cls)}[^\"']*[\"'][^>]*>(.*?)</(?:div|article)>",
            RegexOptions.Singleline);
        return m.Success ? m.Groups[1].Value : null;
    }
}
