-module(ts_user).
-export([create_table/1, new/1, new/2, update/1, update/2, lookup/1, list/2,
         check_credentials/2]).

-include("ts_db_records.hrl").
-include_lib("stdlib/include/qlc.hrl").

create_table(TableOpts) ->
    mnesia:create_table(ts_user,
        TableOpts ++ [{attributes, record_info(fields, ts_user)},
                      {type, ordered_set}]).

% expects the password in clear
new(UR = #ts_user{}) -> 
    {atomic, Result} = mnesia:transaction(fun() -> do_new(UR) end),
    {ok, Result}.

new(UR = #ts_user{}, ExtData) ->
    {atomic, Result} = mnesia:transaction(fun() ->
        NewUser = do_new(UR),
        ts_common:do_set_ext_data(UR, ExtData),
        NewUser end),
    {ok, Result}.

do_new(UR = #ts_user{}) -> 
    case mnesia:read(ts_user, UR#ts_user.username) of
        [ExistingRecord] -> {error, {record_exists, ExistingRecord}};
        [] ->
            NewRec = hash_input_record(UR),
            mnesia:write(NewRec),
            NewRec
    end.

update(UR = #ts_user{}) ->
    {atomic, Result} = mnesia:transaction(fun() -> do_update(UR) end),
    {ok, Result}.

update(UR = #ts_user{}, ExtData) ->
    {atomic, Result} = mnesia:transaction(fun() ->
        UpdatedUser = do_update(UR),
        ts_common:do_set_ext_data(UR, ExtData),
        UpdatedUser end),
    {ok, Result}.

do_update(UR = #ts_user{}) ->
    case mnesia:read(ts_user, UR#ts_user.username) of
        [] -> no_record;
        [Record] ->
            UpdatedRecord = ts_common:update_record(Record, UR),
            HashedRecord = case UR#ts_user.pwd of
                undefined -> UpdatedRecord;
                _Password -> hash_input_record(UpdatedRecord)
            end,
            mnesia:write(HashedRecord),
            HashedRecord
    end.

lookup(Username) ->
    case mnesia:dirty_read(ts_user, Username) of
        [] -> no_record;
        [User] -> User
    end.

list(Start, Length) -> ts_common:list(ts_user, Start, Length).

hash_input_record(User=#ts_user{}) ->
    % create a new User record
    Salt = generate_salt(),
    HashedPwd = hash_pwd(User#ts_user.pwd, Salt),
    User#ts_user{pwd = HashedPwd, pwd_salt = Salt}.
    
generate_salt() -> crypto:rand_bytes(36).

hash_pwd(Password, Salt) -> crypto:sha(Password ++ Salt).

check_credentials(Username, Password) ->
    case lookup(Username) of
        no_record -> false;
        User ->
            HashedInput = hash_pwd(Password, User#ts_user.pwd_salt),
            HashedInput == User#ts_user.pwd
    end.
