%%%-------------------------------------------------------------------
%%% @author aresei
%%% @copyright (C) 2023, <COMPANY>
%%% @doc
%%% @end
%%%-------------------------------------------------------------------
-module(iot_host_sup).
-include("iot.hrl").

-behaviour(supervisor).

-export([start_link/0, init/1, delete_host/1, ensured_host_started/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    Specs = lists:map(fun child_spec/1, iot_api_client:get_all_hosts()),

    {ok, {#{strategy => one_for_one, intensity => 1000, period => 3600}, Specs}}.

-spec ensured_host_started(UUID :: binary()) -> {ok, Pid :: pid()} | {error, Reason :: any()}.
ensured_host_started(UUID) when is_binary(UUID) ->
    case iot_host:get_pid(UUID) of
        undefined ->
            %% 尝试删除下host对应的信息
            case delete_host(UUID) of
                ok ->
                    case supervisor:start_child(?MODULE, child_spec(UUID)) of
                        {ok, Pid} when is_pid(Pid) ->
                            {ok, Pid};
                        {error, {'already_started', Pid}} when is_pid(Pid) ->
                            {ok, Pid};
                        {error, Error} ->
                            {error, Error}
                    end;
                {error, Reason} ->
                    {error, Reason}
            end;
        Pid when is_pid(Pid) ->
            {ok, Pid}
    end.

delete_host(UUID) when is_binary(UUID) ->
    Id = iot_host:get_name(UUID),
    case supervisor:terminate_child(?MODULE, Id) of
        ok ->
            delete_host_child(Id, UUID);
        {error, not_found} ->
            delete_host_child(Id, UUID);
        {error, Reason} ->
            logger:warning("[iot_host_sup] terminate host: ~p failed: ~p", [UUID, Reason]),
            {error, Reason}
    end.

delete_host_child(Id, UUID) ->
    case supervisor:delete_child(?MODULE, Id) of
        ok ->
            ok;
        {error, not_found} ->
            ok;
        {error, running} ->
            %% ensure killed then delete again
            iot_host:kill(UUID),
            case supervisor:delete_child(?MODULE, Id) of
                ok ->
                    ok;
                {error, not_found} ->
                    ok;
                {error, Reason} ->
                    {error, Reason}
            end;
        {error, Reason} ->
            {error, Reason}
    end.

-spec child_spec(UUID :: binary()) -> map().
child_spec(UUID) when is_binary(UUID) ->
    Id = iot_host:get_name(UUID),
    #{id => Id,
        start => {iot_host, start_link, [Id, UUID]},
        restart => permanent,
        shutdown => 2000,
        type => worker,
        modules => ['iot_host']}.
