//
//  AppVersion.swift
//  azooKey
//
//  Created by ensan on 2022/07/02.
//  Copyright © 2022 ensan. All rights reserved.
//

import Foundation

/// AppVersion is a struct that represents a version of an app.
/// It is a wrapper of String that conforms to Codable, Equatable, Comparable, Hashable, LosslessStringConvertible, CustomStringConvertible.
/// It is initialized with a string that represents a version of an app.
/// The string must be in the format of "major.minor.patch".
/// The string must not contain any other characters than numbers and dots.
public struct AppVersion: Codable, Equatable, Comparable, Hashable, LosslessStringConvertible, CustomStringConvertible, Sendable {

    /// ParseError is an enum that represents an error that occurs when parsing a string to an AppVersion.
    private enum ParseError: Error {
        case nonIntegerValue
    }

    /// Initializes an AppVersion with a string that represents a version of an app.
    public init?(_ description: String) {
        if let versionSequence = try? description.split(separator: ".").map({ (value: Substring) throws -> Int in
            guard let value = Int(value) else { throw ParseError.nonIntegerValue }
            return value
        }) {
            if versionSequence.count < 1 {
                self.majorVersion = 0
            } else {
                self.majorVersion = versionSequence[0]
            }

            if versionSequence.count < 2 {
                self.minorVersion = 0
            } else {
                self.minorVersion = versionSequence[1]
            }

            if versionSequence.count < 3 {
                self.patchVersion = 0
            } else {
                self.patchVersion = versionSequence[2]
            }
        } else {
            return nil
        }
    }

    /// Compares two AppVersions.
    public static func < (lhs: AppVersion, rhs: AppVersion) -> Bool {
        for (l, r) in zip([lhs.majorVersion, lhs.minorVersion, lhs.patchVersion], [rhs.majorVersion, rhs.minorVersion, rhs.patchVersion]) {
            if l == r {
                continue
            }
            return l < r
        }
        return false
    }

    public var majorVersion: Int
    public var minorVersion: Int
    public var patchVersion: Int

    public var description: String {
        "\(majorVersion).\(minorVersion).\(patchVersion)"
    }
}
