# 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
}

# ------------------------------------------------------------
# ---------------------- 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
    moreThan: Time
    moreThanOrEqualTo: Time
}

input IntFilter {
    equalTo: Int
    notEqualTo: Int
    lessThan: Int
    lessThanOrEqualTo: Int
    moreThan: Int
    moreThanOrEqualTo: Int
    in: [Int!]
    notIn: [Int!]
}

input FloatFilter {
    equalTo: Float
    notEqualTo: Float
    lessThan: Float
    lessThanOrEqualTo: Float
    moreThan: Float
    moreThanOrEqualTo: Float
    in: [Float!]
    notIn: [Float!]
}

input BooleanFilter {
    equalTo: Boolean
    notEqualTo: Boolean
}

input IDFilter {
    equalTo: ID
    notEqualTo: ID
    in: [ID!]
    notIn: [ID!]
}

input StringFilter {
    equalTo: String
    notEqualTo: String
    startWith: String
    notStartWith: String
    endWith: String
    notEndWith: String
    contain: String
    notContain: String
    in: [String!]
    notIn: [String!]

    startWithStrict: String
    notStartWithStrict: String
    endWithStrict: String
    notEndWithStrict: String
    containStrict: String
    notContainStrict: String
}

input RoleFilter {
    equalTo: Role
    notEqualTo: Role
    in: [Role!]
    notIn: [Role!]
}

input DeviceTypeFilter {
    equalTo: DeviceType
    notEqualTo: DeviceType
    in: [DeviceType!]
    notIn: [DeviceType!]
}

input AuthTypeFilter {
    equalTo: AuthType
    notEqualTo: AuthType
    in: [AuthType!]
    notIn: [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")
    userID: ID!         @meta(gorm: "not null")
    refreshKey: String  @isPrivate
}

type MediaItem {
    id: ID!             @meta(gorm: "primaryKey;not null")
    createdAt: Time
    updatedAt: Time
    exifDate: Time
    latitude: Float
    longitude: Float
    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")
    albums: [Album]     @meta(gorm: "many2many:media_albums")
    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")
}

type Album {
    id: ID!                     @meta(gorm: "primaryKey;not null")
    createdAt: Time
    updatedAt: Time
    name: String!               @meta(gorm: "unique;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!
}

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

type PageInfo {
    count: Int!
    page: Int!
    total: Int!
}

type MediaItemResponse {
    data: [MediaItem]
    pageInfo: PageInfo!
}

type UserResponse {
    data: [User]
    pageInfo: PageInfo!
}

type DeviceResponse {
    data: [Device]
    pageInfo: PageInfo!
}

type TagResponse {
    data: [Tag]
    pageInfo: PageInfo!
}

type AlbumResponse {
    data: [Album]
    pageInfo: PageInfo!
}

# ------------------------------------------------------------
# --------------------- 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
        count: Int
        page: Int
    ): MediaItemResponse!           @hasMinRole(role: User)
    devices(
        filter: DeviceFilter
        count: Int
        page: Int
    ): DeviceResponse!              @hasMinRole(role: User)
    albums(
        filter: AlbumFilter
        count: Int
        page: Int
    ): AlbumResponse!               @hasMinRole(role: User)
    tags(
        filter: TagFilter
        count: Int
        page: Int
    ): TagResponse!                 @hasMinRole(role: User)
    users(
        filter: UserFilter
        count: Int
        page: Int
    ): 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)
}