import AVFoundation
import Photos

enum FingerprintService {
    /// Caller must already hold security-scoped access to the URL if it needs it.
    static func fingerprint(forFileAt url: URL) async throws -> ClipFingerprint {
        let values = try? url.resourceValues(forKeys: [.fileSizeKey, .creationDateKey])
        let asset = AVURLAsset(url: url)
        let duration = try await asset.load(.duration).seconds
        var width = 0
        var height = 0
        if let track = try await asset.loadTracks(withMediaType: .video).first {
            let (naturalSize, transform) = try await track.load(.naturalSize, .preferredTransform)
            // Apply the rotation flag so portrait footage fingerprints as portrait.
            let rect = CGRect(origin: .zero, size: naturalSize).applying(transform)
            width = Int(abs(rect.width).rounded())
            height = Int(abs(rect.height).rounded())
        }
        return ClipFingerprint(
            duration: duration,
            pixelWidth: width,
            pixelHeight: height,
            fileSize: values?.fileSize.map(Int64.init),
            creationDate: values?.creationDate
        )
    }

    static func fingerprint(for phAsset: PHAsset) async -> ClipFingerprint {
        var fileSize: Int64?
        if let avAsset = await PhotosService.requestAVAsset(for: phAsset) as? AVURLAsset {
            fileSize = (try? avAsset.url.resourceValues(forKeys: [.fileSizeKey]))?.fileSize.map(Int64.init)
        }
        return ClipFingerprint(
            duration: phAsset.duration,
            pixelWidth: phAsset.pixelWidth,
            pixelHeight: phAsset.pixelHeight,
            fileSize: fileSize,
            creationDate: phAsset.creationDate
        )
    }

    /// 0–1 confidence that `candidate` is the same footage as `target`.
    /// Duration and dimensions are the strong signals; exact file size means the
    /// bytes were copied unmodified; creation date is a weak tiebreaker.
    static func matchScore(candidate: ClipFingerprint, target: ClipFingerprint) -> Double {
        var score = 0.0
        let durationDelta = abs(candidate.duration - target.duration)
        if durationDelta <= 0.15 {
            score += 0.5
        } else if durationDelta <= 1.0 {
            score += 0.2
        }
        if candidate.pixelWidth == target.pixelWidth && candidate.pixelHeight == target.pixelHeight {
            score += 0.3
        }
        if let candidateSize = candidate.fileSize, let targetSize = target.fileSize, candidateSize == targetSize {
            score += 0.2
        }
        if let candidateDate = candidate.creationDate, let targetDate = target.creationDate,
           abs(candidateDate.timeIntervalSince(targetDate)) < 60 {
            score += 0.1
        }
        return min(score, 1.0)
    }
}
