-module(ip_fetcher_app).
-behaviour(application).

-export([start/2, stop/1]).

start(_StartType, _StartArgs) ->
    ip_fetcher_sup:start_link().

stop(_State) ->
    ok.

%% ===================================================================
%% Supervisor
%% ===================================================================

-module(ip_fetcher_sup).
-behaviour(supervisor).

-export([start_link/0, init/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    SupFlags = #{
        strategy  => one_for_one,
        intensity => 3,        % max restarts
        period    => 5         % seconds
    },

    ChildSpec = #{
        id       => ip_fetcher,
        start    => {ip_fetcher_worker, start_link, []},
        restart  => transient, % only restart on abnormal exit
        shutdown => 5000,      % 5 seconds graceful shutdown
        type     => worker,
        modules  => [ip_fetcher_worker]
    },

    {ok, {SupFlags, [ChildSpec]}}.

%% ===================================================================
%% Worker
%% ===================================================================

-module(ip_fetcher_worker).
-behaviour(gen_server).

-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
    % Start the work immediately
    self() ! fetch_ip,
    {ok, #{}}.

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(fetch_ip, State) ->
    case do_fetch_ip() of
        {ok, Ip} ->
            io:format("~s~n", [Ip]),
            % Normal exit — transient worker will NOT be restarted
            {stop, normal, State};
        {error, Reason} ->
            io:format("Failed to connect – will be restarted by supervisor: ~p~n", [Reason]),
            % Abnormal exit — supervisor WILL restart us (up to 3 times in 5s)
            {stop, {connect_failed, Reason}, State}
    end;

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% Internal function to fetch IP
do_fetch_ip() ->
    Host = "icanhazip.com",
    Port = 80,
    Timeout = 10000,

    case gen_tcp:connect(Host, Port, [binary, {active, false}], Timeout) of
        {ok, Socket} ->
            Request = "GET / HTTP/1.0\r\nHost: icanhazip.com\r\nConnection: close\r\n\r\n",
            ok = gen_tcp:send(Socket, Request),

            case recv_all(Socket, <<>>) of
                {ok, Data} ->
                    % Extract just the IP (last line)
                    Lines = binary:split(Data, <<"\n">>, [global, trim]),
                    Ip = lists:last(Lines),
                    gen_tcp:close(Socket),
                    {ok, binary_to_list(Ip)};
                Error ->
                    gen_tcp:close(Socket),
                    Error
            end;
        Error ->
            Error
    end.

recv_all(Socket, Acc) ->
    case gen_tcp:recv(Socket, 0, 10000) of
        {ok, Data} ->
            recv_all(Socket, <<Acc/binary, Data/binary>>);
        {error, closed} ->
            {ok, Acc};
        {error, _} = Err ->
            Err
    end.