# https://gqlgen.com/reference/scalars/
scalar Time
scalar Upload

# https://gqlgen.com/reference/directives/
directive @hasMinRole(role: Role!) on FIELD_DEFINITION
directive @isPrivate on FIELD_DEFINITION | INPUT_FIELD_DEFINITION

directive @meta(
    gorm: String,
) on OBJECT | FIELD_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION | ENUM | INPUT_OBJECT | ARGUMENT_DEFINITION

enum Role {
    Admin
    User
}

enum DeviceType {
    iOS
    Android
    Chrome
    Firefox
    InternetExplorer
    Edge
    Safari
    Unknown
}

enum AuthType {
    Local
    LDAP
}

enum OrderDirection {
    ASC
    DESC
}

# ------------------------------------------------------------
# ---------------------- Authentication ----------------------
# ------------------------------------------------------------

enum AuthResult {
    Success
    Failure
}

type AuthResponse {
    result: AuthResult!
    device: Device
    error: String
}

# ------------------------------------------------------------
# ----------------------- Type Filters -----------------------
# ------------------------------------------------------------

input TimeFilter {
    equalTo: Time
    notEqualTo: Time
    lessThan: Time
    lessThanOrEqualTo: Time
    greaterThan: Time
    greaterThanOrEqualTo: Time
}

input IntFilter {
    equalTo: Int
    notEqualTo: Int
    lessThan: Int
    lessThanOrEqualTo: Int
    greaterThan: Int
    greaterThanOrEqualTo: Int
}

input FloatFilter {
    equalTo: Float
    notEqualTo: Float
    lessThan: Float
    lessThanOrEqualTo: Float
    greaterThan: Float
    greaterThanOrEqualTo: Float
}

input BooleanFilter {
    equalTo: Boolean
    notEqualTo: Boolean
}

input IDFilter {
    equalTo: ID
    notEqualTo: ID
}

input StringFilter {
    equalTo: String
    notEqualTo: String
    startsWith: String
    notStartsWith: String
    endsWith: String
    notEndsWith: String
    contains: String
    notContains: String
}

input RoleFilter {
    equalTo: Role
    notEqualTo: Role
}

input DeviceTypeFilter {
    equalTo: DeviceType
    notEqualTo: DeviceType
}

input AuthTypeFilter {
    equalTo: AuthType
    notEqualTo: AuthType
}

# ------------------------------------------------------------
# -------------------- Object Definitions --------------------
# ------------------------------------------------------------

type User {
    id: ID!                     @meta(gorm: "primaryKey;not null")
    createdAt: Time
    updatedAt: Time
    email: String!              @meta(gorm: "not null;unique")
    username: String!           @meta(gorm: "not null;unique")
    firstName: String
    lastName: String
    role: Role!                 @meta(gorm: "default:User;not null")
    authType: AuthType!         @meta(gorm: "default:Local;not null")
    password: String            @isPrivate
    devices: [Device!]          @meta(gorm: "foreignKey:UserID")
    mediaItems: [MediaItem!]    @meta(gorm: "foreignKey:UserID")
}

type Device {
    id: ID!             @meta(gorm: "primaryKey;not null")
    createdAt: Time
    updatedAt: Time
    name: String!       @meta(gorm: "not null")
    type: DeviceType!   @meta(gorm: "default:Unknown;not null")
    refreshKey: String  @isPrivate
    userID: ID!         @meta(gorm: "not null")
}

type MediaItem {
    id: ID!             @meta(gorm: "primaryKey;not null")
    createdAt: Time
    updatedAt: Time
    exifDate: Time
    latitude: Float     @meta(gorm: "precision:5")
    longitude: Float    @meta(gorm: "precision:5")
    isVideo: Boolean!   @meta(gorm: "default:false;not null")
    fileName: String!   @meta(gorm: "not null")
    origName: String!   @meta(gorm: "not null")
    tags: [Tag]         @meta(gorm: "many2many:media_tags;foreignKey:ID,UserID;References:ID")
    albums: [Album]     @meta(gorm: "many2many:media_albums;foreignKey:ID,UserID;Refrences:ID")
    userID: ID!         @meta(gorm: "not null")
}

type Tag {
    id: ID!             @meta(gorm: "primaryKey;not null")
    createdAt: Time
    updatedAt: Time
    name: String!       @meta(gorm: "unique;not null")
    userID: ID!         @meta(gorm: "not null")
}

type Album {
    id: ID!         @meta(gorm: "primaryKey;not null")
    createdAt: Time
    updatedAt: Time
    name: String!   @meta(gorm: "unique;not null")
    userID: ID!     @meta(gorm: "not null")
}

# ------------------------------------------------------------
# ---------------------- Object Filters ----------------------
# ------------------------------------------------------------

input UserFilter {
    id: IDFilter
    createdAt: TimeFilter
    updatedAt: TimeFilter
    username: StringFilter
    firstName: StringFilter
    lastName: StringFilter
    role: RoleFilter
    authType: AuthTypeFilter

    # and: UserFilter
    # or: UserFilter
}

input MediaItemFilter {
    id: IDFilter
    createdAt: TimeFilter
    updatedAt: TimeFilter
    exifDate: TimeFilter
    latitude: FloatFilter
    longitude: FloatFilter
    isVideo: BooleanFilter
    origName: StringFilter
    tags: TagFilter
    albums: AlbumFilter

    # and: MediaItemFilter
    # or: MediaItemFilter
}

input DeviceFilter {
    id: IDFilter
    createdAt: TimeFilter
    updatedAt: TimeFilter
    name: StringFilter
    type: DeviceTypeFilter

    # and: MediaItemFilter
    # or: MediaItemFilter
}

input TagFilter {
    id: IDFilter
    createdAt: TimeFilter
    updatedAt: TimeFilter
    name: StringFilter

    # and: MediaItemFilter
    # or: MediaItemFilter
}

input AlbumFilter {
    id: IDFilter
    createdAt: TimeFilter
    updatedAt: TimeFilter
    name: StringFilter

    # and: MediaItemFilter
    # or: MediaItemFilter
}

# ------------------------------------------------------------
# -------------------------- Inputs --------------------------
# ------------------------------------------------------------

input NewUser {
    email: String!
    username: String!
    firstName: String
    lastName: String
    role: Role!
    authType: AuthType!
    password: String
}

input NewMediaItem {
    file: Upload!
    tags: [ID!]
    albums: [ID!]
}

input NewTag {
    name: String!
}

input NewAlbum {
    name: String!
}

input Page {
    size: Int
    page: Int
}

input Order {
    by: String
    direction: OrderDirection
}

# ------------------------------------------------------------
# ------------------------ Responses -------------------------
# ------------------------------------------------------------

type PageResponse {
    size: Int!
    page: Int!
    total: Int!
}

type MediaItemResponse {
    data: [MediaItem]
    page: PageResponse!
}

type UserResponse {
    data: [User]
    page: PageResponse!
}

type DeviceResponse {
    data: [Device]
    page: PageResponse!
}

type TagResponse {
    data: [Tag]
    page: PageResponse!
}

type AlbumResponse {
    data: [Album]
    page: PageResponse!
}

# ------------------------------------------------------------
# --------------------- Query & Mutations --------------------
# ------------------------------------------------------------

type Query {
    # Authentication
    login(
        user: String!
        password: String!
        deviceID: ID
    ): AuthResponse!
    logout: AuthResponse!   @hasMinRole(role: User)

    # Single Item
    mediaItem(
        id: ID!
    ): MediaItem!   @hasMinRole(role: User)
    device(
        id: ID!
    ): Device!      @hasMinRole(role: User)
    album(
        id: ID!
    ): Album!       @hasMinRole(role: User)
    user(
        id: ID!
    ): User!        @hasMinRole(role: Admin)
    tag(
        id: ID!
    ): Tag!         @hasMinRole(role: User)
    me: User!       @hasMinRole(role: User)

    # All
    mediaItems(
        filter: MediaItemFilter
        page: Page
        order: Order
    ): MediaItemResponse!       @hasMinRole(role: User)
    devices(
        filter: DeviceFilter
        page: Page
        order: Order
    ): DeviceResponse!          @hasMinRole(role: User)
    albums(
        filter: AlbumFilter
        page: Page
        order: Order
    ): AlbumResponse!           @hasMinRole(role: User)
    tags(
        filter: TagFilter
        page: Page
        order: Order
    ): TagResponse!             @hasMinRole(role: User)
    users(
        filter: UserFilter
        page: Page
        order: Order
    ): UserResponse!            @hasMinRole(role: Admin)
}

type Mutation {
    createMediaItem(input: NewMediaItem!): MediaItem!   @hasMinRole(role: User)
    createAlbum(input: NewAlbum!): Album!               @hasMinRole(role: User)
    createTag(input: NewTag!): Tag!                     @hasMinRole(role: User)
    createUser(input: NewUser!): User!                  @hasMinRole(role: Admin)
}