module Config.Helpers.Markdown exposing (..)

import Browser
import Config.Helpers.Converters exposing (toTitleCase)
import Config.Helpers.Format
    exposing
        ( headerFontSizeBig
        , headerFontSizeMedium
        , headerFontSizeSmall
        , paragraphFontSize
        )
import Config.Helpers.Response exposing (pageList)
import Config.Helpers.ServiceFormat exposing (divider)
import Config.Style.Colour.Helpers exposing (colourTheme)
import Config.Style.Transitions
    exposing
        ( hoverFontDarkOrange
        , hoverFontLightOrange
        , transitionStyleFast
        , transitionStyleMedium
        )
import Element as E exposing (..)
import Element.Background as B
import Element.Border as D
import Element.Font as F
import Element.Input as Input
import Element.Region as Region
import Html exposing (Attribute, Html)
import Html.Attributes
import Markdown.Block as Block exposing (Block, Inline, ListItem(..), Task(..))
import Markdown.Html
import Markdown.Parser
import Markdown.Renderer


articleImage : String -> Element msg
articleImage pic =
    el
        [ centerX
        , width fill
        ]
    <|
        column
            [ E.paddingEach
                { top = 20
                , bottom = 20
                , left = 20
                , right = 20
                }
            ]
            [ image
                [ width fill
                ]
                { src = "/blog/" ++ pic ++ ".png", description = "" }
            ]


renderDeviceMarkdown : String -> Element msg
renderDeviceMarkdown markdown =
    case renderMarkdown markdown of
        Ok ( toc, renderedMarkdown ) ->
            paragraph []
                [ column
                    [ width fill
                    , centerX
                    , spacing 10
                    ]
                    (tocView toc :: renderedMarkdown)
                ]

        Err error ->
            E.text error


renderMarkdown : String -> Result String ( TableOfContents, List (Element msg) )
renderMarkdown markdown =
    case
        markdown
            |> Markdown.Parser.parse
    of
        Ok okAst ->
            case
                Markdown.Renderer.render elmUiRenderer okAst
            of
                Ok rendered ->
                    Ok ( buildToc okAst, rendered )

                Err errors ->
                    Err errors

        Err error ->
            Err (error |> List.map Markdown.Parser.deadEndToString |> String.join "\n")


renderDeviceMarkdownNoToc : String -> Element msg
renderDeviceMarkdownNoToc markdown =
    case renderMarkdownNoToc markdown of
        Ok ( _, renderedMarkdown ) ->
            paragraph []
                [ column
                    [ width fill
                    , centerX
                    , spacing 10
                    ]
                    renderedMarkdown
                ]

        Err error ->
            E.text error


renderMarkdownNoToc :
    String
    -> Result String ( TableOfContents, List (Element msg) ) -- Keep original return type
renderMarkdownNoToc markdown =
    case
        markdown
            |> Markdown.Parser.parse
    of
        Ok okAst ->
            case
                Markdown.Renderer.render elmUiRenderer okAst
            of
                Ok rendered ->
                    Ok ( buildToc okAst, rendered )

                -- Keep building TOC but don't use it
                Err errors ->
                    Err errors

        Err error ->
            Err (error |> List.map Markdown.Parser.deadEndToString |> String.join "\n")


tocView : TableOfContents -> Element msg
tocView toc =
    column
        [ alignTop
        , width fill
        , spacing 10
        ]
        [ paragraph
            [ F.bold
            , headerFontSizeBig
            , F.center
            , width fill
            , F.color colourTheme.textLightOrange
            ]
            [ text "TABLE OF CONTENTS" ]
        , column
            [ E.spacing 3
            , paragraphFontSize
            , E.width fill
            ]
            (toc
                |> List.indexedMap
                    (\index headingBlock ->
                        row
                            [ E.width fill ]
                            [ el
                                [ E.alignTop
                                , width <| px 30
                                , F.bold
                                , alignRight
                                , alignBottom
                                ]
                                (text (String.fromInt (index + 1) ++ "."))
                            , el
                                [ F.alignLeft
                                , E.width fill
                                , headerFontSizeSmall
                                , alignTop
                                ]
                              <|
                                newTabLink
                                    []
                                    { url = "#" ++ headingBlock.anchorId
                                    , label =
                                        el
                                            [ F.color colourTheme.textLightOrange
                                            , transitionStyleFast
                                            , hoverFontDarkOrange
                                            ]
                                        <|
                                            text (toTitleCase headingBlock.name)
                                    }
                            ]
                    )
            )
        , el
            [ width fill
            , height fill
            , spacing 20
            , paddingEach
                { top = 0
                , bottom = 0
                , left = 100
                , right = 100
                }
            ]
          <|
            divider
        , el
            []
          <|
            text ""
        ]


buildToc : List Block -> TableOfContents
buildToc blocks =
    let
        headings =
            gatherHeadings blocks
    in
    headings
        |> List.map Tuple.second
        |> List.map
            (\styledList ->
                { anchorId = styledList |> Block.extractInlineText |> rawTextToId
                , name = styledToString styledList
                , level = 1
                }
            )


styledToString : List Inline -> String
styledToString list =
    list
        |> Block.extractInlineText


gatherHeadings : List Block -> List ( Block.HeadingLevel, List Inline )
gatherHeadings blocks =
    List.filterMap
        (\block ->
            case block of
                Block.Heading level content ->
                    Just ( level, content )

                _ ->
                    Nothing
        )
        blocks


elmUiRenderer : Markdown.Renderer.Renderer (Element msg)
elmUiRenderer =
    { heading = heading
    , paragraph =
        E.paragraph
            [ E.spacing 3
            , paragraphFontSize
            , F.alignLeft
            , width fill
            ]
    , thematicBreak = E.none
    , text = E.text
    , strong =
        \content ->
            E.row
                [ F.bold
                ]
                content
    , emphasis =
        \content ->
            E.row
                [ F.italic
                ]
                content
    , strikethrough =
        \content ->
            E.row
                [ F.strike
                ]
                content
    , codeSpan = code
    , link =
        \{ title, destination } body ->
            E.newTabLink
                [ width fill ]
                { url = destination
                , label =
                    E.paragraph
                        [ F.color colourTheme.textLightOrange
                        , paragraphFontSize
                        , transitionStyleFast
                        , hoverFontDarkOrange
                        ]
                        body
                }
    , hardLineBreak = Html.br [] [] |> E.html
    , image =
        \image ->
            case image.title of
                Just title ->
                    el
                        [ width fill ]
                    <|
                        column
                            [ E.centerX
                            , E.centerY
                            , width fill
                            , E.paddingEach
                                { top = 0
                                , bottom = 10
                                , left = 10
                                , right = 10
                                }
                            ]
                            [ E.image
                                [ E.centerX
                                , width (fill |> maximum 600)
                                ]
                                { src = image.src
                                , description = image.alt
                                }
                            ]

                Nothing ->
                    el
                        [ width fill ]
                    <|
                        column
                            [ E.centerX
                            , E.centerY
                            , width fill
                            , E.paddingEach
                                { top = 3
                                , bottom = 10
                                , left = 10
                                , right = 10
                                }
                            ]
                            [ E.image
                                [ E.centerX
                                , width (fill |> maximum 600)
                                ]
                                { src = image.src
                                , description = image.alt
                                }
                            ]
    , blockQuote =
        \children ->
            E.column
                [ D.widthEach
                    { top = 0
                    , right = 0
                    , bottom = 0
                    , left = 5
                    }
                , D.rounded 10
                , E.paddingEach
                    { top = 13
                    , bottom = 10
                    , left = 10
                    , right = 10
                    }
                , D.color colourTheme.textLightOrange
                , B.color colourTheme.backgroundLightGrey
                , paragraphFontSize
                , width fill
                , spacing 10
                ]
                children
    , unorderedList =
        \items ->
            E.column
                [ E.spacing 10
                , paragraphFontSize
                ]
                (items
                    |> List.map
                        (\(ListItem task children) ->
                            E.row
                                [ E.width fill
                                ]
                                [ E.el
                                    [ E.width <| E.px 20
                                    , E.alignTop
                                    , alignRight
                                    ]
                                    (case task of
                                        IncompleteTask ->
                                            Input.defaultCheckbox False

                                        CompletedTask ->
                                            Input.defaultCheckbox True

                                        NoTask ->
                                            E.text "•"
                                    )
                                , E.column
                                    [ E.width fill, F.alignLeft ]
                                    children
                                ]
                        )
                )
    , orderedList =
        \startingIndex items ->
            E.column
                [ E.spacing 10
                , E.width fill
                ]
                (items
                    |> List.indexedMap
                        (\index itemBlocks ->
                            E.row
                                [ E.width fill
                                ]
                                [ E.el
                                    [ E.alignTop
                                    , width <| px 25
                                    , F.bold
                                    , alignRight
                                    , headerFontSizeMedium
                                    ]
                                    (E.text (String.fromInt (index + startingIndex) ++ "."))
                                , E.column
                                    [ alignLeft
                                    , paragraphFontSize
                                    , E.width fill
                                    ]
                                    itemBlocks
                                ]
                        )
                )
    , codeBlock = codeBlock
    , html = Markdown.Html.oneOf []
    , table = E.column [ width fill ]
    , tableHeader = E.column [ width fill ]
    , tableBody = E.column [ width fill ]
    , tableRow = E.row [ width fill ]
    , tableHeaderCell =
        \maybeAlignment children ->
            E.paragraph [ width fill ] children
    , tableCell =
        \maybeAlignment children ->
            E.paragraph [ width fill ] children
    }


code : String -> Element msg
code snippet =
    E.el
        [ B.color colourTheme.backgroundLightGrey
        , D.rounded 2
        , E.paddingXY 5 3
        , width fill
        , F.family
            [ F.external
                { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro"
                , name = "Source Code Pro"
                }
            ]
        ]
        (E.text snippet)


codeBlock : { body : String, language : Maybe String } -> Element msg
codeBlock details =
    E.el
        [ B.color colourTheme.backgroundLightGrey
        , E.htmlAttribute (Html.Attributes.style "white-space" "pre")
        , width fill
        , E.paddingEach
            { top = 23
            , bottom = 20
            , left = 20
            , right = 20
            }
        , paragraphFontSize
        , F.family
            [ F.external
                { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro"
                , name = "Source Code Pro"
                }
            ]
        ]
        (E.text details.body)


heading : { level : Block.HeadingLevel, rawText : String, children : List (Element msg) } -> Element msg
heading { level, rawText, children } =
    column [ width fill, spacing 20 ]
        [ case level of
            Block.H1 ->
                divider

            _ ->
                none
        , E.paragraph
            [ case level of
                Block.H1 ->
                    headerFontSizeBig

                Block.H2 ->
                    headerFontSizeMedium

                _ ->
                    headerFontSizeSmall
            , F.bold
            , case level of
                Block.H1 ->
                    F.center

                _ ->
                    F.alignLeft
            , width fill
            , F.color colourTheme.textLightOrange
            , Region.heading (Block.headingLevelToInt level)
            , E.htmlAttribute
                (Html.Attributes.attribute "name" (rawTextToId rawText))
            , E.htmlAttribute
                (Html.Attributes.id (rawTextToId rawText))
            ]
            children
        ]


rawTextToId rawText =
    rawText
        |> String.split " "
        -- |> Debug.log "split"
        |> String.join "-"
        -- |> Debug.log "joined"
        |> String.toLower



--  Types


type alias TableOfContents =
    List { anchorId : String, name : String, level : Int }
