import Foundation

struct DiagnosticReportFile: Identifiable, Equatable {
    let url: URL
    let createdAt: Date
    let sizeBytes: Int64

    var id: String { url.path }
    var filename: String { url.lastPathComponent }
    var snapshotID: UUID? { Self.uuidValue(after: "_snapshot-", in: filename) }
    var observationID: Int64? { Self.int64Value(after: "_observation-", in: filename) }

    var displayTitle: String {
        filename
            .replacingOccurrences(of: ".txt", with: "")
            .replacingOccurrences(of: "_", with: " ")
    }

    var displaySubtitle: String {
        let size = ByteCountFormatter.string(fromByteCount: sizeBytes, countStyle: .file)
        return "\(DiagnosticReportStore.displayFormatter.string(from: createdAt)) - \(size)"
    }

    private static func uuidValue(after marker: String, in value: String) -> UUID? {
        guard let markerRange = value.range(of: marker) else { return nil }
        let suffix = value[markerRange.upperBound...]
        let rawValue = suffix.split(separator: "_", maxSplits: 1).first?
            .replacingOccurrences(of: ".txt", with: "")
        return rawValue.flatMap(UUID.init(uuidString:))
    }

    private static func int64Value(after marker: String, in value: String) -> Int64? {
        guard let markerRange = value.range(of: marker) else { return nil }
        let suffix = value[markerRange.upperBound...]
        let rawValue = suffix.split(separator: "_", maxSplits: 1).first?
            .replacingOccurrences(of: ".txt", with: "")
        return rawValue.flatMap(Int64.init)
    }
}

enum DiagnosticReportStore {
    static let directory = URL.applicationSupportDirectory
        .appending(path: "HealthProbeDiagnostics", directoryHint: .isDirectory)

    static let displayFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .short
        return formatter
    }()

    static func persist(text: String, snapshotID: UUID, observationID: Int64?, operationID: UUID?) throws {
        try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)

        let timestamp = Self.filenameTimestamp(Date())
        let observationPart = observationID.map { "_observation-\($0)" } ?? ""
        let operationPart = operationID.map { "_operation-\($0.uuidString)" } ?? ""
        let filename = "\(timestamp)_snapshot-\(snapshotID.uuidString)\(observationPart)\(operationPart).txt"
        let fileURL = directory.appending(path: filename, directoryHint: .notDirectory)
        try text.write(to: fileURL, atomically: true, encoding: .utf8)
    }

    static func list(limit: Int? = nil) throws -> [DiagnosticReportFile] {
        guard FileManager.default.fileExists(atPath: directory.path) else {
            return []
        }

        let urls = try FileManager.default.contentsOfDirectory(
            at: directory,
            includingPropertiesForKeys: [.creationDateKey, .contentModificationDateKey, .fileSizeKey],
            options: [.skipsHiddenFiles]
        )

        let reports = urls
            .filter { $0.pathExtension == "txt" }
            .compactMap(reportFile)
            .sorted { lhs, rhs in
                if lhs.createdAt == rhs.createdAt {
                    return lhs.filename > rhs.filename
                }
                return lhs.createdAt > rhs.createdAt
            }

        if let limit {
            return Array(reports.prefix(limit))
        }
        return reports
    }

    static func list(
        snapshotID: UUID?,
        observationID: Int64?,
        limit: Int? = nil
    ) throws -> [DiagnosticReportFile] {
        let reports = try list(limit: nil).filter { report in
            if let observationID, report.observationID == observationID {
                return true
            }
            if let snapshotID, report.snapshotID == snapshotID {
                return true
            }
            return false
        }

        if let limit {
            return Array(reports.prefix(limit))
        }
        return reports
    }

    static func read(_ report: DiagnosticReportFile) throws -> String {
        try String(contentsOf: report.url, encoding: .utf8)
    }

    static func delete(_ report: DiagnosticReportFile) throws {
        try FileManager.default.removeItem(at: report.url)
    }

    private static func reportFile(_ url: URL) -> DiagnosticReportFile? {
        guard let values = try? url.resourceValues(forKeys: [.creationDateKey, .contentModificationDateKey, .fileSizeKey]) else {
            return nil
        }
        return DiagnosticReportFile(
            url: url,
            createdAt: values.creationDate ?? values.contentModificationDate ?? Date.distantPast,
            sizeBytes: Int64(values.fileSize ?? 0)
        )
    }

    private static func filenameTimestamp(_ date: Date) -> String {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = "yyyyMMdd_HHmmss"
        return formatter.string(from: date)
    }
}
