using System;
using System.Data;
using System.Data.SqlClient;
using System.Web.Services;
using edu.jhu.Cas.Util;
using edu.jhu.Cas.Users;

namespace edu.jhu.Cas.Services {
    [WebService(Namespace = "http://Services.Cas.jhu.edu", Name = "Users", Description = "Provides user management functionality.")]
    public class Users : System.Web.Services.WebService {
        CasUsers cu = new CasUsers();

        const string WSID_S = "@WSID";
        const string PWEN_P = "@PWEN";
        const string authSql = "select count(*) from users where webservicesid = " + WSID_S + " and pwencrypted = " + PWEN_P;
        static SqlCommand authCom;
        /*
         * throws an exception if the wsid/pw combination is invalid.
         * Otherwise does nothing
         */
        public static void Authenticate(long wsid, string password) {
            if (password == null) throw new Exception("Password cannot be null");
            if (authCom == null || authCom.Connection == null || authCom.Connection.State != ConnectionState.Open)
                initAuthCom();
            try {
                doAuth(wsid, password);
            } catch (SqlException) {
                //static prepared sqlcommand may have rotted
                //reint and try once more
                initAuthCom();
                doAuth(wsid, password);
            }
        }
        static void doAuth(long wsid, string password) {
            authCom.Parameters[WSID_S].Value = wsid;
            authCom.Parameters[PWEN_P].Value = Encryptor.MD52Base64Encrypt(password);
            object o = authCom.ExecuteScalar();
            if (o == null || (((int)o) != 1))
                throw new CasJobsException("Authentication failed.");
        }
        static void initAuthCom() {
            authCom = new SqlCommand(authSql);
            authCom.Parameters.Add(WSID_S, SqlDbType.BigInt);
            authCom.Parameters.Add(PWEN_P, SqlDbType.VarChar, 50);
            authCom.Connection = new SqlConnection(SqlFactory.JOBS_STRING);
            authCom.Connection.Open();
            authCom.Prepare();
        }
        [WebMethod(Description = @"
Create a new user account.  Returns WebServices of account created. Requires admin priv.<br/>
<br/>
<b>Parameters:</b><br/>
long admin_wsid : WebServicesID of account that will create the new account.  This must be an admin account.<br/>
string admin_pw : Password of admin account<br/>
string userid : Desired UserID of new user.  Must be unique.<br/>
string name : Name of user<br/>
string email : Email of user<br/>
string password : Password of user.<br/>
string privileges : Privileges of user.  Defaults to null.<br/>
int emaillevel : Describes when to send automatic email to this user.  Valid values are below<br/>
&nbsp;0 = do not send automated mail to this user<br/>
&nbsp;1 = send automated mail when non-quick jobs fail<br/>
&nbsp;2 = send automated mail when non-quick jobs complete successfully<br/>
&nbsp;3 = send automated mail in either of the above two cases<br/>
short gvisible : Determines whether a user is visible for group membership. 1 for yes, 0 for no.<br/>
<br/>
<b>Output:</b><br/>
long : A unique identifier of the newly created account, also referred to as WebServicesID, or just wsid.<br/>
")]
        public long CreateAccount(long admin_wsid, string admin_pw, string userid, string name, string email,
            string password, string privileges, int emaillevel, short gvisible) {
            Authenticate(admin_wsid, admin_pw);
            return cu.CreateAccount(admin_wsid, userid, name, email, password, privileges, emaillevel, gvisible);
        }
        [WebMethod(Description = @"
Fully removes an account from the system.  Requires admin priv.  This method has no output when succesful.<br/>
<br/>
<b>Parameters:</b><br/>
long admin_wsid : WebServicesID of admin user.<br/>
string admin_pw : Password of admin account<br/>
long deleteme : WebServicesID of account to delete<br/>
<br/>
<b>Output:</b><br/>
Void
")]
        public void DeleteAccount(long admin_wsid, string admin_pw, long deleteme) {
            Authenticate(admin_wsid, admin_pw);
            cu.DeleteAccount(admin_wsid, deleteme);
        }
        [WebMethod(Description = @"
Update account described by 'wsid' with information described in 'user' param.<br/>  
The WebServicesID in 'user' determines which user's account is updated and must match the parameter 'wsid'; users may only update their own account.<br/>
Fields left null in the User object will also be updated as null in the db.<br/>
The 'Privileges' and 'Password' fields are exceptions to this rule; null values in these fields will not override existing values.<br/>
<br/>
<b>Parameters:</b><br/>
long wsid : WebServicesID of account to update.<br/>
string pw : Password of that account<br/>
CJUser user : Object containing user parameters to update.  See GetUsers() for CJUser field documentation.<br/>
<br/>
<b>Output:</b><br/>
bool : true if successful, false otherwise
")]
        public bool UpdateAccount(long wsid, string pw, CJUser user) {
            Authenticate(wsid, pw);
            User u = new User();
            u.Email = user.Email;
            u.EmailLevel = user.EmailLevel;
            u.GVisible = user.GVisible;
            u.Name = user.Name;
            u.Privileges = user.Privileges;
            u.PWEncrypted = user.Password;
            u.UserID = user.UserID;
            u.WebServicesID = user.WebServicesID;
            return cu.UpdateAccount(wsid, u);
        }
        [WebMethod(Description = @"
Returns an array of CJUser objects representing all users that match the criteria in conditions.<br/>
Conditions is a formatted string of the same format of that used in Jobs.FindJobs() (in the jobs.asmx web service).<br/>
Please see Jobs.FindJobs() for further documentation on the conditions string.<br/>
The wsid of the user calling this method must have the admin priv.<br/>
<br/>
The following are valid conditions keys:<br/>
string userid : unique string identifier of user<br/>
string name : user's full name<br/>
string email : user's email<br/>
string privileges : user's privileges<br/>
short gvisible : Determines whether a user is visible for group membership. 1 for yes, 0 for no.<br/>
long wsid : unique numeric identifier of user<br/>
int emaillevel : Describes when to send automatic email to this user.  See CreateAccount for appropriate values<br/>
DateTime timecreated: account creation date of user<br/>
<br/>
<b>Parameters:</b><br/>
long admin_wsid : WebServicesID of admin account.<br/>
string admin_pw : Password of that account<br/>
string conditions : A formatted string describing the conditions for the search.  See above for details on this.<br/>
<br/>
<b>Output:</b><br/>
CJUser[] : Contains objects matching conditions<br/>
<br/>
A CJUser object contains:<br/>
string userid : unique string identifier of user<br/>
string name : user's full name<br/>
string email : user's email<br/>
string privileges : user's privileges<br/>
bool gvisible : Determines whether a user is visible for group membership. <br/>
long webservicesid : unique numeric identifier of user<br/>
int emaillevel : Describes when to send automatic email to this user.  See CreateAccount for appropriate values<br/>
DateTime timecreated: account creation date of user<br/>
string password : This is always returned as null.  Field exists so it may be updated from UpdateAccount()<br/>
            ")]
        public CJUser[] GetUsers(long admin_wsid, string admin_pw, string conditions) {
            Authenticate(admin_wsid, admin_pw);
            cu.HasPrivilege(admin_wsid,PRIVS.ADMIN);
            string qry = @"
select [userid],[name],[email],[privileges],[gvisible],[webservicesid],[emaillevel],[timecreated]
from users";
            string consql = ConditionFactory.UserConditions2SQL(conditions);
            if (consql != "")
                qry = qry + " WHERE " + consql;
            return DataConverter.dataSet2CJUser(SqlFactory.select(qry));
        }
    }
}