// Copyright © 2024 Ryan Booker. All rights reserved.

import ArgumentParser
import Foundation

public enum Either<First, Second> {
    case first(First)
    case second(Second)

    public func reduce<T>(
        first firstResult: (First) -> T,
        second secondResult: (Second) -> T
    ) -> T {
        switch self {
        case let .first(value):
            firstResult(value)
        case let .second(error):
            secondResult(error)
        }
    }
}

extension Either: CustomStringConvertible
where First: CustomStringConvertible, Second: CustomStringConvertible {
    public var description: String {
        reduce(first: \.description, second: \.description)
    }
}

extension Either: Decodable
where First: Decodable, Second: Decodable {
    public init(from decoder: Decoder) throws {
        self = try tryBoth(decoder, first: First.init, second: Second.init)
    }
}

extension Either: ExpressibleByArgument
where First: ExpressibleByArgument, Second: ExpressibleByArgument {
    public init?(argument: String) {
        if let value = First(argument: argument) {
            self = .first(value)
        } else if let value = Second(argument: argument) {
            self = .second(value)
        } else {
            return nil
        }
    }
}

public func tryBoth<A, First, Second>(
    _ value: A,
    first: (A) throws -> First,
    second: (A) throws -> Second
) throws -> Either<First, Second> {
    do {
        return try .first(first(value))
    } catch let firstError {
        do {
            return try .second(second(value))
        } catch let secondError {
            throw [firstError, secondError]
        }
    }
}

extension Array: Error where Element: Error {}