{-
   Copyright 2020 Eike K. & Contributors

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


module Page.Queue.View2 exposing (viewContent, viewSidebar)

import Api.Model.JobDetail exposing (JobDetail)
import Api.Model.JobLogEvent exposing (JobLogEvent)
import Comp.Progress
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Data.Priority
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Messages.Page.Queue exposing (Texts)
import Page.Queue.Data exposing (..)
import Styles as S
import Util.Time exposing (formatIsoDateTime)


viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg
viewSidebar texts visible _ _ model =
    let
        count v =
            case v of
                CurrentJobs ->
                    List.length model.state.progress

                QueueAll ->
                    List.length model.state.queued
                        + List.length model.state.completed

                QueueWaiting ->
                    List.length model.state.queued

                QueueSuccess ->
                    filterJobDetails model.state.completed "success"
                        |> List.length

                QueueError ->
                    filterJobDetails model.state.completed "failed"
                        |> List.length

                QueueCancelled ->
                    filterJobDetails model.state.completed "cancelled"
                        |> List.length

        tabLink cls v icon label =
            a
                [ href "#"
                , class S.sidebarLink
                , class cls
                , classList [ ( "bg-blue-100 dark:bg-bluegray-600", model.queueView == v ) ]
                , onClick (SetQueueView v)
                ]
                [ i [ class icon ]
                    []
                , div
                    [ class "ml-3" ]
                    [ text label ]
                , div [ class "ml-auto bg-gray-200 border rounded-full h-6 w-6 flex items-center justify-center text-xs dark:bg-bluegray-800 dark:text-bluegray-200 dark:border-bluegray-800 dark:bg-opacity-50" ]
                    [ count v |> String.fromInt |> text
                    ]
                ]
    in
    div
        [ id "sidebar"
        , class S.sidebar
        , class S.sidebarBg
        , classList [ ( "hidden", not visible ) ]
        ]
        [ div [ class "" ]
            [ h1 [ class S.header1 ]
                [ text texts.sidebarTitle
                ]
            ]
        , div [ class "flex flex-col my-2" ]
            [ tabLink "" CurrentJobs "fa fa-play-circle" texts.currentlyRunning
            , tabLink "" QueueAll "fa fa-hourglass-half" texts.queue
            , tabLink "ml-8" QueueWaiting "fa fa-clock" texts.waiting
            , tabLink "ml-8" QueueError "fa fa-bolt" texts.errored
            , tabLink "ml-8" QueueSuccess "fa fa-check" texts.success
            , tabLink "ml-8" QueueCancelled "fa fa-times-circle" texts.cancelled
            ]
        ]


viewContent : Texts -> Flags -> UiSettings -> Model -> Html Msg
viewContent texts _ _ model =
    let
        gridStyle =
            "grid gap-4 grid-cols-1 md:grid-cols-2"

        message str =
            div [ class "h-28 flex flex-col items-center justify-center w-full" ]
                [ div [ class S.header2 ]
                    [ text str
                    ]
                ]
    in
    div
        [ class "py-2"
        , class S.content
        ]
        [ case model.showLog of
            Just job ->
                renderJobLog job

            Nothing ->
                span [ class "hidden" ] []
        , case model.queueView of
            CurrentJobs ->
                if List.isEmpty model.state.progress then
                    message texts.noJobsRunning

                else
                    div [ class "flex flex-col space-y-2" ]
                        (List.map (renderProgressCard texts model) model.state.progress)

            QueueAll ->
                if List.isEmpty model.state.completed && List.isEmpty model.state.completed then
                    message texts.noJobsDisplay

                else
                    div [ class gridStyle ]
                        (List.map (renderInfoCard texts model)
                            (model.state.queued ++ model.state.completed)
                        )

            QueueWaiting ->
                if List.isEmpty model.state.queued then
                    message texts.noJobsWaiting

                else
                    div [ class gridStyle ]
                        (List.map (renderInfoCard texts model) model.state.queued)

            QueueError ->
                let
                    items =
                        filterJobDetails model.state.completed "failed"
                in
                if List.isEmpty items then
                    message texts.noJobsFailed

                else
                    div [ class gridStyle ]
                        (List.map (renderInfoCard texts model) items)

            QueueSuccess ->
                let
                    items =
                        filterJobDetails model.state.completed "success"
                in
                if List.isEmpty items then
                    message texts.noJobsSuccess

                else
                    div [ class gridStyle ]
                        (List.map (renderInfoCard texts model) items)

            QueueCancelled ->
                let
                    items =
                        filterJobDetails model.state.completed "cancelled"
                in
                if List.isEmpty items then
                    message texts.noJobsCancelled

                else
                    div [ class gridStyle ]
                        (List.map (renderInfoCard texts model) items)
        ]


filterJobDetails : List JobDetail -> String -> List JobDetail
filterJobDetails list state =
    let
        isState job =
            state == job.state
    in
    List.filter isState list


renderJobLog : JobDetail -> Html Msg
renderJobLog job =
    div
        [ class " absolute top-12 left-0 w-full h-full-12 z-40 flex flex-col items-center px-4 py-2 "
        , class "bg-white bg-opacity-80 dark:bg-black dark:bg-bluegray-900 dark:bg-opacity-90"
        ]
        [ div [ class (S.box ++ "py-2 px-2 flex flex-col w-full") ]
            [ div [ class "flex flex-row mb-4 px-2" ]
                [ span [ class "font-semibold" ]
                    [ text job.name
                    ]
                , div [ class "flex-grow flex flex-row justify-end" ]
                    [ a
                        [ href "#"
                        , class S.link
                        , onClick QuitShowLog
                        ]
                        [ i [ class "fa fa-times" ] []
                        ]
                    ]
                ]
            , div [ class styleJobLog ]
                (List.map renderLogLine job.logs)
            ]
        ]


renderProgressCard : Texts -> Model -> JobDetail -> Html Msg
renderProgressCard texts model job =
    div [ class (S.box ++ "px-2 flex flex-col") ]
        [ Comp.Progress.topAttachedIndicating job.progress
        , Html.map (DimmerMsg job)
            (Comp.YesNoDimmer.viewN
                (model.cancelJobRequest == Just job.id)
                (dimmerSettings texts)
                model.deleteConfirm
            )
        , div [ class "py-2 flex flex-row x-space-2 items-center" ]
            [ div [ class "flex flex-row items-center py-0.5" ]
                [ i [ class "fa fa-circle-notch animate-spin" ] []
                , span [ class "ml-2" ]
                    [ text job.name
                    ]
                ]
            , div [ class "flex-grow flex flex-row items-center justify-end" ]
                [ div [ class S.basicLabel ]
                    [ text job.state
                    , div [ class "ml-3" ]
                        [ Maybe.withDefault "" job.worker |> text
                        ]
                    ]
                , div [ class (S.basicLabel ++ "ml-2") ]
                    [ i [ class "fa fa-clock" ] []
                    , div [ class "ml-3" ]
                        [ getDuration model job |> Maybe.withDefault "-:-" |> text
                        ]
                    ]
                ]
            ]
        , div [ class "py-2", id "joblog" ]
            [ div [ class styleJobLog ]
                (List.map renderLogLine job.logs)
            ]
        , div [ class "py-2 flex flex-row justify-end" ]
            [ button [ class S.secondaryButton, onClick (RequestCancelJob job) ]
                [ text texts.basics.cancel
                ]
            ]
        ]


styleJobLog : String
styleJobLog =
    "bg-gray-900 text-xs leading-5 px-2 py-1 font-mono text-gray-100 overflow-auto max-h-96 rounded"


renderLogLine : JobLogEvent -> Html Msg
renderLogLine log =
    let
        lineStyle =
            case String.toLower log.level of
                "info" ->
                    ""

                "debug" ->
                    "opacity-50"

                "warn" ->
                    "text-yellow-400"

                "error" ->
                    "text-red-400"

                _ ->
                    ""
    in
    span [ class lineStyle ]
        [ formatIsoDateTime log.time |> text
        , text ": "
        , text log.message
        , br [] []
        ]


isFinal : JobDetail -> Bool
isFinal job =
    case job.state of
        "failed" ->
            True

        "success" ->
            True

        "cancelled" ->
            True

        _ ->
            False


dimmerSettings : Texts -> Comp.YesNoDimmer.Settings
dimmerSettings texts =
    let
        defaults =
            Comp.YesNoDimmer.defaultSettings texts.deleteThisJob texts.basics.yes texts.basics.no
    in
    { defaults
        | headerClass = "text-lg text-white"
        , headerIcon = ""
        , extraClass = "rounded"
    }


renderInfoCard : Texts -> Model -> JobDetail -> Html Msg
renderInfoCard texts model job =
    let
        prio =
            Data.Priority.fromString job.priority
                |> Maybe.withDefault Data.Priority.Low

        color solid =
            jobStateColor job solid

        labelStyle solid =
            " label min-h-6 inline text-xs font-semibold " ++ color solid ++ " "
    in
    div
        [ class (S.box ++ "px-4 py-4 flex flex-col rounded relative")
        ]
        [ Html.map (DimmerMsg job)
            (Comp.YesNoDimmer.viewN
                (model.cancelJobRequest == Just job.id)
                (dimmerSettings texts)
                model.deleteConfirm
            )
        , div [ class "flex flex-row" ]
            [ div [ class "flex-grow items-center" ]
                [ i
                    [ classList
                        [ ( "fa fa-check", job.state == "success" )
                        , ( "fa fa-redo", job.state == "stuck" )
                        , ( "fa fa-bolt", job.state == "failed" )
                        , ( "fa fa-meh-outline", job.state == "canceled" )
                        , ( "fa fa-cog", not (isFinal job) && job.state /= "stuck" )
                        ]
                    , class "justify-center"
                    ]
                    []
                , div [ class (labelStyle True ++ " ml-2") ]
                    [ text job.state
                    ]
                , div [ class "ml-2 break-all hidden sm:inline-block" ]
                    [ text job.name
                    ]
                ]
            , div [ class "flex flex-row space-x-2" ]
                [ a
                    [ onClick (ShowLog job)
                    , href "#"
                    , class S.link
                    , classList [ ( "hidden", not (isFinal job || job.state == "stuck") ) ]
                    ]
                    [ i
                        [ class "fa fa-file"
                        , title texts.showLog
                        ]
                        []
                    ]
                , a
                    [ title texts.remove
                    , href "#"
                    , class S.link
                    , onClick (RequestCancelJob job)
                    ]
                    [ i
                        [ class "fa fa-times"
                        ]
                        []
                    ]
                , div
                    [ classList [ ( "hidden", isFinal job ) ]
                    ]
                    [ div [ class "font-mono" ]
                        [ getDuration model job |> Maybe.withDefault "3:12" |> text
                        ]
                    ]
                ]
            ]
        , div [ class "sm:hidden mt-1 break-all" ]
            [ text job.name
            ]
        , div [ class "my-2" ]
            [ hr [ class S.border ] []
            ]
        , div [ class "flex flex-row space-x-2 items-center flex-wrap" ]
            [ div [ class "flex flex-row justify-start " ]
                [ div [ class "text-xs font-semibold" ]
                    [ texts.formatDateTime job.submitted |> text
                    ]
                ]
            , div [ class "flex-grow flex flex-row justify-end space-x-2 flex-wrap" ]
                [ div
                    [ class (labelStyle False)
                    , classList [ ( "hidden", not (isFinal job) ) ]
                    ]
                    [ i [ class "fa fa-clock mr-3" ] []
                    , span []
                        [ getDuration model job |> Maybe.withDefault "-:-" |> text
                        ]
                    ]
                , div [ class (labelStyle False) ]
                    [ span [ class "mr-3" ]
                        [ text texts.retries
                        ]
                    , span []
                        [ job.retries |> String.fromInt |> text
                        ]
                    ]
                , case job.state of
                    "waiting" ->
                        a
                            [ class (labelStyle False)
                            , onClick (ChangePrio job.id (Data.Priority.next prio))
                            , href "#"
                            , title texts.changePriority
                            ]
                            [ i [ class "sort numeric up icon" ] []
                            , text "Prio"
                            , div [ class "detail" ]
                                [ code []
                                    [ Data.Priority.fromString job.priority
                                        |> Maybe.map Data.Priority.toName
                                        |> Maybe.withDefault job.priority
                                        |> text
                                    ]
                                ]
                            ]

                    _ ->
                        div
                            [ class (labelStyle False)
                            ]
                            [ span [ class "mr-3" ]
                                [ text texts.prio
                                ]
                            , code [ class "font-mono" ]
                                [ Data.Priority.fromString job.priority
                                    |> Maybe.map Data.Priority.toName
                                    |> Maybe.withDefault job.priority
                                    |> text
                                ]
                            ]
                ]
            ]
        ]


jobStateColor : JobDetail -> Bool -> String
jobStateColor job solid =
    case job.state of
        "success" ->
            if solid then
                S.greenSolidLabel

            else
                S.greenBasicLabel

        "failed" ->
            if solid then
                S.redSolidLabel

            else
                S.redBasicLabel

        "canceled" ->
            "text-orange-500 border-orange-500"

        "stuck" ->
            "text-purple-500 border-purple-500"

        "scheduled" ->
            "text-blue-500 border-blue-500"

        "waiting" ->
            "text-gray-500 border-gray-500"

        _ ->
            ""
