module Panes.Login exposing 
    ( view
    , update
    , apply
    , subscription
    , 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 (Interview, Id)
import Network.Messages exposing (ListPersonInfo)
import Tools
import PaneMeta exposing (PaneMeta)
import Editor
import History
import Model exposing (LoginPane(..))
import Json.Encode
import Ports
import Json.Decode as JD
import Json.Decode.Pipeline exposing (required)
import Json.Encode as JE
import Panes.Modals exposing (Msg(..))

type Msg
    = None
    | SetStringValue Int String
    | Continue
    | Cancel
    | FidoReceive String JE.Value

view : View LoginPane Msg
view { meta, state } =
    case state of
        LoginPaneEmpty -> text ""
        LoginPaneRegister data ->
            let
                isEmpty : Bool
                isEmpty = data.username == "" && data.password1 == ""
                    && data.password2 == ""

                takeFirst : List (Bool, String) -> Maybe String
                takeFirst list =
                    if isEmpty
                    then Nothing
                    else list
                        |> List.filter Tuple.first
                        |> List.head
                        |> Maybe.map Tuple.second

                nameError : Maybe String
                nameError = takeFirst
                    [ (data.username == "", "Insert a username")
                    ]
                
                pw1Error : Maybe String
                pw1Error = takeFirst
                    [ (data.password1 == "", "Insert a password")
                    , (String.length data.password1 < 12, "Password too short")
                    , (String.length data.password1 > 128, "Password too long")
                    ]
                
                pw2Error : Maybe String
                pw2Error = takeFirst
                    [ (data.password1 /= data.password2, "Password doesn't match")
                    ]
                
                viewError : Maybe String -> Html msg
                viewError message =
                    case message of
                        Nothing -> text ""
                        Just m -> div [ class "pane-login-error" ]
                            [ text m ]
                
                errorAttr : Maybe String -> Html.Attribute msg
                errorAttr message =
                    HA.classList [ ("pane-login-value-invalid", message /= Nothing) ]

            in div [ class "pane-login" ]
                    [ viewLoginBubble
                    , viewCancel
                    , div [ class "pane-login-card" ]
                        [ Html.h2 [] [ text "Register new Account" ]
                        , Html.h3 []
                            [ text <| "Database: " ++ data.database
                            ]
                        , div [ class "pane-login-form" ]
                            [ div [ class "pane-login-title" ]
                                [ text "Username" ]
                            , viewError nameError
                            , Html.input
                                [ HA.type_ "text" 
                                , HA.value data.username
                                , HE.onInput <| SetStringValue 0
                                , errorAttr nameError
                                ] []
                            ]
                        , div [ class "pane-login-form" ]
                            [ div [ class "pane-login-title" ]
                                [ text "Password" ]
                            , viewError pw1Error
                            , Html.input
                                [ HA.type_ "password" 
                                , HA.value data.password1
                                , HE.onInput <| SetStringValue 1
                                , errorAttr pw1Error
                                ] []
                            ]
                        , div [ class "pane-login-form" ]
                            [ div [ class "pane-login-title" ]
                                [ text "retype Password" ]
                            , viewError pw2Error
                            , Html.input
                                [ HA.type_ "password" 
                                , HA.value data.password2
                                , HE.onInput <| SetStringValue 2
                                , errorAttr pw2Error
                                ] []
                            ]
                        , Html.button
                            [ HA.disabled <| List.any ((/=) Nothing)
                                [ nameError, pw1Error, pw2Error ] || isEmpty
                            , HE.onClick Continue
                            ]
                            [ text "Continue" ]
                        ]
                    ]
        LoginPaneRegisterFido data ->
            div [ class "pane-login" ]
                [ viewFidoBubble
                , viewCancel
                , div [ class "pane-login-card" ]
                    [ Html.h2 [] [ text "Two Factor Authentification" ]
                    , case data.database of
                        Just database -> Html.h3 []
                            [ text <| "Database: " ++ database
                            ]
                        Nothing -> text ""
                    , Html.p []
                        [ text "Follow the instructions from your browser to continue the registration" ]
                    , case data.error of
                        Nothing -> text ""
                        Just error ->
                            Html.p [ class "pane-login-error" ]
                                [ text error ]
                    ]
                ]
        LoginPaneLogin data ->
            div [ class "pane-login" ]
                [ viewLoginBubble
                , viewCancel
                , div [ class "pane-login-card" ]
                    [ Html.h2 [] [ text "Login into your Account" ]
                    , case data.database of
                        Nothing -> text ""
                        Just database ->
                            Html.h3 []
                                [ text <| "Database: " ++ database
                                ]
                    , div [ class "pane-login-form" ]
                        [ div [ class "pane-login-title" ]
                            [ text "Username" ]
                        , Html.input
                            [ HA.type_ "text" 
                            , HA.value data.username
                            , HE.onInput <| SetStringValue 0
                            ] []
                        ]
                    , div [ class "pane-login-form" ]
                        [ div [ class "pane-login-title" ]
                            [ text "Password" ]
                        , Html.input
                            [ HA.type_ "password" 
                            , HA.value data.password
                            , HE.onInput <| SetStringValue 1
                            ] []
                        ]
                    , case data.error of
                        Nothing -> text ""
                        Just error ->
                            Html.p [ class "pane-login-error" ]
                                [ text error ]
                    , Html.button
                        [ HA.disabled <| List.any ((==) "")
                            [ data.username, data.password ]
                        , HE.onClick Continue
                        ]
                        [ text "Continue" ]
                    ]
                ]

viewCancel : Html Msg
viewCancel =
    div [ class "pane-login-cancel"
        , HE.onClick Cancel
        ]
        [ Html.img
            [ HA.src "/img/svgrepo/essential-set-2/multiply-135247.svg" 
            ] []
        ]

viewLoginBubble : Html msg
viewLoginBubble =
    div [ class "pane-login-bubble" ]
        [ Html.img
            [ HA.src "/img/svgrepo/essential-set-2/id-card-36356.svg" 
            ] []
        ]

viewFidoBubble : Html msg
viewFidoBubble =
    div [ class "pane-login-bubble" ]
        [ Html.img
            [ HA.src "/img/svgrepo/essential-set-2/fingerprint-84006.svg" 
            ] []
        ]
    
update : Update LoginPane Msg
update msg { meta, state } =
    case (msg, state) of
        (None, _) -> triple state Cmd.none []
        (SetStringValue 0 value, LoginPaneRegister data) ->
            triple
                (LoginPaneRegister { data | username = String.left 50 value })
                Cmd.none
                []
        (SetStringValue 1 value, LoginPaneRegister data) ->
            triple
                (LoginPaneRegister { data | password1 = value })
                Cmd.none
                []
        (SetStringValue 2 value, LoginPaneRegister data) ->
            triple
                (LoginPaneRegister { data | password2 = value })
                Cmd.none
                []
        (SetStringValue 0 value, LoginPaneLogin data) ->
            triple
                (LoginPaneLogin { data | username = value })
                Cmd.none
                []
        (SetStringValue 1 value, LoginPaneLogin data) ->
            triple
                (LoginPaneLogin { data | password = value })
                Cmd.none
                []
        (SetStringValue _ _, _) -> triple state Cmd.none []
        (Continue, LoginPaneRegister data) ->
            triple
                state
                Cmd.none
                [ Panes.Send
                    <| Network.Messages.AccountRegister
                        data.database
                        data.key
                        data.username
                        data.password1
                ]
        (Continue, LoginPaneLogin data) ->
            triple
                state
                Cmd.none
                [ Panes.Send
                    <| Network.Messages.AccountLogin
                        data.username
                        data.password
                ]
        (Continue, _) -> triple state Cmd.none []
        (Cancel, _) -> triple LoginPaneEmpty Cmd.none []
        (FidoReceive method value, LoginPaneRegisterFido _) ->
            triple
                state
                Cmd.none
                [ Panes.Send
                    <| Network.Messages.FidoReceive
                        method
                        value
                ]
        (FidoReceive _ _, _) -> triple state Cmd.none []

apply : Apply LoginPane Msg
apply event { meta, state } =
    case event of
        Network.Messages.AccountFidoRequest command database value ->
            triple
                (LoginPaneRegisterFido
                    { database = database
                    , fidoData = value
                    , error = Nothing
                    }
                )
                (Ports.fido2send
                    <| JE.object
                    [ ("method", JE.string command)
                    , ("data", value)
                    ]
                )
                []
        Network.Messages.FidoResult True _ ->
            triple LoginPaneEmpty Cmd.none []
        Network.Messages.FidoResult False error ->
            triple
                (case state of
                    LoginPaneRegisterFido data -> LoginPaneRegisterFido { data | error = error }
                    LoginPaneLogin data -> LoginPaneLogin { data | error = error }
                    _ -> state
                )
                Cmd.none
                []
        Network.Messages.LoginRequest name ->
            triple
                (LoginPaneLogin
                    { database = name
                    , username = ""
                    , password = ""
                    , error = Nothing
                    }
                )
                Cmd.none
                []
        _ -> triple state Cmd.none []

subscription : Subscription LoginPane Msg
subscription data =
    Ports.fido2receive
        <| Maybe.withDefault None
        << Result.toMaybe
        << JD.decodeValue
            (JD.succeed FidoReceive
                |> required "method" JD.string
                |> required "data" JD.value
            )
