{-
   Copyright 2020 Eike K. & Contributors

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


module Comp.ItemDetail.MultiEditMenu exposing
    ( Model
    , Msg
    , SaveNameState(..)
    , defaultViewConfig
    , init
    , loadModel
    , update
    , view2
    )

import Api
import Api.Model.EquipmentList exposing (EquipmentList)
import Api.Model.FolderItem exposing (FolderItem)
import Api.Model.FolderList exposing (FolderList)
import Api.Model.IdName exposing (IdName)
import Api.Model.PersonList exposing (PersonList)
import Api.Model.ReferenceList exposing (ReferenceList)
import Comp.CustomFieldMultiInput
import Comp.DatePicker
import Comp.DetailEdit
import Comp.Dropdown exposing (isDropdownChangeMsg)
import Comp.ItemDetail.FieldTabState as FTabState exposing (EditTab(..), tabName)
import Comp.ItemDetail.FormChange exposing (FormChange(..))
import Comp.SimpleTextInput
import Comp.Tabs as TB
import Comp.TagDropdown
import Data.CustomFieldChange exposing (CustomFieldChange(..))
import Data.Direction exposing (Direction)
import Data.DropdownStyle
import Data.EquipmentOrder
import Data.Fields
import Data.Flags exposing (Flags)
import Data.FolderOrder
import Data.Icons as Icons
import Data.PersonOrder
import Data.PersonUse
import Data.UiSettings exposing (UiSettings)
import DatePicker exposing (DatePicker)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Http
import Markdown
import Messages.Comp.ItemDetail.MultiEditMenu exposing (Texts)
import Page exposing (Page(..))
import Set exposing (Set)
import Styles as S
import Util.Folder
import Util.List



--- Model


type SaveNameState
    = Saving
    | SaveSuccess
    | SaveFailed


type TagEditMode
    = AddTags
    | RemoveTags
    | ReplaceTags


type alias Model =
    { tagModel : Comp.TagDropdown.Model
    , nameInput : Comp.SimpleTextInput.Model
    , folderModel : Comp.Dropdown.Model IdName
    , allFolders : List FolderItem
    , directionModel : Comp.Dropdown.Model Direction
    , itemDatePicker : DatePicker
    , itemDate : Maybe Int
    , dueDate : Maybe Int
    , dueDatePicker : DatePicker
    , corrOrgModel : Comp.Dropdown.Model IdName
    , corrPersonModel : Comp.Dropdown.Model IdName
    , concPersonModel : Comp.Dropdown.Model IdName
    , concEquipModel : Comp.Dropdown.Model IdName
    , modalEdit : Maybe Comp.DetailEdit.Model
    , tagEditMode : TagEditMode
    , customFieldModel : Comp.CustomFieldMultiInput.Model
    , openTabs : Set String
    }


type Msg
    = ItemDatePickerMsg Comp.DatePicker.Msg
    | DueDatePickerMsg Comp.DatePicker.Msg
    | SetNameMsg Comp.SimpleTextInput.Msg
    | RemoveDueDate
    | RemoveDate
    | ConfirmMsg Bool
    | ToggleTagEditMode
    | FolderDropdownMsg (Comp.Dropdown.Msg IdName)
    | TagDropdownMsg Comp.TagDropdown.Msg
    | DirDropdownMsg (Comp.Dropdown.Msg Direction)
    | OrgDropdownMsg (Comp.Dropdown.Msg IdName)
    | CorrPersonMsg (Comp.Dropdown.Msg IdName)
    | ConcPersonMsg (Comp.Dropdown.Msg IdName)
    | ConcEquipMsg (Comp.Dropdown.Msg IdName)
    | GetOrgResp (Result Http.Error ReferenceList)
    | GetPersonResp (Result Http.Error PersonList)
    | GetEquipResp (Result Http.Error EquipmentList)
    | GetFolderResp (Result Http.Error FolderList)
    | CustomFieldMsg Comp.CustomFieldMultiInput.Msg
    | ToggleAkkordionTab String


init : Model
init =
    { tagModel = Comp.TagDropdown.initWith [] []
    , directionModel =
        Comp.Dropdown.makeSingleList
            { options = Data.Direction.all
            , selected = Nothing
            }
    , corrOrgModel = Comp.Dropdown.makeSingle
    , corrPersonModel = Comp.Dropdown.makeSingle
    , concPersonModel = Comp.Dropdown.makeSingle
    , concEquipModel = Comp.Dropdown.makeSingle
    , folderModel = Comp.Dropdown.makeSingle
    , allFolders = []
    , nameInput = Comp.SimpleTextInput.initDefault Nothing
    , itemDatePicker = Comp.DatePicker.emptyModel
    , itemDate = Nothing
    , dueDate = Nothing
    , dueDatePicker = Comp.DatePicker.emptyModel
    , modalEdit = Nothing
    , tagEditMode = AddTags
    , customFieldModel = Comp.CustomFieldMultiInput.initWith []
    , openTabs = Set.empty
    }


loadModel : Flags -> Cmd Msg
loadModel flags =
    let
        ( _, dpc ) =
            Comp.DatePicker.init

        ( _, tc ) =
            Comp.TagDropdown.init flags
    in
    Cmd.batch
        [ Api.getOrgLight flags GetOrgResp
        , Api.getPersons flags "" Data.PersonOrder.NameAsc GetPersonResp
        , Api.getEquipments flags "" Data.EquipmentOrder.NameAsc GetEquipResp
        , Api.getFolders flags "" Data.FolderOrder.NameAsc False GetFolderResp
        , Cmd.map CustomFieldMsg (Comp.CustomFieldMultiInput.initCmd flags)
        , Cmd.map ItemDatePickerMsg dpc
        , Cmd.map DueDatePickerMsg dpc
        , Cmd.map TagDropdownMsg tc
        ]


isFolderMember : Model -> Bool
isFolderMember model =
    let
        selected =
            Comp.Dropdown.getSelected model.folderModel
                |> List.head
                |> Maybe.map .id
    in
    Util.Folder.isFolderMember model.allFolders selected



--- Update


type alias UpdateResult =
    { model : Model
    , cmd : Cmd Msg
    , sub : Sub Msg
    , change : FormChange
    }


resultNoCmd : FormChange -> Model -> UpdateResult
resultNoCmd change model =
    UpdateResult model Cmd.none Sub.none change


resultNone : Model -> UpdateResult
resultNone model =
    resultNoCmd NoFormChange model


update : Flags -> Msg -> Model -> UpdateResult
update flags msg model =
    case msg of
        ConfirmMsg flag ->
            resultNoCmd (ConfirmChange flag) model

        TagDropdownMsg m ->
            let
                ( m2, c2 ) =
                    Comp.TagDropdown.update m model.tagModel

                newModel =
                    { model | tagModel = m2 }

                mkChange list =
                    case model.tagEditMode of
                        AddTags ->
                            AddTagChange list

                        RemoveTags ->
                            RemoveTagChange list

                        ReplaceTags ->
                            ReplaceTagChange list

                change =
                    if Comp.TagDropdown.isChangeMsg m then
                        Comp.TagDropdown.getSelected newModel.tagModel
                            |> Util.List.distinct
                            |> List.map (\t -> IdName t.id t.name)
                            |> ReferenceList
                            |> mkChange

                    else
                        NoFormChange
            in
            UpdateResult newModel (Cmd.map TagDropdownMsg c2) Sub.none change

        ToggleTagEditMode ->
            let
                ( m2, _ ) =
                    Comp.TagDropdown.update (Comp.TagDropdown.setSelected []) model.tagModel

                newModel =
                    { model | tagModel = m2 }
            in
            case model.tagEditMode of
                AddTags ->
                    resultNone { newModel | tagEditMode = RemoveTags }

                RemoveTags ->
                    resultNone { newModel | tagEditMode = ReplaceTags }

                ReplaceTags ->
                    resultNone { newModel | tagEditMode = AddTags }

        FolderDropdownMsg m ->
            let
                ( m2, _ ) =
                    Comp.Dropdown.update m model.folderModel

                newModel =
                    { model | folderModel = m2 }

                idref =
                    Comp.Dropdown.getSelected m2 |> List.head

                change =
                    if isDropdownChangeMsg m then
                        FolderChange idref

                    else
                        NoFormChange
            in
            resultNoCmd change newModel

        GetFolderResp (Ok fs) ->
            let
                model_ =
                    { model | allFolders = fs.items }

                mkIdName fitem =
                    IdName fitem.id fitem.name

                opts =
                    fs.items
                        |> List.map mkIdName
                        |> Comp.Dropdown.SetOptions
            in
            update flags (FolderDropdownMsg opts) model_

        GetFolderResp (Err _) ->
            resultNone model

        DirDropdownMsg m ->
            let
                ( m2, _ ) =
                    Comp.Dropdown.update m model.directionModel

                newModel =
                    { model | directionModel = m2 }

                change =
                    if isDropdownChangeMsg m then
                        let
                            dir =
                                Comp.Dropdown.getSelected m2 |> List.head
                        in
                        case dir of
                            Just d ->
                                DirectionChange d

                            Nothing ->
                                NoFormChange

                    else
                        NoFormChange
            in
            resultNoCmd change newModel

        OrgDropdownMsg m ->
            let
                ( m2, _ ) =
                    Comp.Dropdown.update m model.corrOrgModel

                newModel =
                    { model | corrOrgModel = m2 }

                idref =
                    Comp.Dropdown.getSelected m2 |> List.head

                change =
                    if isDropdownChangeMsg m then
                        OrgChange idref

                    else
                        NoFormChange
            in
            resultNoCmd change newModel

        GetOrgResp (Ok orgs) ->
            let
                opts =
                    Comp.Dropdown.SetOptions orgs.items
            in
            update flags (OrgDropdownMsg opts) model

        GetOrgResp (Err _) ->
            resultNone model

        CorrPersonMsg m ->
            let
                ( m2, _ ) =
                    Comp.Dropdown.update m model.corrPersonModel

                newModel =
                    { model | corrPersonModel = m2 }

                idref =
                    Comp.Dropdown.getSelected m2 |> List.head

                change =
                    if isDropdownChangeMsg m then
                        CorrPersonChange idref

                    else
                        NoFormChange
            in
            resultNoCmd change newModel

        ConcPersonMsg m ->
            let
                ( m2, _ ) =
                    Comp.Dropdown.update m model.concPersonModel

                newModel =
                    { model | concPersonModel = m2 }

                idref =
                    Comp.Dropdown.getSelected m2 |> List.head

                change =
                    if isDropdownChangeMsg m then
                        ConcPersonChange idref

                    else
                        NoFormChange
            in
            resultNoCmd change newModel

        GetPersonResp (Ok ps) ->
            let
                { concerning, correspondent } =
                    Data.PersonUse.spanPersonList ps.items

                concRefs =
                    List.map (\e -> IdName e.id e.name) concerning

                corrRefs =
                    List.map (\e -> IdName e.id e.name) correspondent

                res1 =
                    update flags (CorrPersonMsg (Comp.Dropdown.SetOptions corrRefs)) model

                res2 =
                    update flags (ConcPersonMsg (Comp.Dropdown.SetOptions concRefs)) res1.model
            in
            res2

        GetPersonResp (Err _) ->
            resultNone model

        ConcEquipMsg m ->
            let
                ( m2, _ ) =
                    Comp.Dropdown.update m model.concEquipModel

                newModel =
                    { model | concEquipModel = m2 }

                idref =
                    Comp.Dropdown.getSelected m2 |> List.head

                change =
                    if isDropdownChangeMsg m then
                        EquipChange idref

                    else
                        NoFormChange
            in
            resultNoCmd change newModel

        GetEquipResp (Ok equips) ->
            let
                opts =
                    Comp.Dropdown.SetOptions
                        (List.map (\e -> IdName e.id e.name)
                            equips.items
                        )
            in
            update flags (ConcEquipMsg opts) model

        GetEquipResp (Err _) ->
            resultNone model

        ItemDatePickerMsg m ->
            let
                ( dp, event ) =
                    Comp.DatePicker.updateDefault m model.itemDatePicker
            in
            case event of
                DatePicker.Picked date ->
                    let
                        newModel =
                            { model | itemDatePicker = dp, itemDate = Just (Comp.DatePicker.midOfDay date) }
                    in
                    resultNoCmd (ItemDateChange newModel.itemDate) newModel

                _ ->
                    resultNone { model | itemDatePicker = dp }

        RemoveDate ->
            resultNoCmd (ItemDateChange Nothing) { model | itemDate = Nothing }

        DueDatePickerMsg m ->
            let
                ( dp, event ) =
                    Comp.DatePicker.updateDefault m model.dueDatePicker
            in
            case event of
                DatePicker.Picked date ->
                    let
                        newModel =
                            { model | dueDatePicker = dp, dueDate = Just (Comp.DatePicker.midOfDay date) }
                    in
                    resultNoCmd (DueDateChange newModel.dueDate) newModel

                _ ->
                    resultNone { model | dueDatePicker = dp }

        RemoveDueDate ->
            resultNoCmd (DueDateChange Nothing) { model | dueDate = Nothing }

        SetNameMsg lm ->
            let
                result =
                    Comp.SimpleTextInput.update lm model.nameInput

                formChange =
                    case result.change of
                        Comp.SimpleTextInput.ValueUpdated v ->
                            NameChange <| Maybe.withDefault "" v

                        Comp.SimpleTextInput.ValueUnchanged ->
                            NoFormChange
            in
            { model = { model | nameInput = result.model }
            , cmd = Cmd.batch [ Cmd.map SetNameMsg result.cmd ]
            , sub = Sub.map SetNameMsg result.sub
            , change = formChange
            }

        CustomFieldMsg lm ->
            let
                res =
                    Comp.CustomFieldMultiInput.update flags lm model.customFieldModel

                model_ =
                    { model | customFieldModel = res.model }

                cmd_ =
                    Cmd.map CustomFieldMsg res.cmd

                sub_ =
                    Sub.map CustomFieldMsg res.sub

                change =
                    case res.result of
                        NoFieldChange ->
                            NoFormChange

                        FieldValueRemove cf ->
                            RemoveCustomValue cf

                        FieldValueChange cf value ->
                            CustomValueChange cf value

                        FieldCreateNew ->
                            NoFormChange
            in
            UpdateResult model_ cmd_ sub_ change

        ToggleAkkordionTab name ->
            let
                tabs =
                    if Set.member name model.openTabs then
                        Set.remove name model.openTabs

                    else
                        Set.insert name model.openTabs
            in
            UpdateResult { model | openTabs = tabs } Cmd.none Sub.none NoFormChange



--- View


type alias ViewConfig =
    { menuClass : String
    , nameState : SaveNameState
    , customFieldState : String -> SaveNameState
    }


defaultViewConfig : ViewConfig
defaultViewConfig =
    { menuClass = ""
    , nameState = SaveSuccess
    , customFieldState = \_ -> SaveSuccess
    }



--- View2


view2 : Texts -> Flags -> ViewConfig -> UiSettings -> Model -> Html Msg
view2 =
    renderEditForm2


renderEditForm2 : Texts -> Flags -> ViewConfig -> UiSettings -> Model -> Html Msg
renderEditForm2 texts flags cfg settings model =
    let
        fieldVisible field =
            Data.UiSettings.fieldVisible settings field

        optional fields html =
            if
                List.map fieldVisible fields
                    |> List.foldl (||) False
            then
                html

            else
                span [ class "hidden" ] []

        tagModeIcon =
            case model.tagEditMode of
                AddTags ->
                    i [ class "fa fa-plus" ] []

                RemoveTags ->
                    i [ class "fa fa-eraser" ] []

                ReplaceTags ->
                    i [ class "fa fa-redo-alt" ] []

        tagModeMsg =
            case model.tagEditMode of
                AddTags ->
                    texts.tagModeAddInfo

                RemoveTags ->
                    texts.tagModeRemoveInfo

                ReplaceTags ->
                    texts.tagModeReplaceInfo

        customFieldIcon field =
            case cfg.customFieldState field.id of
                SaveSuccess ->
                    Nothing

                SaveFailed ->
                    Just "text-red-500 fa fa-exclamation-triangle"

                Saving ->
                    Just "fa fa-sync-alt animate-spin"

        customFieldSettings =
            { showAddButton = False
            , classes = "mb-4"
            , fieldIcon = customFieldIcon
            , style = dds
            , createCustomFieldTitle = ""
            , selectPlaceholder = texts.basics.selectPlaceholder
            }

        dds =
            Data.DropdownStyle.sidebarStyle

        tabStyle =
            TB.searchMenuStyle

        folderCfg =
            { makeOption = Util.Folder.mkFolderOption flags model.allFolders
            , placeholder = texts.basics.selectPlaceholder
            , labelColor = \_ -> \_ -> ""
            , style = dds
            }

        idNameCfg =
            { makeOption = \e -> { text = e.name, additional = "" }
            , labelColor = \_ -> \_ -> ""
            , placeholder = texts.basics.selectPlaceholder
            , style = dds
            }

        directionCfg =
            { makeOption =
                \entry ->
                    { text = texts.direction entry
                    , additional = ""
                    }
            , placeholder = texts.chooseDirection
            , labelColor = \_ -> \_ -> ""
            , style = dds
            }
    in
    div [ class cfg.menuClass, class "mt-2" ]
        [ TB.akkordion
            tabStyle
            (tabState settings model)
            [ { name = tabName TabConfirmUnconfirm
              , title = texts.confirmUnconfirm
              , titleRight = []
              , info = Nothing
              , body =
                    [ div
                        [ class "flex flex-row space-x-4"
                        ]
                        [ button
                            [ class S.primaryButton
                            , class "flex-grow"
                            , onClick (ConfirmMsg True)
                            ]
                            [ text texts.confirm
                            ]
                        , button
                            [ class S.secondaryButton
                            , class "flex-grow"
                            , onClick (ConfirmMsg False)
                            ]
                            [ text texts.unconfirm
                            ]
                        ]
                    ]
              }
            , { name = tabName TabTags
              , title = texts.basics.tags
              , titleRight = []
              , info = Nothing
              , body =
                    [ div [ class "field" ]
                        [ label [ class S.inputLabel ]
                            [ Icons.tagsIcon ""
                            , text texts.basics.tags
                            , a
                                [ class "float-right"
                                , class S.link
                                , href "#"
                                , title texts.changeTagMode
                                , onClick ToggleTagEditMode
                                ]
                                [ tagModeIcon
                                ]
                            ]
                        , Html.map TagDropdownMsg
                            (Comp.TagDropdown.view
                                texts.tagDropdown
                                settings
                                dds
                                model.tagModel
                            )
                        , Markdown.toHtml [ class "opacity-50 text-sm" ] tagModeMsg
                        ]
                    ]
              }
            , { name = tabName TabFolder
              , title = texts.basics.folder
              , titleRight = []
              , info = Nothing
              , body =
                    [ Html.map FolderDropdownMsg (Comp.Dropdown.view2 folderCfg settings model.folderModel)
                    , div
                        [ classList
                            [ ( S.message, True )
                            , ( "hidden", isFolderMember model )
                            ]
                        ]
                        [ Markdown.toHtml [] texts.basics.folderNotOwnerWarning
                        ]
                    ]
              }
            , { name = tabName TabCustomFields
              , title = texts.basics.customFields
              , titleRight = []
              , info = Nothing
              , body =
                    [ Html.map CustomFieldMsg
                        (Comp.CustomFieldMultiInput.view2
                            texts.customFieldMultiInput
                            customFieldSettings
                            model.customFieldModel
                        )
                    ]
              }
            , { name = tabName TabDate
              , title = texts.basics.date
              , titleRight = []
              , info = Nothing
              , body =
                    [ div [ class "relative" ]
                        [ Html.map ItemDatePickerMsg
                            (Comp.DatePicker.viewTime
                                model.itemDate
                                actionInputDatePicker2
                                model.itemDatePicker
                            )
                        , a
                            [ class S.inputLeftIconLinkSidebar
                            , href "#"
                            , onClick RemoveDate
                            ]
                            [ i [ class "fa fa-trash-alt font-thin" ] []
                            ]
                        , Icons.dateIcon2 S.dateInputIcon
                        ]
                    ]
              }
            , { name = tabName TabDueDate
              , title = texts.dueDateTab
              , titleRight = []
              , info = Nothing
              , body =
                    [ div [ class "relative" ]
                        [ Html.map DueDatePickerMsg
                            (Comp.DatePicker.viewTime
                                model.dueDate
                                actionInputDatePicker2
                                model.dueDatePicker
                            )
                        , a
                            [ class S.inputLeftIconLinkSidebar
                            , href "#"
                            , onClick RemoveDueDate
                            ]
                            [ i [ class "fa fa-trash-alt font-thin" ] []
                            ]
                        , Icons.dueDateIcon2 S.dateInputIcon
                        ]
                    ]
              }
            , { name = tabName TabCorrespondent
              , title = texts.basics.correspondent
              , titleRight = []
              , info = Nothing
              , body =
                    [ optional [ Data.Fields.CorrOrg ] <|
                        div [ class "mb-4" ]
                            [ label [ class S.inputLabel ]
                                [ Icons.organizationIcon ""
                                , span [ class "ml-2" ]
                                    [ text texts.basics.organization
                                    ]
                                ]
                            , Html.map OrgDropdownMsg
                                (Comp.Dropdown.view2
                                    idNameCfg
                                    settings
                                    model.corrOrgModel
                                )
                            ]
                    , optional [ Data.Fields.CorrPerson ] <|
                        div [ class "mb-4" ]
                            [ label [ class S.inputLabel ]
                                [ Icons.personIcon ""
                                , span [ class "ml-2" ]
                                    [ text texts.basics.person
                                    ]
                                ]
                            , Html.map CorrPersonMsg
                                (Comp.Dropdown.view2
                                    idNameCfg
                                    settings
                                    model.corrPersonModel
                                )
                            ]
                    ]
              }
            , { name = tabName TabConcerning
              , title = texts.basics.concerning
              , titleRight = []
              , info = Nothing
              , body =
                    [ optional [ Data.Fields.ConcPerson ] <|
                        div [ class "mb-4" ]
                            [ label [ class S.inputLabel ]
                                [ Icons.personIcon ""
                                , span [ class "ml-2" ]
                                    [ text texts.basics.person ]
                                ]
                            , Html.map ConcPersonMsg (Comp.Dropdown.view2 idNameCfg settings model.concPersonModel)
                            ]
                    , optional [ Data.Fields.ConcEquip ] <|
                        div [ class "mb-4" ]
                            [ label [ class S.inputLabel ]
                                [ Icons.equipmentIcon ""
                                , span [ class "ml-2" ]
                                    [ text texts.basics.equipment ]
                                ]
                            , Html.map ConcEquipMsg
                                (Comp.Dropdown.view2 idNameCfg
                                    settings
                                    model.concEquipModel
                                )
                            ]
                    ]
              }
            , { name = tabName TabDirection
              , title = texts.basics.direction
              , titleRight = []
              , info = Nothing
              , body =
                    [ Html.map DirDropdownMsg (Comp.Dropdown.view2 directionCfg settings model.directionModel)
                    ]
              }
            , { name = tabName TabName
              , title = texts.basics.name
              , titleRight = []
              , info = Nothing
              , body =
                    [ div [ class "relative" ]
                        [ Html.map SetNameMsg
                            (Comp.SimpleTextInput.view [ class S.textInputSidebar ] model.nameInput)
                        , span [ class S.inputLeftIconOnly ]
                            [ i
                                [ classList
                                    [ ( "text-green-500 fa fa-check"
                                      , cfg.nameState == SaveSuccess
                                      )
                                    , ( "text-red-500 fa fa-exclamation-triangle"
                                      , cfg.nameState == SaveFailed
                                      )
                                    , ( "sync fa fa-circle-notch animate-spin"
                                      , cfg.nameState == Saving
                                      )
                                    ]
                                ]
                                []
                            ]
                        ]
                    ]
              }
            ]
        ]


tabState : UiSettings -> Model -> TB.Tab Msg -> ( TB.State, Msg )
tabState settings model tab =
    FTabState.tabState settings
        model.openTabs
        (Just model.customFieldModel)
        (.name >> ToggleAkkordionTab)
        tab


actionInputDatePicker2 : DatePicker.Settings
actionInputDatePicker2 =
    Comp.DatePicker.defaultSettings
