import { Injectable } from '@angular/core'

import { Observable, BehaviorSubject, forkJoin, of as observableOf } from 'rxjs'
import { map } from 'rxjs/operators'

// import { Apollo, gql } from 'apollo-angular'

import { MatDialog } from '@angular/material/dialog'

import { GisafAdminAccessRoleDialogComponent } from './role-dialog.component'
import { GisafAdminAccessUserDialogComponent } from './user-dialog.component'
import { Role, User, ACL } from './models'


// const getUsersQuery = gql`
// query users {
//     users {
//         id
//         name
//         email
//         active
//     }
// }
// `

// const getRolesQuery = gql`
// query getRoles {
//     roles {
//         id
//         name
//         description
//     }
// }
// `

// const getAclsQuery = gql`
// query acls {
//     acls {
//         user_id
//         role_ids
//     }
// }
// `

// const addUserRoleMutation = gql`
// mutation addUserRole(
//     $user_id: Int!,
//     $role_id: Int!,
// ) {
//     addUserRole(
//         user_id: $user_id,
//         role_id: $role_id,
//     ) {
//         result
//     }
// }
// `

// const deleteUserRoleMutation = gql`
// mutation deleteUserRole(
//     $user_id: Int!,
//     $role_id: Int!,
// ) {
//     deleteUserRole(
//         user_id: $user_id,
//         role_id: $role_id,
//     ) {
//         result
//     }
// }
// `

// const deleteUserMutation = gql`
// mutation deleteUser(
//     $id: Int!,
// ) {
//     deleteUser(
//         id: $id,
//     ) {
//         id
//     }
// }
// `

// const deleteRoleMutation = gql`
// mutation deleteRole(
//     $id: Int!,
// ) {
//     deleteRole(
//         id: $id,
//     ) {
//         id
//     }
// }
// `

// const saveRoleMutation = gql`
// mutation saveRole(
//     $id: Int,
//     $name: String!,
//     $description: String!
// ) {
//     saveRole (
//         id: $id,
//         name: $name,
//         description: $description
//     ) {
//         id
//     }
// }
// `

// const saveUserMutation = gql`
// mutation saveUser(
//     $id: Int,
//     $name: String!,
//     $email: String!,
//     $password: String,
//     $active: Boolean!,
// ) {
//     saveUser (
//         id: $id,
//         name: $name,
//         email: $email,
//         active: $active,
//         password: $password
//     ) {
//         id
//     }
// }
// `

@Injectable()
export class AdminManageAccessDataService {
    constructor(
        // private apollo: Apollo,
        public dialog: MatDialog,
    ) {}

    private _users = new BehaviorSubject<User[]>([])
    private _roles = new BehaviorSubject<Role[]>([])
    private _acls = new BehaviorSubject<ACL[]>([])
    users$: Observable<User[]> = this._users.asObservable()
    roles$: Observable<Role[]> = this._roles.asObservable()
    acls$: Observable<ACL[]> = this._acls.asObservable()

    init() {
        this.getAllAccessData().subscribe()
        this.acls$.subscribe(
            (acls: ACL[]) => {
            /*
                acls.forEach(
                    (acl: ACL) => {
                        // Scan all the list of users, should be an object
                        let user: User = this._users.value.find(u=>u.id==acl.user_id)
                        user._roles.next(acl.role_ids.map(
                            role_id => this._roles.value.find(
                                (role: Role) => role.id == role_id
                            )
                        ))
                    }
                )
            }
            */
                this._users.value.forEach(
                    (user: User) => {
                        let acl: ACL = acls.find(a => a.user_id == user.id)
                        // Scan all the list of roles, should be an object (optimize)
                        if (acl && acl.role_ids) {
                            user._roles.next(acl.role_ids.map(
                                role_id => this._roles.value.find(
                                    (role: Role) => role.id == role_id
                                )
                            ))
                        }
                        else {
                            user._roles.next([])
                        }
                    }
                )
            }
        )
    }

    getUsers(): Observable<User[]> {
        console.warn('Migrate Graphql')
        return observableOf()
    //     return this.apollo.query({
    //         query: getUsersQuery,
    //     }).pipe(map(
    //         res =>
    //             res['data']['users'].map(
    //                 // Really create an object because User has roles in its constructor
    //                 (item: object) => new User(
    //                     item['id'],
    //                     item['name'],
    //                     item['email'],
    //                     item['active'],
    //                 )
    //             )
    //         )
    //     )
    }

    getRoles(): Observable<Role[]> {
        console.warn('Migrate Graphql')
        return observableOf()
        // return this.apollo.query({
        //     query: getRolesQuery,
        // }).pipe(map(
        //     res =>
        //         res['data']['roles'].map(
        //             (item: object) => item as Role
        //         )
        //     )
        // )
    }

    getACLs(): Observable<ACL[]> {
        console.warn('Migrate Graphql')
        return observableOf()
        // return this.apollo.query({
        //     query: getAclsQuery,
        // }).pipe(map(
        //     res =>
        //         res['data']['acls'].map(
        //             (item: object) => item as ACL
        //         )
        //     )
        // )
    }

    getAllAccessData(): Observable<void> {
        return forkJoin([
            this.getUsers(),
            this.getRoles(),
            this.getACLs(),
        ]).pipe(map(
            ([users, roles, acls]) => {
                this._users.next(users)
                this._roles.next(roles)
                this._acls.next(acls)

            }
        ))
    }

    getUserRoles(user: User): Role[] {
        let roles = this._acls.value.find(
            userRole => userRole.user_id === user.id
        )
        if (!roles) {
            return []
        }
        return roles.role_ids.map(
            role_id => this._roles.value.find(
                role => role.id == role_id
            )
        )
    }

    addUserRole(user: User, role: Role) {
        console.warn('Migrate Graphql')
        return observableOf()
        // return this.apollo.mutate({
        //     mutation: addUserRoleMutation,
        //     variables: {
        //         user_id: user.id,
        //         role_id: role.id
        //     }
        // }).pipe(map(
        //     res => {
        //         this.getACLs().subscribe(
        //             res => this._acls.next(res)
        //         )
        //         return res['data']['addUserRole']
        //     }
        // ))
    }

    deleteUserRole(user: User, role: Role) {
        console.warn('Migrate Graphql')
        return observableOf()
        // return this.apollo.mutate({
        //     mutation: deleteUserRoleMutation,
        //     variables: {
        //         user_id: user.id,
        //         role_id: role.id
        //     }
        // }).pipe(map(
        //     res => {
        //         this.getACLs().subscribe(
        //             res => this._acls.next(res)
        //         )
        //         return res['data']['deleteUserRole']
        //     }
        // ))
    }

    deleteUser(user: User) {
        console.warn('Migrate Graphql')
        return observableOf()
        // return this.apollo.mutate({
        //     mutation: deleteUserMutation,
        //     variables: {
        //         id: user.id,
        //     }
        // }).pipe(map(
        //     res => res['data']['deleteUser']
        // ))
    }

    deleteRole(role: Role) {
        console.warn('Migrate Graphql')
        return observableOf()
        // return this.apollo.mutate({
        //     mutation: deleteRoleMutation,
        //     variables: {
        //         id: role.id,
        //     }
        // }).pipe(map(
        //     res => res['data']['deleteRole']
        // ))
    }

    saveRole(role: Role) {
        console.warn('Migrate Graphql')
        return observableOf()
        // return this.apollo.mutate({
        //     mutation: saveRoleMutation,
        //     variables: {
        //         id: role.id,
        //         name: role.name,
        //         description: role.description
        //     }
        // }).pipe(map(
        //     res => res['data']['saveRole']
        // ))
    }

    saveUser(user: User) {
        console.warn('Migrate Graphql')
        return observableOf()
        // return this.apollo.mutate({
        //     mutation: saveUserMutation,
        //     variables: {
        //         id: user.id,
        //         name: user.name,
        //         email: user.email,
        //         active: user.active,
        //         password: user.password,
        //     }
        // }).pipe(map(
        //     res => res['data']['saveUser']
        // ))
    }

    openUserDialog(user?: User) {
        const dialogRef = this.dialog.open(GisafAdminAccessUserDialogComponent, {
            width: '75%',
            data: {
                'user': user
            }
        })
        dialogRef.afterClosed().subscribe(
            (user: User) => user &&
                this.saveUser(
                    user
                ).subscribe({
                    next: (name: string) => {
                        if (user) {
                            // Update
                            //let row = this.dataSource.data.findIndex(c=>c['name'] == name)
                            //this.dataSource.data[row] = user
                        }
                        else {
                            // New
                            this.saveUser(user)
                        }
                        this.init()
                    },
                    error: err => {
                        // Popup the dialog again, the error is catched by Apollo
                        this.openUserDialog(user)
                    }
                })
        )
    }

    openRoleDialog(role?: Role) {
        const dialogRef = this.dialog.open(GisafAdminAccessRoleDialogComponent, {
            width: '75%',
            data: {
                'role': role
            }
        })
        dialogRef.afterClosed().subscribe(
            (role: Role) => role &&
                this.saveRole(
                    role
                ).subscribe({
                    next: (name: string) => {
                        if (role) {
                            // Update
                            //let row = this.dataSource.data.findIndex(c=>c['name'] == name)
                            //this.dataSource.data[row] = role
                        }
                        else {
                            // New
                            this.saveRole(role)
                        }
                        this.init()
                    },
                    error: err => {
                        // Popup the dialog again, the error is catched by Apollo
                        this.openRoleDialog(role)
                    }
                })
        )
    }

}