{-
   Copyright 2020 Eike K. & Contributors

   SPDX-License-Identifier: AGPL-3.0-or-later
-}


module Comp.NotificationChannelManage exposing
    ( Model
    , Msg
    , init
    , update
    , view
    )

import Api
import Api.Model.BasicResult exposing (BasicResult)
import Comp.Basic as B
import Comp.ChannelForm
import Comp.ChannelMenu
import Comp.MenuBar as MB
import Comp.NotificationChannelTable
import Data.ChannelType exposing (ChannelType)
import Data.Flags exposing (Flags)
import Data.NotificationChannel exposing (NotificationChannel)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Http
import Messages.Comp.NotificationChannelManage exposing (Texts)
import Styles as S


type alias Model =
    { listModel : Comp.NotificationChannelTable.Model
    , detailModel : Maybe Comp.ChannelForm.Model
    , items : List NotificationChannel
    , deleteConfirm : DeleteConfirm
    , loading : Bool
    , formState : FormState
    , newChannelMenuOpen : Bool
    , jsonFilterError : Maybe String
    }


type DeleteConfirm
    = DeleteConfirmOff
    | DeleteConfirmOn


type SubmitType
    = SubmitDelete
    | SubmitUpdate
    | SubmitCreate


type FormState
    = FormStateInitial
    | FormErrorHttp Http.Error
    | FormSubmitSuccessful SubmitType
    | FormErrorSubmit String
    | FormErrorInvalid


type Msg
    = TableMsg Comp.NotificationChannelTable.Msg
    | DetailMsg Comp.ChannelForm.Msg
    | GetDataResp (Result Http.Error (List NotificationChannel))
    | ToggleNewChannelMenu
    | SubmitResp SubmitType (Result Http.Error BasicResult)
    | NewChannelInit ChannelType
    | BackToTable
    | Submit
    | RequestDelete
    | CancelDelete
    | DeleteChannelNow String


initModel : Model
initModel =
    { listModel = Comp.NotificationChannelTable.init
    , detailModel = Nothing
    , items = []
    , loading = False
    , formState = FormStateInitial
    , newChannelMenuOpen = False
    , deleteConfirm = DeleteConfirmOff
    , jsonFilterError = Nothing
    }


initCmd : Flags -> Cmd Msg
initCmd flags =
    Api.getChannels flags GetDataResp


init : Flags -> ( Model, Cmd Msg )
init flags =
    ( initModel, initCmd flags )



--- Update


update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
    case msg of
        GetDataResp (Ok res) ->
            ( { model
                | items = res
                , formState = FormStateInitial
              }
            , Cmd.none
            )

        GetDataResp (Err err) ->
            ( { model | formState = FormErrorHttp err }
            , Cmd.none
            )

        TableMsg lm ->
            let
                ( mm, action ) =
                    Comp.NotificationChannelTable.update flags lm model.listModel

                ( detail, cmd ) =
                    case action of
                        Comp.NotificationChannelTable.NoAction ->
                            ( Nothing, Cmd.none )

                        Comp.NotificationChannelTable.EditAction channel ->
                            let
                                ( dm, dc ) =
                                    Comp.ChannelForm.initWith flags channel
                            in
                            ( Just dm, Cmd.map DetailMsg dc )
            in
            ( { model
                | listModel = mm
                , detailModel = detail
              }
            , cmd
            )

        DetailMsg lm ->
            case model.detailModel of
                Just dm ->
                    let
                        ( mm, mc ) =
                            Comp.ChannelForm.update flags lm dm
                    in
                    ( { model | detailModel = Just mm }
                    , Cmd.map DetailMsg mc
                    )

                Nothing ->
                    ( model, Cmd.none )

        ToggleNewChannelMenu ->
            ( { model | newChannelMenuOpen = not model.newChannelMenuOpen }, Cmd.none )

        SubmitResp submitType (Ok res) ->
            ( { model
                | formState =
                    if res.success then
                        FormSubmitSuccessful submitType

                    else
                        FormErrorSubmit res.message
                , detailModel =
                    if submitType == SubmitDelete then
                        Nothing

                    else
                        model.detailModel
                , loading = False
              }
            , if submitType == SubmitDelete then
                initCmd flags

              else
                Cmd.none
            )

        SubmitResp _ (Err err) ->
            ( { model | formState = FormErrorHttp err, loading = False }
            , Cmd.none
            )

        NewChannelInit ct ->
            let
                ( mm, mc ) =
                    Comp.ChannelForm.init flags ct
            in
            ( { model | detailModel = Just mm, newChannelMenuOpen = False }, Cmd.map DetailMsg mc )

        BackToTable ->
            ( { model | detailModel = Nothing }, initCmd flags )

        Submit ->
            case model.detailModel of
                Just dm ->
                    case Comp.ChannelForm.getChannel dm of
                        Just data ->
                            postChannel flags data model

                        Nothing ->
                            ( { model | formState = FormErrorInvalid }, Cmd.none )

                Nothing ->
                    ( model, Cmd.none )

        RequestDelete ->
            ( { model | deleteConfirm = DeleteConfirmOn }, Cmd.none )

        CancelDelete ->
            ( { model | deleteConfirm = DeleteConfirmOff }, Cmd.none )

        DeleteChannelNow id ->
            ( { model | deleteConfirm = DeleteConfirmOff, loading = True }
            , Api.deleteChannel flags id (SubmitResp SubmitDelete)
            )


postChannel : Flags -> NotificationChannel -> Model -> ( Model, Cmd Msg )
postChannel flags channel model =
    if (Data.NotificationChannel.getRef channel |> .id) == "" then
        ( { model | loading = True }, Api.createChannel flags channel (SubmitResp SubmitCreate) )

    else
        ( { model | loading = True }, Api.updateChannel flags channel (SubmitResp SubmitUpdate) )



--- View2


view : Texts -> UiSettings -> Model -> Html Msg
view texts settings model =
    div [ class "flex flex-col" ]
        (case model.detailModel of
            Just msett ->
                viewForm texts settings model msett

            Nothing ->
                viewList texts model
        )


viewState : Texts -> Model -> Html Msg
viewState texts model =
    div
        [ classList
            [ ( S.errorMessage, not (isSuccess model.formState) )
            , ( S.successMessage, isSuccess model.formState )
            , ( "hidden", model.formState == FormStateInitial )
            ]
        , class "mb-2"
        ]
        [ case model.formState of
            FormStateInitial ->
                text ""

            FormSubmitSuccessful SubmitCreate ->
                text texts.channelCreated

            FormSubmitSuccessful SubmitUpdate ->
                text texts.channelUpdated

            FormSubmitSuccessful SubmitDelete ->
                text texts.channelDeleted

            FormErrorSubmit m ->
                text m

            FormErrorHttp err ->
                text (texts.httpError err)

            FormErrorInvalid ->
                text texts.formInvalid
        ]


isSuccess : FormState -> Bool
isSuccess state =
    case state of
        FormSubmitSuccessful _ ->
            True

        _ ->
            False


viewForm : Texts -> UiSettings -> Model -> Comp.ChannelForm.Model -> List (Html Msg)
viewForm texts settings outerModel model =
    let
        channelId =
            Comp.ChannelForm.getChannel model
                |> Maybe.map Data.NotificationChannel.getRef
                |> Maybe.map .id

        newChannel =
            channelId == Just "" || channelId == Nothing

        headline =
            case Comp.ChannelForm.channelType model of
                Data.ChannelType.Matrix ->
                    span []
                        [ text texts.integrate
                        , a
                            [ href "https://matrix.org"
                            , target "_blank"
                            , class S.link
                            , class "mx-3"
                            ]
                            [ i [ class "fa fa-external-link-alt mr-1" ] []
                            , text "Matrix"
                            ]
                        , text texts.intoDocspell
                        ]

                Data.ChannelType.Mail ->
                    span []
                        [ text texts.notifyEmailInfo
                        ]

                Data.ChannelType.Gotify ->
                    span []
                        [ text texts.integrate
                        , a
                            [ href "https://gotify.net"
                            , target "_blank"
                            , class S.link
                            , class "mx-3"
                            ]
                            [ i [ class "fa fa-external-link-alt mr-1" ] []
                            , text "Gotify"
                            ]
                        , text texts.intoDocspell
                        ]

                Data.ChannelType.Http ->
                    span []
                        [ text texts.postRequestInfo
                        ]
    in
    [ h1 [ class S.header2 ]
        [ Data.ChannelType.icon (Comp.ChannelForm.channelType model) "w-8 h-8 inline-block mr-2"
        , if newChannel then
            text texts.addChannel

          else
            text texts.updateChannel
        , div [ class "text-xs opacity-50 font-mono" ]
            [ Maybe.withDefault "" channelId |> text
            ]
        ]
    , div [ class "pt-2 pb-4 font-medium" ]
        [ headline
        ]
    , MB.view
        { start =
            [ MB.CustomElement <|
                B.primaryButton
                    { handler = onClick Submit
                    , title = texts.basics.submitThisForm
                    , icon = "fa fa-save"
                    , label = texts.basics.submit
                    , disabled = False
                    , attrs = [ href "#" ]
                    }
            , MB.SecondaryButton
                { tagger = BackToTable
                , title = texts.basics.backToList
                , icon = Just "fa fa-arrow-left"
                , label = texts.basics.backToList
                }
            ]
        , end =
            if not newChannel then
                [ MB.DeleteButton
                    { tagger = RequestDelete
                    , title = texts.deleteThisChannel
                    , icon = Just "fa fa-trash"
                    , label = texts.basics.delete
                    }
                ]

            else
                []
        , rootClasses = "mb-4"
        , sticky = True
        }
    , div [ class "mt-2" ]
        [ viewState texts outerModel
        ]
    , Html.map DetailMsg
        (Comp.ChannelForm.view texts.notificationForm settings model)
    , B.loadingDimmer
        { active = outerModel.loading
        , label = texts.basics.loading
        }
    , B.contentDimmer
        (outerModel.deleteConfirm == DeleteConfirmOn)
        (div [ class "flex flex-col" ]
            [ div [ class "text-lg" ]
                [ i [ class "fa fa-info-circle mr-2" ] []
                , text texts.reallyDeleteChannel
                ]
            , div [ class "mt-4 flex flex-row items-center" ]
                [ B.deleteButton
                    { label = texts.basics.yes
                    , icon = "fa fa-check"
                    , disabled = False
                    , handler = onClick (DeleteChannelNow (Maybe.withDefault "" channelId))
                    , attrs = [ href "#" ]
                    }
                , B.secondaryButton
                    { label = texts.basics.no
                    , icon = "fa fa-times"
                    , disabled = False
                    , handler = onClick CancelDelete
                    , attrs = [ href "#", class "ml-2" ]
                    }
                ]
            ]
        )
    ]


viewList : Texts -> Model -> List (Html Msg)
viewList texts model =
    let
        menuModel =
            { menuOpen = model.newChannelMenuOpen
            , toggleMenu = ToggleNewChannelMenu
            , menuLabel = texts.newChannel
            , onItem = NewChannelInit
            }
    in
    [ MB.view
        { start = []
        , end =
            [ Comp.ChannelMenu.channelMenu texts.channelType menuModel
            ]
        , rootClasses = "mb-4"
        , sticky = True
        }
    , Html.map TableMsg
        (Comp.NotificationChannelTable.view texts.notificationTable
            model.listModel
            model.items
        )
    ]
