using System.Text;
using System.Text.Json;
using JobsMedical.Web.Data;
using JobsMedical.Web.Models;
using JobsMedical.Web.Services.Scraping;
using Microsoft.EntityFrameworkCore;

namespace JobsMedical.Web.Services.Social;

/// <summary>Result of building the daily digest — reused by the worker, the admin preview, and Instagram.</summary>
public record SocialDigest(int Count, string Body, string TelegramText, string InstagramCaption, string Hashtags);

public record SocialPostResult(bool TelegramOk, bool BaleOk, int Count, string? Error);

/// <summary>
/// Composes a daily «کادر آماده به کار امروز» digest and posts it as text to Telegram and Bale
/// (via their bot APIs). Also produces an Instagram caption + hashtags for the manual flow.
/// All credentials/toggles live in <see cref="AppSetting"/> (admin panel, DB-backed).
/// </summary>
public class SocialPostService
{
    private readonly AppDbContext _db;
    private readonly SettingsService _settings;
    private readonly ScrapeHttpClients _clients;
    private readonly ILogger<SocialPostService> _log;

    public SocialPostService(AppDbContext db, SettingsService settings, ScrapeHttpClients clients, ILogger<SocialPostService> log)
    {
        _db = db; _settings = settings; _clients = clients; _log = log;
    }

    /// <summary>Today's freshly-published «آماده به کار» listings, formatted for sharing.</summary>
    public async Task<SocialDigest> BuildDigestAsync(AppSetting s, int take = 8, CancellationToken ct = default)
    {
        var since = DateTime.UtcNow.AddHours(-24);
        var items = await _db.TalentListings
            .Include(t => t.Role).Include(t => t.City).Include(t => t.District)
            .Where(t => t.Status == ShiftStatus.Open && t.CreatedAt >= since)
            .OrderByDescending(t => t.CreatedAt)
            .Take(take)
            .ToListAsync(ct);

        var sb = new StringBuilder();
        foreach (var t in items)
        {
            var role = t.Role?.Name ?? "کادر درمان";
            var city = t.City?.Name ?? "";
            var area = t.District?.Name ?? t.AreaNote;
            var exp = t.YearsExperience is int y && y > 0 ? $"، {JalaliDate.ToPersianDigits(y.ToString())} سال سابقه" : "";
            var loc = string.IsNullOrWhiteSpace(area) ? city : $"{city}، {area}";
            sb.Append("• ").Append(role).Append(exp);
            if (!string.IsNullOrWhiteSpace(loc)) sb.Append(" — 📍 ").Append(loc);
            sb.Append('\n');
        }
        var body = sb.ToString().TrimEnd();

        var header = string.IsNullOrWhiteSpace(s.SocialHeader) ? null : s.SocialHeader!.Trim();
        var footer = string.IsNullOrWhiteSpace(s.SocialFooter) ? null : s.SocialFooter!.Trim();
        var title = $"🩺 کادر درمان آماده‌به‌کار امروز ({JalaliDate.ToPersianDigits(items.Count.ToString())} نفر)";

        var tg = new StringBuilder();
        if (header is not null) tg.Append(header).Append("\n\n");
        tg.Append(title).Append("\n\n");
        tg.Append(items.Count == 0 ? "امروز موردی ثبت نشد." : body);
        if (footer is not null) tg.Append("\n\n").Append(footer);

        var hashtags = BuildHashtags(s, items);
        var caption = new StringBuilder();
        if (header is not null) caption.Append(header).Append("\n\n");
        caption.Append(title).Append("\n\n").Append(items.Count == 0 ? "" : body);
        if (footer is not null) caption.Append("\n\n").Append(footer);
        caption.Append("\n\n").Append(hashtags);

        return new SocialDigest(items.Count, body, tg.ToString(), caption.ToString().Trim(), hashtags);
    }

    private static string BuildHashtags(AppSetting s, List<TalentListing> items)
    {
        var tags = new List<string> { "#همکادر", "#استخدام_کادر_درمان", "#آماده_به_کار", "#پرستار", "#استخدام_پرستار", "#کاریابی_پزشکی" };
        foreach (var t in items)
        {
            if (t.Role?.Name is string r) tags.Add("#" + r.Replace(' ', '_'));
            if (t.City?.Name is string c) tags.Add("#" + c.Replace(' ', '_'));
        }
        foreach (var extra in AppSetting.SplitList(s.InstagramHashtags))
            tags.Add(extra.StartsWith('#') ? extra : "#" + extra.Replace(' ', '_'));
        return string.Join(" ", tags.Distinct().Take(25));
    }

    /// <summary>Build today's digest and post it to the enabled text channels (Telegram + Bale).</summary>
    public async Task<SocialPostResult> PostAsync(CancellationToken ct = default)
    {
        var s = await _settings.GetAsync();
        var digest = await BuildDigestAsync(s, ct: ct);
        if (digest.Count == 0)
            return new SocialPostResult(false, false, 0, "موردی برای انتشار امروز نبود.");

        var client = _clients.For(s, s.SocialUseProxy);
        bool tgOk = false, baleOk = false;
        string? err = null;

        if (s.SocialTelegramEnabled && !string.IsNullOrWhiteSpace(s.SocialTelegramBotToken) && !string.IsNullOrWhiteSpace(s.SocialTelegramChatId))
        {
            var (ok, e) = await SendAsync(client, "https://api.telegram.org", s.SocialTelegramBotToken!, s.SocialTelegramChatId!, digest.TelegramText, ct);
            tgOk = ok; err ??= e;
        }

        if (s.SocialBaleEnabled && !string.IsNullOrWhiteSpace(s.SocialBaleBotToken) && !string.IsNullOrWhiteSpace(s.SocialBaleChatId))
        {
            var (ok, e) = await SendAsync(client, "https://tapi.bale.ai", s.SocialBaleBotToken!, s.SocialBaleChatId!, digest.TelegramText, ct);
            baleOk = ok; err ??= e;
        }

        // record the run so the scheduler paces itself
        var row = await _db.AppSettings.FirstOrDefaultAsync(x => x.Id == 1, ct);
        if (row is not null) { row.SocialLastPostedAt = DateTime.UtcNow; await _db.SaveChangesAsync(ct); }

        return new SocialPostResult(tgOk, baleOk, digest.Count, err);
    }

    /// <summary>Telegram-compatible bot sendMessage (Bale shares the same shape).</summary>
    private async Task<(bool ok, string? error)> SendAsync(HttpClient client, string apiBase, string token, string chatId, string text, CancellationToken ct)
    {
        try
        {
            var url = $"{apiBase}/bot{token}/sendMessage";
            var form = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["chat_id"] = chatId,
                ["text"] = text.Length > 4000 ? text[..4000] : text,
                ["disable_web_page_preview"] = "true",
            });
            using var resp = await client.PostAsync(url, form, ct);
            var body = await resp.Content.ReadAsStringAsync(ct);
            if (resp.IsSuccessStatusCode && body.Contains("\"ok\":true")) return (true, null);
            _log.LogWarning("Social post to {Base} failed: {Status} {Body}", apiBase, (int)resp.StatusCode, body);
            return (false, ExtractError(body) ?? $"خطای {(int)resp.StatusCode}");
        }
        catch (Exception ex)
        {
            _log.LogWarning(ex, "Social post to {Base} errored", apiBase);
            return (false, ex.Message);
        }
    }

    private static string? ExtractError(string body)
    {
        try { using var d = JsonDocument.Parse(body); return d.RootElement.TryGetProperty("description", out var v) ? v.GetString() : null; }
        catch { return null; }
    }
}
