// Local file preview only. No Health store, device or service account is used.
// Compile with the source files and revision documented in README.txt.
import Foundation

struct Sample: Codable {
    let time: String?
    let powerWatts: Double?
}

struct Preview: Codable {
    let file: String
    let sport: String?
    let start: String?
    let durationSeconds: Double?
    let distanceMeters: Double?
    let energyKilocalories: Double?
    let routePoints: Int
    let heartRateSamples: Int
    let cadenceSamples: Int
    let powerSamples: Int
    let speedSamples: Int
    let power: [Sample]
    let lossMessages: [String]
}

struct Comparison: Codable {
    let file: String
    let matches: Int
    let confidence: String?
    let reasons: [String]
}

struct Report: Codable {
    let sourceRevision: String
    let method: String
    let previews: [Preview]
    let duplicateReference: String
    let duplicateChecks: [Comparison]
    let shiftedStartControlMatches: Int
    let differentSportControlMatches: Int
}

@main struct PreviewExperiment {
    static func main() throws {
        guard CommandLine.arguments.count == 3 else {
            throw NSError(domain: "PreviewExperiment", code: 1, userInfo: [NSLocalizedDescriptionKey: "Usage: preview-import DOWNLOAD_DIRECTORY SOURCE_REVISION"])
        }
        let directory = URL(fileURLWithPath: CommandLine.arguments[1], isDirectory: true)
        let names = ["synthetic-ride.fit", "synthetic-ride.gpx", "synthetic-ride.tcx", "synthetic-ride-core-only.gpx"]
        let formatter = ISO8601DateFormatter()
        var drafts: [ImportedWorkoutDraft] = []
        var previews: [Preview] = []
        for name in names {
            let data = try WorkoutImportParser.readBoundedData(from: directory.appendingPathComponent(name))
            let draft = try WorkoutImportParser.parse(data: data, fileName: name)
            drafts.append(draft)
            previews.append(Preview(file: name, sport: draft.sport, start: draft.start.map(formatter.string),
                durationSeconds: draft.durationSeconds, distanceMeters: draft.distanceMeters,
                energyKilocalories: draft.energyKilocalories, routePoints: draft.routePointCount,
                heartRateSamples: draft.heartRateSampleCount, cadenceSamples: draft.cadenceSampleCount,
                powerSamples: draft.powerSampleCount, speedSamples: draft.speedSampleCount,
                power: draft.points.map { Sample(time: $0.date.map(formatter.string), powerWatts: $0.power) },
                lossMessages: draft.losses))
        }
        func candidate(_ draft: ImportedWorkoutDraft, id: String, start: Date? = nil, sport: String? = nil) -> ImportDuplicateCandidate {
            ImportDuplicateCandidate(id: id, name: id, sport: sport ?? draft.sport,
                start: start ?? draft.start, durationSeconds: draft.durationSeconds, distanceMeters: draft.distanceMeters)
        }
        let reference = candidate(drafts[0], id: "synthetic existing FIT preview")
        var comparisons: [Comparison] = []
        for index in 1..<names.count {
            let matches = ImportDuplicateChecker.matches(importCandidate: candidate(drafts[index], id: names[index]), existing: [reference])
            comparisons.append(Comparison(file: names[index], matches: matches.count,
                confidence: matches.first?.confidence.rawValue, reasons: matches.first?.reasons ?? []))
        }
        // An unchanged FIT under a new filename still describes the same workout.
        let renamedData = try WorkoutImportParser.readBoundedData(from: directory.appendingPathComponent(names[0]))
        let renamed = try WorkoutImportParser.parse(data: renamedData, fileName: "renamed-ride.fit")
        let renamedMatches = ImportDuplicateChecker.matches(importCandidate: candidate(renamed, id: "renamed-ride.fit"), existing: [reference])
        comparisons.append(Comparison(file: "renamed-ride.fit (unchanged FIT bytes)", matches: renamedMatches.count,
            confidence: renamedMatches.first?.confidence.rawValue, reasons: renamedMatches.first?.reasons ?? []))
        let shifted = ImportDuplicateChecker.matches(importCandidate: candidate(drafts[0], id: "four hours later",
            start: drafts[0].start?.addingTimeInterval(4 * 3600)), existing: [reference])
        let otherSport = ImportDuplicateChecker.matches(importCandidate: candidate(drafts[0], id: "different sport", sport: "swimming"), existing: [reference])
        guard previews.allSatisfy({ $0.routePoints == 6 }),
              previews.prefix(3).allSatisfy({ $0.powerSamples == 5 && $0.power[2].powerWatts == 0 && $0.power[3].powerWatts == nil }),
              previews[3].powerSamples == 0,
              comparisons.allSatisfy({ $0.matches == 1 }), shifted.isEmpty, otherSport.isEmpty else {
            throw NSError(domain: "PreviewExperiment", code: 2, userInfo: [NSLocalizedDescriptionKey: "The published sample expectations did not hold; inspect the source and files."])
        }
        let report = Report(sourceRevision: CommandLine.arguments[2],
            method: "Actual WorkoutPort file parser and duplicate checker; synthetic files and a synthetic existing candidate. No Health write, app UI, upload or destination-service test.",
            previews: previews, duplicateReference: reference.name, duplicateChecks: comparisons,
            shiftedStartControlMatches: shifted.count, differentSportControlMatches: otherSport.count)
        let encoder = JSONEncoder()
        encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
        print(String(decoding: try encoder.encode(report), as: UTF8.self))
    }
}
