using System.Text.RegularExpressions;
using Microsoft.Extensions.Options;
using Tesseract;

public class TesseractOcrService : IOcrService
{
    private static readonly (Regex Pattern, string FieldName)[] ClinicalPatterns =
    [
        (new Regex(@"(?:Patient\s+)?Name[:\s]+(.+)", RegexOptions.IgnoreCase | RegexOptions.Multiline), "patient.fullName"),
        (new Regex(@"DOB[:\s]+(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", RegexOptions.IgnoreCase), "patient.dateOfBirth"),
        (new Regex(@"(?:Date of Birth|Birth Date)[:\s]+(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", RegexOptions.IgnoreCase), "patient.dateOfBirth"),
        (new Regex(@"(?:Sex|Gender)[:\s]+(\S+)", RegexOptions.IgnoreCase), "patient.sex"),
        (new Regex(@"(?:Admission Date|Admitted)[:\s]+(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", RegexOptions.IgnoreCase), "encounter.admissionDate"),
        (new Regex(@"(?:Department|Ward|Unit)[:\s]+(.+)", RegexOptions.IgnoreCase | RegexOptions.Multiline), "encounter.department"),
        (new Regex(@"(?:Room(?:/Bed)?|Bed)[:\s]+(.+)", RegexOptions.IgnoreCase | RegexOptions.Multiline), "encounter.roomBed"),
        (new Regex(@"HR[:\s]+(\d+(?:\.\d+)?)", RegexOptions.IgnoreCase), "observation.HEART_RATE.value"),
        (new Regex(@"(?:Heart Rate|Pulse)[:\s]+(\d+(?:\.\d+)?)", RegexOptions.IgnoreCase), "observation.HEART_RATE.value"),
        (new Regex(@"(?:Temp|Temperature)[:\s]+(\d+(?:\.\d+)?)", RegexOptions.IgnoreCase), "observation.TEMP_C.value"),
        (new Regex(@"(?:BP Sys|Systolic)[:\s]+(\d+(?:\.\d+)?)", RegexOptions.IgnoreCase), "observation.BP_SYSTOLIC.value"),
        (new Regex(@"(?:BP Dia|Diastolic)[:\s]+(\d+(?:\.\d+)?)", RegexOptions.IgnoreCase), "observation.BP_DIASTOLIC.value"),
        (new Regex(@"(?:RR|Resp(?:iratory)?\s*Rate)[:\s]+(\d+(?:\.\d+)?)", RegexOptions.IgnoreCase), "observation.RESP_RATE.value"),
        (new Regex(@"(?:SpO2|O2 Sat)[:\s]+(\d+(?:\.\d+)?)", RegexOptions.IgnoreCase), "observation.SPO2.value"),
    ];

    private readonly TesseractOcrOptions _options;
    private readonly ImagePreprocessor _preprocessor;
    private readonly ILogger<TesseractOcrService> _logger;

    public TesseractOcrService(
        IOptions<OcrOptions> options,
        ImagePreprocessor preprocessor,
        ILogger<TesseractOcrService> logger)
    {
        _options = options.Value.Tesseract;
        _preprocessor = preprocessor;
        _logger = logger;
    }

    public async Task<OcrExtractionResult> ExtractAsync(
        Stream documentStream, string contentType)
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();

        using var preprocessed = _preprocessor.Preprocess(documentStream, contentType);
        using var memStream = new MemoryStream();
        await preprocessed.CopyToAsync(memStream);
        var imageBytes = memStream.ToArray();

        using var engine = new TesseractEngine(
            _options.DataPath, _options.Language, EngineMode.Default);
        using var pix = Pix.LoadFromMemory(imageBytes);
        using var page = engine.Process(pix);

        var rawText = page.GetText();
        var meanConfidence = page.GetMeanConfidence();
        var fields = ParseClinicalText(rawText, meanConfidence);

        sw.Stop();
        return new OcrExtractionResult(fields, rawText, (int)sw.ElapsedMilliseconds);
    }

    private static List<OcrExtractedField> ParseClinicalText(string text, float meanConfidence)
    {
        var fields = new List<OcrExtractedField>();
        if (string.IsNullOrWhiteSpace(text))
            return fields;

        var baseConfidence = Math.Clamp(meanConfidence / 100f, 0.0, 1.0);
        var matchedFields = new HashSet<string>(StringComparer.Ordinal);

        foreach (var (pattern, fieldName) in ClinicalPatterns)
        {
            if (matchedFields.Contains(fieldName))
                continue;

            var match = pattern.Match(text);
            if (!match.Success)
                continue;

            var value = match.Groups[1].Value.Trim();
            if (value.Length == 0)
                continue;

            fields.Add(new OcrExtractedField(fieldName, value, baseConfidence));
            matchedFields.Add(fieldName);
        }

        return fields;
    }
}
