using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Minio;
using Minio.DataModel.Args;
using Minio.Exceptions;

public sealed class DischargeSummaryService : IDischargeSummaryService
{
    private readonly AppDbContext _db;
    private readonly MinioOptions _minioOpts;

    public DischargeSummaryService(AppDbContext db, IOptions<MinioOptions> minioOpts)
    {
        _db = db;
        _minioOpts = minioOpts.Value;
    }

    public async Task<DischargeSummaryInfo> GetInfoAsync(Guid encounterId, CancellationToken ct = default)
    {
        var encounter = await _db.Encounters
            .AsNoTracking()
            .FirstOrDefaultAsync(e => e.Id == encounterId, ct);

        if (encounter is null)
            throw new NotFoundException("Encounter not found.");

        if (encounter.Status != EncounterStatus.Discharged)
        {
            return new DischargeSummaryInfo(
                "NotDischarged",
                encounter.DischargedAt,
                null,
                null);
        }

        var exists = await ObjectExistsAsync(encounterId, ct);
        if (!exists)
        {
            return new DischargeSummaryInfo(
                "Pending",
                encounter.DischargedAt,
                null,
                null);
        }

        return new DischargeSummaryInfo(
            "Ready",
            encounter.DischargedAt,
            "text/plain",
            "discharge-summary.pdf");
    }

    public async Task<DischargeSummaryContent> GetContentAsync(Guid encounterId, CancellationToken ct = default)
    {
        var info = await GetInfoAsync(encounterId, ct);

        if (info.Status == "NotDischarged")
        {
            throw new ConflictException(
                "Discharge summary is only available for discharged encounters.",
                "ENCOUNTER_NOT_DISCHARGED");
        }

        if (info.Status == "Pending")
        {
            throw new NotFoundException(
                "Discharge summary is still being generated.",
                "DISCHARGE_SUMMARY_PENDING");
        }

        var client = MinioClientFactory.Build(_minioOpts);
        var bucket = _minioOpts.BucketName;
        var objectKey = ObjectKey(encounterId);
        var ms = new MemoryStream();

        await client.GetObjectAsync(new GetObjectArgs()
            .WithBucket(bucket)
            .WithObject(objectKey)
            .WithCallbackStream(stream => stream.CopyTo(ms)), ct);

        ms.Position = 0;
        return new DischargeSummaryContent(ms, info.ContentType!, info.FileName!);
    }

    private async Task<bool> ObjectExistsAsync(Guid encounterId, CancellationToken ct)
    {
        var client = MinioClientFactory.Build(_minioOpts);
        var bucket = _minioOpts.BucketName;
        var objectKey = ObjectKey(encounterId);

        try
        {
            await client.StatObjectAsync(new StatObjectArgs()
                .WithBucket(bucket)
                .WithObject(objectKey), ct);
            return true;
        }
        catch (ObjectNotFoundException)
        {
            return false;
        }
    }

    private static string ObjectKey(Guid encounterId) =>
        $"discharge-summaries/{encounterId}/summary.pdf";
}
