/// The parent relation is one side of a one-to-many database relation.
///
/// The parent relation will return the parent model that the supplied child references.
///
/// The opposite side of this relation is called `Children`.
///
///     final class Pet: Model {
///         var userID: UUID
///         ...
///         var user: Parent<Pet, User> {
///             return parent(\.userID)
///         }
///     }
///
///     final class User: Model {
///         var id: UUID?
///         ...
///     }
///
/// Create a new `Parent` relation using the `Model.parent(...)` method. The method accepts
/// a key path to the child's property that references its parent's ID.
///
/// `Parent` structs (and other relations) are usually generated by stored properties on the model.
public struct Parent<Child, Parent>
    where Child: Model, Parent: Model, Child.Database == Parent.Database
{
    /// The child object with reference to parent.
    public var child: Child

    /// The parent's ID.
    public var parentID: Parent.ID

    /// Creates a new children relationship.
    internal init(child: Child, parentID: Parent.ID) {
        self.child = child
        self.parentID = parentID
    }
}

extension Parent where Child.Database: QuerySupporting {
    /// Create a query for the parent.
    ///
    ///     let pet: Pet
    ///     pet.user.query(on: conn).first()
    ///
    /// - parameters:
    ///     - conn: Database connection to use.
    /// - returns: Newly created `QueryBuilder`.
    public func query(on conn: DatabaseConnectable) -> QueryBuilder<Parent.Database, Parent> {
        return Parent.query(on: conn)
            .filter(Parent.idKey == parentID)
    }

    /// Convenience for getting the parent.
    ///
    ///     let pet: Pet
    ///     pet.user.get()
    ///
    /// This method will throw an error if no `Parent` is found.
    ///
    /// - parameters:
    ///     - conn: Database connection to use.
    /// - returns: The `Parent`.
    public func get(on conn: DatabaseConnectable) -> Future<Parent> {
        return self.query(on: conn).first().map { first in
            guard let parent = first else {
                throw FluentError(identifier: "parentRequired", reason: "This parent relationship could not be resolved.")
            }
            return parent
        }
    }
}

// MARK: Model

extension Model {
    /// Create a children relation for this model.
    ///
    ///     var user: Parent<User, Pet> {
    ///         return parent(\.userID)
    ///     }
    ///
    /// The `parentID` should refer to the field on the child entity that contains the parent's ID.
    public func parent<P>(_ parentID: KeyPath<Self, P.ID>) -> Parent<Self, P> where P: Model {
        return Parent(
            child: self,
            parentID: self[keyPath: parentID]
        )
    }

    /// Create a children relation for this model.
    ///
    ///     var user: Parent<Pet, User>? {
    ///         return parent(\.userID)
    ///     }
    ///
    /// This method supports an optional relation and will return `nil` if the parent ID is `nil`.
    ///
    /// The `parentID` should refer to the field on the child entity that contains the parent's ID.
    public func parent<P>(_ parentID: KeyPath<Self, P.ID?>) -> Parent<Self, P>? where P: Model {
        guard let parentID = self[keyPath: parentID] else {
            return nil
        }

        return Parent(child: self, parentID: parentID)
    }
}

