-module(js_session).
-author('Maxim Sokhatsky').
-include_lib("n2o/include/wf.hrl").
-include_lib("kvs/include/user.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-export(?SESSION_API).
-compile(export_all).
-record(state, {unique, node}).

init(State,Ctx) -> {ok,State,Ctx}.
finish(State,Ctx) -> {ok,State,Ctx}.

ensure_sid(State, Ctx) -> 
    SessionUser = wf:cookie_req(<<"n2o-name">>,?REQ),
    SessionId   = wf:cookie_req(<<"n2o-sid">>, ?REQ),
    wf:info(?MODULE,"Session Init n2o-sid: ~p",[SessionId]),
    {{D1,D2,D3},{T1,T2,T3}} = calendar:now_to_datetime(now()),
    Till = {{D1,D2,D3+1},{T1,T2,T3}},
    TTL = 24 * 60 * 60, % 1 day TTL
    SessionCookie = case lookup_ets({SessionId,<<"auth">>}) of 
        undefined ->
            CookieValue = case kvs:get(user,SessionUser) of
                {ok,User} ->
                    SS = lists:keyfind(n2o,1,User#user.tokens),
                    case SS of
                        {n2o,SessionId} -> SessionId;
                        _ -> new_cookie_value() end;
                _ -> new_cookie_value() end,
            Cookie = {{CookieValue,<<"auth">>},<<"/">>,now(),{TTL,Till},new},
            ets:insert(cookies,Cookie),
            wf:info(?MODULE,"Cookie New: ~p",[Cookie]), 
            Cookie;
        {{Session,Key},Path,Issued,{_TTL,_Till},Status} ->
            case expired(Issued,{_TTL,_Till}) of
                false ->
                    Cookie = {{Session,Key},Path,Issued,{_TTL,_Till},Status},
                    wf:info(?MODULE,"Cookie Same: ~p",[Cookie]),
                    Cookie;
                true -> Cookie = {{new_cookie_value(),<<"auth">>},<<"/">>,now(),{TTL,Till},new},
                    ets:insert(cookies,Cookie), 
                    wf:info(?MODULE,"Cookie Expired: ~p",[Cookie]),
                    Cookie end;
        _ -> error_logger:info_msg("Cookie Error"), skip
    end,
    {{ID,_},_,_,_,_} = SessionCookie,
    put(session_id,ID),
    wf:info(?MODULE,"State: ~p",[SessionCookie]),
    {ok, State, Ctx#context{session=SessionCookie}}.

expired(_Issued,{_TTL,Till}) -> false. % Till < calendar:now_to_datetime(now()).

lookup_ets(Key) ->
    Res = ets:lookup(cookies,Key),
    wf:info(?MODULE,"Lookup ETS: ~p",[{Res,Key}]),
    case Res of 
         [] -> undefined;
         [Value] -> Value;
         Values -> Values end.

clear() -> clear(session_id()).
clear(Session) ->
    [ ets:delete(cookies,X) || X <- ets:select(cookies,
        ets:fun2ms(fun(A) when (element(1,element(1,A)) == Session) -> element(1,A) end)) ].

cookie_expire(SecondsToLive) -> 
  Seconds = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
  DateTime = calendar:gregorian_seconds_to_datetime(Seconds + SecondsToLive),
  httpd_util:rfc1123_date(DateTime).

ttl() -> 60*60*24*30.

session_id() -> get(session_id).
new_cookie_value() ->
    SessionKey = base64:encode(erlang:md5(term_to_binary({now(), make_ref()}))),
    wf:wire(wf:f("document.cookie='~s=~s; path=/; expires=~s';",
                [wf:to_list(session_cookie_name()),
                 wf:to_list(SessionKey),
                 cookie_expire(ttl())])),
    SessionKey.

new_cookie_value(SessionKey) ->
    wf:info(?MODULE,wf:f("document.cookie='~s=~s; path=/; expires=~s';",
                [wf:to_list(session_cookie_name()),
                 wf:to_list(SessionKey),
                 cookie_expire(ttl())]),[]),
    SessionKey.

new_state() -> #state{unique=new_cookie_value()}.
session_cookie_name() -> <<"n2o-sid">>.
set_value(Key, Value) -> ets:insert(cookies,{{session_id(),Key},Value}), Value.
get_value(Key, DefaultValue) -> 
    Res = case lookup_ets({session_id(),Key}) of
               undefined -> DefaultValue;
               {_,Value} -> Value end,
    wf:info(?MODULE,"Session Lookup Key ~p Value ~p",[Key,Res]),
    Res.