/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React from 'react';
import ReactDOM from 'react-dom';
import { makeStyles } from '@material-ui/core';
import SchemaView from '../../../../static/js/SchemaView';
import BaseUISchema from '../../../../static/js/SchemaView/base_schema.ui';
import pgAdmin from 'sources/pgadmin';
import Theme from 'sources/Theme';
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';
import PropTypes from 'prop-types';
import getApiInstance from '../../../../static/js/api_instance';
import authConstant from 'pgadmin.browser.constants';
import current_user from 'pgadmin.user_management.current_user';
import { isEmptyString } from '../../../../static/js/validators';
import Notify from '../../../../static/js/helpers/Notifier';
import { showChangeOwnership } from '../../../../static/js/Dialogs/index';

class UserManagementCollection extends BaseUISchema {
  constructor(authSources, roleOptions) {
    super({
      id: undefined,
      username: undefined,
      email: undefined,
      active: true,
      role: '2',
      newPassword: undefined,
      confirmPassword: undefined,
      auth_source: authConstant['INTERNAL']
    });

    this.authOnlyInternal = (current_user['auth_sources'].length  == 1 &&
      current_user['auth_sources'].includes(authConstant['INTERNAL'])) ? true : false;
    this.authSources = authSources;
    this.roleOptions = roleOptions;
  }

  get idAttribute() {
    return 'id';
  }

  isUserNameEnabled(state) {
    if (this.authOnlyInternal || state.auth_source == authConstant['INTERNAL']) {
      return false;
    }
    return true;
  }

  isEditable(state) {
    return state.id != current_user['id'];
  }

  get baseFields() {
    let obj = this;
    return [
      {
        id: 'auth_source', label: gettext('Authentication source'), cell: 'select',
        options: obj.authSources, minWidth: 110, width: 110,
        controlProps: {
          allowClear: false,
          openOnEnter: false,
          first_empty: false,
        },
        visible: function() {
          if (obj.authOnlyInternal)
            return false;
          return true;
        },
        editable: function(state) {
          return (obj.isNew(state) && !obj.authOnlyInternal);
        }
      }, {
        id: 'username', label: gettext('Username'), cell: 'text',
        minWidth: 90, width: 90,
        deps: ['auth_source'],
        depChange: (state)=>{
          if (obj.isUserNameEnabled(state) && obj.isNew(state) && !isEmptyString(obj.username)) {
            return {username: undefined};
          }
        },
        editable: (state)=> {
          return obj.isUserNameEnabled(state);
        }
      }, {
        id: 'email', label: gettext('Email'), cell: 'text',
        minWidth: 90, width: 90, deps: ['id'],
        editable: (state)=> {
          if (obj.isNew(state))
            return true;

          if (obj.isEditable(state) && state.auth_source != authConstant['INTERNAL'])
            return true;

          return false;
        }
      }, {
        id: 'role', label: gettext('Role'), cell: 'select',
        options: obj.roleOptions, minWidth: 95, width: 95,
        controlProps: {
          allowClear: false,
          openOnEnter: false,
          first_empty: false,
        },
        editable: (state)=> {
          return obj.isEditable(state);
        }
      }, {
        id: 'active', label: gettext('Active'), cell: 'switch', width: 60, disableResizing: true,
        editable: (state)=> {
          return obj.isEditable(state);
        }
      }, {
        id: 'newPassword', label: gettext('New password'), cell: 'password',
        minWidth: 90, width: 90, deps: ['auth_source'],
        editable: (state)=> {
          return obj.isEditable(state) && state.auth_source == authConstant['INTERNAL'];
        }
      }, {
        id: 'confirmPassword', label: gettext('Confirm password'), cell: 'password',
        minWidth: 90, width: 90, deps: ['auth_source'],
        editable: (state)=> {
          return obj.isEditable(state) && state.auth_source == authConstant['INTERNAL'];
        }
      }, {
        id: 'locked', label: gettext('Locked'), cell: 'switch', width: 60, disableResizing: true,
        editable: (state)=> {
          return obj.isEditable(state);
        }
      }
    ];
  }

  validate(state, setError) {
    let msg = undefined;
    let obj = this;
    if (obj.isUserNameEnabled(state) && isEmptyString(state.username)) {
      msg = gettext('Username cannot be empty.');
      setError('username', msg);
      return true;
    } else {
      setError('username', null);
    }

    if (state.auth_source == authConstant['INTERNAL']) {
      let email_filter = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
      if (isEmptyString(state.email)) {
        msg = gettext('Email cannot be empty.');
        setError('email', msg);
        return true;
      } else if (!email_filter.test(state.email)) {
        msg = gettext('Invalid email address: %s.', state.email);
        setError('email', msg);
        return true;
      } else {
        setError('email', null);
      }
      // TODO: Check for duplicate email address errmsg = gettext('The email address %s already exists.'

      if (obj.isNew(state) && isEmptyString(state.newPassword)) {
        msg = gettext('Password cannot be empty for user %s.', state.email);
        setError('newPassword', msg);
        return true;
      } else if (state.newPassword?.length < 6) {
        msg = gettext('Password must be at least 6 characters for user %s.', state.email);
        setError('newPassword', msg);
        return true;
      } else {
        setError('newPassword', null);
      }

      if (obj.isNew(state) && isEmptyString(state.confirmPassword)) {
        msg = gettext('Confirm Password cannot be empty for user %s.', state.email);
        setError('confirmPassword', msg);
        return true;
      } else {
        setError('confirmPassword', null);
      }

      if (state.newPassword !== state.confirmPassword) {
        msg = gettext('Passwords do not match for user %s.', state.email);
        setError('confirmPassword', msg);
        return true;
      } else {
        setError('confirmPassword', null);
      }
    }

    return false;
  }
}

class UserManagementSchema extends BaseUISchema {
  constructor(authSources, roleOptions) {
    super();
    this.userManagementCollObj = new UserManagementCollection(authSources, roleOptions);
  }

  deleteUser(deleteRow) {
    Notify.confirm(
      gettext('Delete user?'),
      gettext('Are you sure you wish to delete this user?'),
      deleteRow,
      function() {
        return true;
      }
    );
  }

  get baseFields() {
    let obj = this;
    const api = getApiInstance();
    return [
      {
        id: 'userManagement', label: '', type: 'collection', schema: obj.userManagementCollObj,
        canAdd: true, canDelete: true, isFullTab: true, group: 'temp_user',
        canDeleteRow: (row)=>{
          if (row['id'] == current_user['id'])
            return false;
          return true;
        },
        onDelete: (row, deleteRow)=> {
          let deletedUser = {'id': row['id'], 'name': !isEmptyString(row['email']) ? row['email'] : row['username']};
          api.get(url_for('user_management.shared_servers', {'uid': row['id']}))
            .then((res)=>{
              if (res.data?.data?.shared_servers > 0) {
                api.get(url_for('user_management.admin_users', {'uid': row['id']}))
                  .then((result)=>{
                    showChangeOwnership(gettext('Change ownership'),
                      result?.data?.data?.result?.data,
                      res?.data?.data?.shared_servers,
                      deletedUser,
                      deleteRow
                    );
                  })
                  .catch((err)=>{
                    Notify.error(err);
                  });
              } else {
                obj.deleteUser(deleteRow);
              }
            })
            .catch((err)=>{
              Notify.error(err);
              obj.deleteUser(deleteRow);
            });
        },
        canSearch: true
      },
    ];
  }
}

const useStyles = makeStyles((theme)=>({
  root: {
    ...theme.mixins.tabPanel,
    padding: 0,
  },
}));

function UserManagementDialog({onClose}) {
  const classes = useStyles();
  const [authSources, setAuthSources] = React.useState([]);
  const [roles, setRoles] = React.useState([]);
  const api = getApiInstance();

  React.useEffect(async ()=>{
    try {
      api.get(url_for('user_management.auth_sources'))
        .then(res=>{
          setAuthSources(res.data);
        })
        .catch((err)=>{
          Notify.error(err);
        });

      api.get(url_for('user_management.roles'))
        .then(res=>{
          setRoles(res.data);
        })
        .catch((err)=>{
          Notify.error(err);
        });
    } catch (error) {
      Notify.error(error);
    }
  }, []);

  const onSaveClick = (_isNew, changeData)=>{
    return new Promise((resolve, reject)=>{
      try {
        api.post(url_for('user_management.save'), changeData['userManagement'])
          .then(()=>{
            Notify.success('Users Saved Successfully');
          })
          .catch((err)=>{
            Notify.error(err);
          });
        resolve();
        onClose();
      } catch (error) {
        reject(error);
      }
    });
  };

  const authSourcesOptions = authSources.map((m)=>({
    label: m.label,
    value: m.value,
  }));

  if(authSourcesOptions.length <= 0) {
    return <></>;
  }

  const roleOptions = roles.map((m)=>({
    label: m.name,
    value: m.id,
  }));

  if(roleOptions.length <= 0) {
    return <></>;
  }

  const onDialogHelp = () => {
    window.open(url_for('help.static', { 'filename': 'user_management.html' }), 'pgadmin_help');
  };

  return <SchemaView
    formType={'dialog'}
    getInitData={()=>{ return new Promise((resolve, reject)=>{
      api.get(url_for('user_management.users'))
        .then((res)=>{
          resolve({userManagement:res.data});
        })
        .catch((err)=>{
          reject(err);
        });
    }); }}
    schema={new UserManagementSchema(authSourcesOptions, roleOptions)}
    viewHelperProps={{
      mode: 'edit',
    }}
    onSave={onSaveClick}
    onClose={onClose}
    onHelp={onDialogHelp}
    hasSQL={false}
    disableSqlHelp={true}
    isTabView={false}
    formClassName={classes.root}
  />;
}

UserManagementDialog.propTypes = {
  onClose: PropTypes.func
};

export default function showUserManagement() {
  pgAdmin.Browser.Node.registerUtilityPanel();
  let panel = pgAdmin.Browser.Node.addUtilityPanel(980, pgAdmin.Browser.stdH.md),
    j = panel.$container.find('.obj_properties').first();
  panel.title(gettext('User Management'));

  const onClose = ()=> {
    ReactDOM.unmountComponentAtNode(j[0]);
    panel.close();
  };

  ReactDOM.render(
    <Theme>
      <UserManagementDialog
        onClose={onClose}
      />
    </Theme>, j[0]);
}