using Health.Domain.Entities;
using Health.Domain.Enums;

namespace Health.Application.Reports;

public sealed class ReportService(
    IReportRepository reports,
    IReportFileStorage fileStorage,
    IReportAnalysisQueue queue) : IReportService
{
    private const long MaxReportFileBytes = 20 * 1024 * 1024;
    private static readonly HashSet<string> AllowedExtensions = new(StringComparer.OrdinalIgnoreCase)
    {
        ".jpg", ".jpeg", ".png", ".webp", ".pdf"
    };

    private readonly IReportRepository _reports = reports;
    private readonly IReportFileStorage _fileStorage = fileStorage;
    private readonly IReportAnalysisQueue _queue = queue;

    public async Task<IReadOnlyList<ReportDto>> GetReportsAsync(Guid userId, CancellationToken ct)
    {
        var result = await _reports.ListAsync(userId, ct);
        return result.Select(ToDto).ToList();
    }

    public async Task<ReportDto?> GetReportAsync(Guid userId, Guid reportId, CancellationToken ct)
    {
        var report = await _reports.GetOwnedAsync(userId, reportId, ct);
        return report == null ? null : ToDto(report);
    }

    public async Task<ReportUploadResult> UploadReportAsync(Guid userId, ReportUploadFile file, CancellationToken ct)
    {
        if (file.Length <= 0)
            return Fail(400, "未上传文件");

        if (file.Length > MaxReportFileBytes)
            return Fail(400, "报告文件不能超过 20MB，请压缩后重新上传");

        var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
        if (!AllowedExtensions.Contains(ext))
            return Fail(400, "目前支持上传 JPG、PNG、WEBP 图片或 PDF 报告");

        var storedFile = await _fileStorage.SaveAsync(file, ext, ct);
        var report = new Report
        {
            Id = Guid.NewGuid(),
            UserId = userId,
            FileUrl = storedFile.FileUrl,
            FileType = ext == ".pdf" ? ReportFileType.Pdf : ReportFileType.Image,
            Category = ReportCategory.Other,
            Status = ReportStatus.Analyzing,
            AiSummary = null,
            AiIndicators = null,
            CreatedAt = DateTime.UtcNow,
        };

        await _reports.AddAsync(report, ct);
        await _queue.EnqueueAsync(new ReportAnalysisJob(Guid.NewGuid(), report.Id, storedFile.FilePath), ct);

        return new ReportUploadResult(true, 0, "报告已上传，AI 正在分析中", ToDto(report));
    }

    public async Task<bool> DeleteReportAsync(Guid userId, Guid reportId, CancellationToken ct)
    {
        var report = await _reports.GetOwnedAsync(userId, reportId, ct);
        if (report == null) return false;

        var filePath = _fileStorage.GetLocalFilePath(report.FileUrl);
        if (_fileStorage.Exists(filePath))
            _fileStorage.Delete(filePath);

        _reports.Delete(report);
        await _reports.SaveChangesAsync(ct);
        return true;
    }

    public async Task<bool> ReanalyzeReportAsync(Guid userId, Guid reportId, CancellationToken ct)
    {
        var report = await _reports.GetOwnedAsync(userId, reportId, ct);
        if (report == null) return false;

        var filePath = _fileStorage.GetLocalFilePath(report.FileUrl);
        if (!_fileStorage.Exists(filePath)) return false;

        report.Status = ReportStatus.Analyzing;
        report.AiSummary = null;
        report.AiIndicators = null;
        await _queue.EnqueueAsync(new ReportAnalysisJob(Guid.NewGuid(), report.Id, filePath), ct);
        return true;
    }

    public static ReportDto ToDto(Report report) => new(
        report.Id,
        report.UserId,
        report.FileUrl,
        report.FileType.ToString(),
        report.Category.ToString(),
        report.Status.ToString(),
        ToAiStatus(report),
        report.Status == ReportStatus.DoctorReviewed ? "Reviewed" : "Pending",
        report.Severity,
        report.AiSummary,
        report.AiIndicators,
        report.DoctorComment,
        report.DoctorRecommendation,
        report.DoctorName,
        report.ReviewedAt,
        report.CreatedAt);

    private static string ToAiStatus(Report report) => report.Status switch
    {
        ReportStatus.Analyzing => "Analyzing",
        ReportStatus.AnalysisFailed => "Failed",
        _ when string.IsNullOrWhiteSpace(report.AiSummary) => "Analyzing",
        _ => "Succeeded"
    };

    private static ReportUploadResult Fail(int code, string message) =>
        new(false, code, message, null);
}
