using System.Globalization;
using HtmlAgilityPack;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OFACScraper.Configuration;
using OFACScraper.Models;

namespace OFACScraper;

public class OFACScraper
{
    private readonly HttpClient _http;
    private readonly OFACOptions _options;
    private readonly ILogger<OFACScraper> _logger;

    public OFACScraper(HttpClient http, IOptions<OFACOptions> options, ILogger<OFACScraper> logger)
    {
        _http = http;
        _options = options.Value;
        _logger = logger;
    }

    public string GetYearUrl(int year)
    {
        // The current year's data is on the main page, not a year subpath.
        // Past years use /{year}-enforcement-information.
        var currentYear = DateTime.UtcNow.Year;
        if (year == currentYear)
            return _options.BaseUrl + "/civil-penalties-and-enforcement-information";
        return _options.BaseUrl + string.Format(_options.YearUrlTemplate, year);
    }

    /// <summary>
    /// Fetches and parses the OFAC civil penalties table for a given year.
    /// Returns all penalty records found on the page.
    /// </summary>
    public async Task<List<OFACRecord>> GetYearRecordsAsync(int year, CancellationToken ct = default)
    {
        var url = GetYearUrl(year);
        _logger.LogInformation("Scraping year {Year}: {Url}", year, url);

        string html;
        try
        {
            html = await _http.GetStringAsync(url, ct);
        }
        catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            _logger.LogWarning("No page found for year {Year} (404)", year);
            return [];
        }

        var doc = new HtmlDocument();
        doc.LoadHtml(html);

        var table = doc.DocumentNode.SelectSingleNode("//table[contains(@class,'usa-table')]");
        if (table == null)
        {
            _logger.LogWarning("No usa-table found for year {Year}", year);
            return [];
        }

        var rows = table.SelectNodes(".//tbody/tr");
        if (rows == null) return [];

        var records = new List<OFACRecord>();

        foreach (var row in rows)
        {
            var cells = row.SelectNodes(".//td");
            if (cells == null || cells.Count < 4) continue;

            // Date cell: <a href="/media/.../download?inline" title="filename.pdf">MM/DD/YYYY</a>
            var dateLink = cells[0].SelectSingleNode(".//a");
            if (dateLink == null) continue; // "Year to date totals" summary row

            var dateText = HtmlEntity.DeEntitize(dateLink.InnerText).Trim();
            if (!DateTime.TryParseExact(dateText, "MM/dd/yyyy", CultureInfo.InvariantCulture,
                    DateTimeStyles.None, out var date))
            {
                _logger.LogWarning("Could not parse date '{Date}' in year {Year}", dateText, year);
                continue;
            }

            var docHref = dateLink.GetAttributeValue("href", "").Trim();
            if (string.IsNullOrEmpty(docHref)) continue;

            var docUrl = docHref.StartsWith("http") ? docHref : _options.BaseUrl + docHref;
            var fileName = dateLink.GetAttributeValue("title", "").Trim();
            if (string.IsNullOrEmpty(fileName))
                fileName = $"{date:yyyyMMdd}_{Slugify(HtmlEntity.DeEntitize(cells[1].InnerText).Trim())}.pdf";

            var name = HtmlEntity.DeEntitize(cells[1].InnerText).Trim();
            var penaltyText = HtmlEntity.DeEntitize(cells[3].InnerText).Trim();
            var penalty = ParsePenalty(penaltyText);

            records.Add(new OFACRecord
            {
                Date = date,
                Name = name,
                PenaltyTotalUsd = penalty,
                DocumentUrl = docUrl,
                FileName = fileName,
                Year = year,
                YearPageUrl = url
            });
        }

        _logger.LogInformation("Year {Year}: found {Count} records", year, records.Count);

        if (_options.RequestDelayMs > 0)
            await Task.Delay(_options.RequestDelayMs, ct);

        return records;
    }

    private static decimal? ParsePenalty(string text)
    {
        if (string.IsNullOrWhiteSpace(text)) return null;
        var cleaned = text.Replace("$", "").Replace(",", "").Trim();
        return decimal.TryParse(cleaned, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)
            ? value
            : null;
    }

    private static string Slugify(string text) =>
        new string(text.ToLowerInvariant()
            .Select(c => char.IsLetterOrDigit(c) ? c : '_')
            .ToArray())
            .Trim('_');
}
