-module(handler_event_by_id). -include("records.hrl"). -export([init/2]). init(Req, Opts) -> handle(Req, Opts). handle(Req, _Opts) -> case cowboy_req:method(Req) of <<"GET">> -> get_event(Req); <<"PUT">> -> update_event(Req); <<"DELETE">> -> delete_event(Req); _ -> send_error(Req, 405, <<"Method not allowed">>) end. %% GET /v1/events/:id - получение события get_event(Req) -> case handler_auth:authenticate(Req) of {ok, UserId, Req1} -> EventId = cowboy_req:binding(id, Req1), case logic_event:get_event(UserId, EventId) of {ok, Event} -> Response = event_to_json(Event), send_json(Req1, 200, Response); {error, access_denied} -> send_error(Req1, 403, <<"Access denied">>); {error, not_found} -> send_error(Req1, 404, <<"Event not found">>); {error, _} -> send_error(Req1, 500, <<"Internal server error">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. %% PUT /v1/events/:id - обновление события update_event(Req) -> case handler_auth:authenticate(Req) of {ok, UserId, Req1} -> EventId = cowboy_req:binding(id, Req1), {ok, Body, Req2} = cowboy_req:read_body(Req1), try jsx:decode(Body, [return_maps]) of UpdatesMap when is_map(UpdatesMap) -> Updates = maps:to_list(UpdatesMap), UpdatesWithTypes = convert_fields(Updates), case logic_event:update_event(UserId, EventId, UpdatesWithTypes) of {ok, Event} -> Response = event_to_json(Event), send_json(Req2, 200, Response); {error, access_denied} -> send_error(Req2, 403, <<"Access denied">>); {error, not_found} -> send_error(Req2, 404, <<"Event not found">>); {error, event_in_past} -> send_error(Req2, 400, <<"Event cannot be in the past">>); {error, _} -> send_error(Req2, 500, <<"Internal server error">>) end; _ -> send_error(Req2, 400, <<"Invalid JSON">>) catch _:_ -> send_error(Req2, 400, <<"Invalid JSON format">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. %% DELETE /v1/events/:id - удаление события delete_event(Req) -> case handler_auth:authenticate(Req) of {ok, UserId, Req1} -> EventId = cowboy_req:binding(id, Req1), case logic_event:delete_event(UserId, EventId) of {ok, _} -> send_json(Req1, 200, #{status => <<"deleted">>}); {error, access_denied} -> send_error(Req1, 403, <<"Access denied">>); {error, not_found} -> send_error(Req1, 404, <<"Event not found">>); {error, _} -> send_error(Req1, 500, <<"Internal server error">>) end; {error, Code, Message, Req1} -> send_error(Req1, Code, Message) end. %% Вспомогательные функции convert_fields(Updates) -> lists:map(fun ({start_time, Value}) when is_binary(Value) -> case parse_datetime(Value) of {ok, DateTime} -> {start_time, DateTime}; _ -> {start_time, Value} end; ({location, Value}) when is_map(Value) -> case Value of #{<<"lat">> := Lat, <<"lon">> := Lon} -> Address = maps:get(<<"address">>, Value, <<"">>), {location, #location{address = Address, lat = Lat, lon = Lon}}; _ -> {location, undefined} end; ({tags, Value}) when is_list(Value) -> {tags, Value}; (Other) -> Other end, Updates). parse_datetime(Str) -> try [DateStr, TimeStr] = string:split(Str, "T"), TimeStrNoZ = string:trim(TimeStr, trailing, "Z"), [YearStr, MonthStr, DayStr] = string:split(DateStr, "-", all), [HourStr, MinuteStr, SecondStr] = string:split(TimeStrNoZ, ":", all), Year = binary_to_integer(YearStr), Month = binary_to_integer(MonthStr), Day = binary_to_integer(DayStr), Hour = binary_to_integer(HourStr), Minute = binary_to_integer(MinuteStr), Second = binary_to_integer(SecondStr), {ok, {{Year, Month, Day}, {Hour, Minute, Second}}} catch _:_ -> {error, invalid_format} end. event_to_json(Event) -> LocationJson = case Event#event.location of undefined -> null; #location{address = Addr, lat = Lat, lon = Lon} -> #{address => Addr, lat => Lat, lon => Lon} end, RecurrenceJson = case Event#event.recurrence_rule of undefined -> null; Rule -> Decoded = jsx:decode(Rule, [return_maps]), case Decoded of Map when is_map(Map) -> Map; {ok, Map} -> Map end end, #{ id => Event#event.id, calendar_id => Event#event.calendar_id, title => Event#event.title, description => Event#event.description, event_type => Event#event.event_type, start_time => datetime_to_iso8601(Event#event.start_time), duration => Event#event.duration, recurrence => RecurrenceJson, master_id => Event#event.master_id, is_instance => Event#event.is_instance, specialist_id => Event#event.specialist_id, location => LocationJson, tags => Event#event.tags, capacity => Event#event.capacity, online_link => Event#event.online_link, status => Event#event.status, rating_avg => Event#event.rating_avg, rating_count => Event#event.rating_count, created_at => datetime_to_iso8601(Event#event.created_at), updated_at => datetime_to_iso8601(Event#event.updated_at) }. datetime_to_iso8601({{Year, Month, Day}, {Hour, Minute, Second}}) -> iolist_to_binary(io_lib:format("~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0BZ", [Year, Month, Day, Hour, Minute, Second])). send_json(Req, Status, Data) -> Body = jsx:encode(Data), cowboy_req:reply(Status, #{<<"content-type">> => <<"application/json">>}, Body, Req), {ok, Body, []}. send_error(Req, Status, Message) -> Body = jsx:encode(#{error => Message}), cowboy_req:reply(Status, #{<<"content-type">> => <<"application/json">>}, Body, Req), {ok, Body, []}.