module Panes.Person exposing
    ( component
    , Model
    , Msg
    )

import Html exposing (Html, div, text)
import Html.Attributes as HA exposing (class)
import Html.Events as HE
import Triple exposing (triple)
import Panes exposing (..)
import Lazy exposing (Lazy)
import Data exposing (Person, Id, File)
import Network.Messages
import Tools
import PaneMeta exposing (PaneMeta)
import Editor
import Editor.Attributes
import History
import Debug.Extra
import Dict
import Panes.Person.Files
import Tools.DeleteButton
import Notification exposing (Notification)
import Panes.Special.MissingData

component : RawComponent Id Model Msg
component =
    { init = init
    , view = view
    , title = always ""
    , update = update
    , apply = apply
    , save = save
    , subscription = subscription
    }

type alias Model = Lazy Id (Result Id LoadedModel)

type alias LoadedModel =
    { data: Person
    , editContact: Maybe Editor.Component
    , editName: Maybe Editor.Component
    , editAttr: Maybe Editor.Attributes.Editor
    , files: Panes.Person.Files.Model
    }

type Msg
    = None
    | SendEvent Event
    | DoDelete
    | EditContact Editor.EditorMsg
    | EditName Editor.EditorMsg
    | EditAttr Editor.Attributes.Msg
    | WrapFiles Panes.Person.Files.NodeMsg

init : Init Id Model Msg
init _ id =
    triple
        (Lazy.Loading id)
        Cmd.none
        [ Send <| Network.Messages.DataPersonGet id
        ]

view : View Model Msg
view { meta, state } =
    case state of
        Lazy.Loading _ ->
            div [ class "pane-person" ]
                [ text "Loading..." ]
        Lazy.Loaded (Err _) ->
            Html.map SendEvent
            <| Panes.Special.MissingData.viewMissingData
        Lazy.Loaded (Ok info) ->
            div [ class "pane-person" ]
                [ if info.data.deleted then text "" else
                    Tools.DeleteButton.view DoDelete
                , div [ class "pane-person-title" ]
                    [ div [ class "pane-person-title-text" ]
                        [ text info.data.role ]
                    , div [ class "pane-person-title-id" ]
                        [ text info.data.id ]
                    , div [ class "utils-database" ]
                        [ text info.data.database ]
                    ]
                , viewInfoGrid meta info
                , div [ class "pane-person-title-2" ]
                    [ div [ class "pane-person-title-text" ]
                        [ text "Attributes" ]
                    ]
                , Html.map EditAttr
                    <| Maybe.withDefault
                        (div [ class "pane-person-no-attr" ]
                            [ text <| "The schema for the role of this person couldn't be loaded. "
                                ++ "The editor cannot be shown."
                            ]
                        )
                    <| Maybe.map
                        (Editor.Attributes.view info.data.deleted meta.now)
                    <| info.editAttr
                , div [ class "pane-person-title-2" ]
                    [ div [ class "pane-person-title-text" ]
                        [ text "Files" ]
                    ]
                , Html.map WrapFiles
                    <| Panes.Person.Files.view meta info.files
                ]

viewInfoGrid : PaneMeta -> LoadedModel -> Html Msg
viewInfoGrid meta { data, editContact, editName } =
    div [ class "utils-edit-grid" ]
    <| List.map
        (\(title, cell) ->
            div [ class "utils-edit-grid-row" ]
                [ div [ class "utils-edit-grid-name" ]
                    [ text title ]
                , div [ class "utils-edit-grid-cell" ]
                    [ cell ]
                ]
        )
    <| List.filterMap identity
        [ Just
            <| Tuple.pair "created"
            <| Tools.viewDateElement meta.zone meta.now data.created
        , Just 
            <| Tuple.pair "modified"
            <| Tools.viewDateElement meta.zone meta.now data.modified
        , if data.deleted
            then Just ("deleted", text "true")
            else Nothing
        , Maybe.map
            (\id ->
                Tuple.pair "interview"
                <| div []
                    [ Html.a
                        [ HE.onClick <| SendEvent <| Open <| History.Interview id 
                        , class "utils-link"
                        ]
                        [ text "Open" ]
                    ]
            )
            data.interview
        , Maybe.map
            (\edit ->
                Tuple.pair "name"
                <| Html.map EditName
                <| Editor.view data.deleted meta.now edit
            )
            editName
        , Maybe.map
            (\edit ->
                Tuple.pair "contact"
                <| Html.map EditContact
                <| Editor.view data.deleted meta.now edit
            )
            editContact
        ]

update : Update Model Msg
update msg { meta, state } =
    case (msg, state) of
        (None, _) -> triple
            state
            Cmd.none
            []
        (SendEvent event, _) -> triple
            state
            Cmd.none
            [ event ]
        (_, Lazy.Loading _) -> triple state Cmd.none []
        (DoDelete, Lazy.Loaded (Ok d)) ->
            triple
                state
                Cmd.none
                [ Panes.RemoveHistory <| History.Person d.data.id
                , Panes.Open <| History.Home
                , Panes.Send <| Network.Messages.EditPersonSend
                    { id = d.data.id
                    , delete = True
                    , name = Nothing
                    , contact = Nothing
                    }
                ]
        (EditContact sub, Lazy.Loaded (Ok d)) ->
            case d.editContact of
                Just edit ->
                    Editor.update meta.now sub edit
                    |> Triple.mapAll
                        (\x -> Lazy.Loaded <| Ok { d | editContact = Just x })
                        (Cmd.map EditContact)
                        identity
                Nothing -> triple state Cmd.none []
        (EditName sub, Lazy.Loaded (Ok d)) ->
            case d.editName of
                Just edit ->
                    Editor.update meta.now sub edit
                    |> Triple.mapAll
                        (\x -> Lazy.Loaded <| Ok { d | editName = Just x })
                        (Cmd.map EditName)
                        identity
                Nothing -> triple state Cmd.none []
        (EditAttr sub, Lazy.Loaded (Ok d)) ->
            case d.editAttr of
                Just edit ->
                    Editor.Attributes.update meta.now sub edit
                    |> Triple.mapAll
                        (\x -> Lazy.Loaded <| Ok { d | editAttr = Just x })
                        (Cmd.map EditAttr)
                        identity
                Nothing -> triple state Cmd.none []
        (WrapFiles sub, Lazy.Loaded (Ok d)) ->
            Panes.Person.Files.update d.data.id meta sub d.files
            |> Triple.mapAll
                (\x -> Lazy.Loaded <| Ok { d | files = x })
                (Cmd.map WrapFiles)
                identity
        (_, Lazy.Loaded (Err _)) -> triple state Cmd.none []

build : PaneMeta -> Id -> Person -> Panes.Person.Files.Model -> LoadedModel
build meta id info files =
    { data = info
    , editContact = 
        Maybe.map
            (Editor.initMultiLine
                (Editor.TargetPersonContact id)
            )
            info.contact
    , editName = 
        Maybe.map
            (Editor.initSingleLine
                (Editor.TargetPersonName id)
            )
            info.name
    , editAttr =
        Maybe.map 
            (Editor.Attributes.init
                meta.zoneName meta.zone meta.now
                id info.attributes
            )
        <| Maybe.andThen
            (Dict.get info.role)
        <| Maybe.andThen
            (\x -> Dict.get x meta.schemas)
        <| Maybe.map .roleSchema
        <| Maybe.andThen
            (.database
                >> List.filter (\{ name } -> name == info.database)
                >> List.head
            )
        <| meta.info
    , files = files
    }

apply : Apply Model Msg
apply event { meta, state } =
    (\data ->
        case Triple.first data of
            Lazy.Loading _ -> data
            Lazy.Loaded (Err _) -> data
            Lazy.Loaded (Ok d) ->
                Panes.Person.Files.apply
                    d.data.id
                    event
                    d.files
                |> Triple.mapAll
                    (\x -> Lazy.Loaded <| Ok { d | files = x })
                    (\x -> Cmd.batch [ Triple.second data, Cmd.map WrapFiles x ])
                    (\x -> Triple.third data ++ x)
    )
    <| case event of
        Network.Messages.InfoSend _ ->
            case state of
                Lazy.Loaded (Err id) ->
                    triple state Cmd.none
                        [ Send <| Network.Messages.DataPersonGet id ]
                _ -> triple state Cmd.none []
        Network.Messages.SchemaResponse name schema ->
            case state of
                Lazy.Loading _ -> triple state Cmd.none []
                Lazy.Loaded (Err _) -> triple state Cmd.none []
                Lazy.Loaded (Ok data) ->
                    if data.editAttr == Nothing
                    then triple
                        (Lazy.Loaded <| Ok
                            <| build
                                { meta | schemas = Dict.insert name schema meta.schemas }
                                data.data.id data.data data.files
                        )
                        Cmd.none
                        []
                    else triple state Cmd.none []
        Network.Messages.DataPersonSend id Nothing ->
            triple (Lazy.Loaded <| Err id) Cmd.none []
        Network.Messages.DataPersonSend id (Just info) ->
            if Lazy.Loading id == state || Lazy.Loaded (Err id) == state
            then Panes.Person.Files.init info.database id
                |> \(files, fileCmd) -> triple
                    (Lazy.Loaded
                        <| Ok
                        <| build meta id info files
                    )
                    (Cmd.map WrapFiles fileCmd)
                    [ Send <| Network.Messages.FilePersonFetch
                        { person = id
                        , root = Nothing
                        }
                    ]
            else triple state Cmd.none []
        Network.Messages.FilePersonSend { person, root } list ->
            case state of
                Lazy.Loading _ -> triple state Cmd.none []
                Lazy.Loaded (Err _) -> triple state Cmd.none []
                Lazy.Loaded (Ok d) ->
                    if d.data.id == person
                    then triple
                        (Lazy.Loaded <| Ok
                            { d
                            | files = d.files |> \files ->
                                { files
                                | tree = case root of
                                    Just rootId ->
                                        Panes.Person.Files.append list rootId files.tree
                                    Nothing ->
                                        (++) files.tree <| List.map (Panes.Person.Files.create) list
                                }
                            }
                        )
                        Cmd.none
                        <| if root == Nothing
                            then List.filterMap
                                    (Panes.Person.Files.loadSingle d.data.id)
                                <| List.map Panes.Person.Files.create list
                            else []
                    else triple state Cmd.none []
        Network.Messages.EditDenied denied ->
            if denied.target == Network.Messages.EditTargetAttribute
                || denied.target == Network.Messages.EditTargetPerson
            then triple state Cmd.none
                [ Panes.SetNotification
                    { id = Nothing
                    , img = Just "/img/svgrepo/essential-set-2/error-138890.svg"
                    , title = "Cannot edit person"
                    , description = denied.reason
                    , progress = Notification.NoProgress
                    , close = Just <| Tools.addMs 15000 meta.now
                    }
                ]
            else triple state Cmd.none []
        _ -> triple state Cmd.none []

save : Save Model
save model =
    case model of
        Lazy.Loading _ -> []
        Lazy.Loaded (Err _) -> []
        Lazy.Loaded (Ok d) ->
            List.concat
                [ Maybe.withDefault []
                    <| Maybe.map Editor.save d.editName
                , Maybe.withDefault []
                    <| Maybe.map Editor.save d.editContact
                , Maybe.withDefault []
                    <| Maybe.map Editor.Attributes.save d.editAttr
                ]

subscription : Subscription Model Msg
subscription { state } = 
    case state of
        Lazy.Loading _ -> Sub.none
        Lazy.Loaded (Err _) -> Sub.none
        Lazy.Loaded (Ok d) ->
            Sub.map WrapFiles
            <| Panes.Person.Files.subscription d.files
