<commit_before>/*
 * Copyright 2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import elmish.Component
import elmish.View
import elmish.a
import elmish.attributes
import elmish.code
import elmish.div
import elmish.img
import elmish.empty
import elmish.h1
import elmish.ol
import elmish.pre
import elmish.small
import elmish.span
import elmish.tree.Tree
import elmish.tree.TreeView
import elmish.tree.viewSubTrees

import kotlin.browser.window


internal
sealed class ProblemNode {

    data class Error(val label: ProblemNode, val docLink: ProblemNode?) : ProblemNode()

    data class Warning(val label: ProblemNode, val docLink: ProblemNode?) : ProblemNode()

    data class Task(val path: String, val type: String) : ProblemNode()

    data class Bean(val type: String) : ProblemNode()

    data class Property(val kind: String, val name: String, val owner: String) : ProblemNode()

    data class Label(val text: String) : ProblemNode()

    data class Link(val href: String, val label: String) : ProblemNode()

    data class Message(val prettyText: PrettyText) : ProblemNode()

    data class Exception(val stackTrace: String) : ProblemNode()
}


internal
data class PrettyText(val fragments: List<Fragment>) {

    sealed class Fragment {

        data class Text(val text: String) : Fragment()

        data class Reference(val name: String) : Fragment()
    }
}


internal
typealias ProblemTreeModel = TreeView.Model<ProblemNode>


internal
typealias ProblemTreeIntent = TreeView.Intent<ProblemNode>


internal
object InstantExecutionReportPage : Component<InstantExecutionReportPage.Model, InstantExecutionReportPage.Intent> {

    data class Model(
        val cacheAction: String,
        val documentationLink: String,
        val totalProblems: Int,
        val messageTree: ProblemTreeModel,
        val taskTree: ProblemTreeModel,
        val displayFilter: DisplayFilter = DisplayFilter.All
    )

    enum class DisplayFilter {
        All, Errors, Warnings
    }

    sealed class Intent {

        data class TaskTreeIntent(val delegate: ProblemTreeIntent) : Intent()

        data class MessageTreeIntent(val delegate: ProblemTreeIntent) : Intent()

        data class Copy(val text: String) : Intent()

        data class SetFilter(val displayFilter: DisplayFilter) : Intent()
    }

    override fun step(intent: Intent, model: Model): Model = when (intent) {
        is Intent.TaskTreeIntent -> model.copy(
            taskTree = TreeView.step(intent.delegate, model.taskTree)
        )
        is Intent.MessageTreeIntent -> model.copy(
            messageTree = TreeView.step(intent.delegate, model.messageTree)
        )
        is Intent.Copy -> {
            window.navigator.clipboard.writeText(intent.text)
            model
        }
        is Intent.SetFilter -> model.copy(
            displayFilter = intent.displayFilter
        )
    }

    override fun view(model: Model): View<Intent> = div(
        attributes { className("report-wrapper") },
        div(
            attributes { className("header") },
            img(attributes { src("configuration-cache-report-logo.png") }),
            learnMore(model.documentationLink)
        ),
        div(
            attributes { className("title") },
            h1("${model.totalProblems} problems were found ${model.cacheAction} the configuration cache"),
            div(
                attributes { className("filters") },
                div(
                    span("View"),
                    div(
                        attributes { className("filters-group") },
                        displayFilterButton(DisplayFilter.All, model.displayFilter),
                        displayFilterButton(DisplayFilter.Errors, model.displayFilter),
                        displayFilterButton(DisplayFilter.Warnings, model.displayFilter)
                    )
                )
            )
        ),
        div(
            attributes { className("groups") },
            div(attributes { className("group-selector group-selector--active") }, "Problems grouped by message"),
            div(attributes { className("group-selector") }, "Problems grouped by task")
        ),
        div(
            attributes { className("content") },
            viewTree(model.messageTree, Intent::MessageTreeIntent, model.displayFilter),
            viewTree(model.taskTree, Intent::TaskTreeIntent, model.displayFilter)
        )
    )

    private
    fun displayFilterButton(displayFilter: DisplayFilter, activeFilter: DisplayFilter): View<Intent> = span(
        attributes {
            className("btn")
            if (displayFilter == activeFilter) {
                className("btn-active")
            }
            onClick { Intent.SetFilter(displayFilter) }
        },
        displayFilter.name
    )

    private
    fun learnMore(documentationLink: String): View<Intent> = div(
        attributes { className("learn-more") },
        span("Learn more about the "),
        a(
            attributes { href(documentationLink) },
            "Gradle Configuration Cache"
        ),
        span(".")
    )

    private
    fun viewTree(model: ProblemTreeModel, treeIntent: (ProblemTreeIntent) -> Intent, displayFilter: DisplayFilter): View<Intent> = div(
//        h2(model.tree.label.unsafeCast<ProblemNode.Label>().text), // TODO remove this once tab navigation is working
        ol(
            viewSubTrees(applyFilter(displayFilter, model)) { child ->
                when (val node = child.tree.label) {
                    is ProblemNode.Error -> {
                        viewLabel(treeIntent, child, node.label, node.docLink, errorIcon)
                    }
                    is ProblemNode.Warning -> {
                        viewLabel(treeIntent, child, node.label, node.docLink, warningIcon)
                    }
                    is ProblemNode.Exception -> {
                        viewException(treeIntent, child, node)
                    }
                    else -> {
                        viewLabel(treeIntent, child, node)
                    }
                }
            }
        )
    )

    private
    fun applyFilter(displayFilter: DisplayFilter, model: ProblemTreeModel): Sequence<Tree.Focus<ProblemNode>> {
        val children = model.tree.focus().children
        return when (displayFilter) {
            DisplayFilter.All -> children<commit_msg>Add interactivity to tabs<commit_after>/*
 * Copyright 2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import elmish.Component
import elmish.View
import elmish.a
import elmish.attributes
import elmish.code
import elmish.div
import elmish.img
import elmish.empty
import elmish.h1
import elmish.ol
import elmish.pre
import elmish.small
import elmish.span
import elmish.tree.Tree
import elmish.tree.TreeView
import elmish.tree.viewSubTrees

import kotlin.browser.window


internal
sealed class ProblemNode {

    data class Error(val label: ProblemNode, val docLink: ProblemNode?) : ProblemNode()

    data class Warning(val label: ProblemNode, val docLink: ProblemNode?) : ProblemNode()

    data class Task(val path: String, val type: String) : ProblemNode()

    data class Bean(val type: String) : ProblemNode()

    data class Property(val kind: String, val name: String, val owner: String) : ProblemNode()

    data class Label(val text: String) : ProblemNode()

    data class Link(val href: String, val label: String) : ProblemNode()

    data class Message(val prettyText: PrettyText) : ProblemNode()

    data class Exception(val stackTrace: String) : ProblemNode()
}


internal
data class PrettyText(val fragments: List<Fragment>) {

    sealed class Fragment {

        data class Text(val text: String) : Fragment()

        data class Reference(val name: String) : Fragment()
    }
}


internal
typealias ProblemTreeModel = TreeView.Model<ProblemNode>


internal
typealias ProblemTreeIntent = TreeView.Intent<ProblemNode>


internal
object InstantExecutionReportPage : Component<InstantExecutionReportPage.Model, InstantExecutionReportPage.Intent> {

    data class Model(
        val cacheAction: String,
        val documentationLink: String,
        val totalProblems: Int,
        val messageTree: ProblemTreeModel,
        val taskTree: ProblemTreeModel,
        val displayFilter: DisplayFilter = DisplayFilter.All,
        val tab: Tab = Tab.ByMessage
    )

    enum class DisplayFilter {
        All, Errors, Warnings
    }

    enum class Tab(val text: String) {
        ByMessage("Problems grouped by message"),
        ByTask("Problems grouped by tasks")
    }

    sealed class Intent {

        data class TaskTreeIntent(val delegate: ProblemTreeIntent) : Intent()

        data class MessageTreeIntent(val delegate: ProblemTreeIntent) : Intent()

        data class Copy(val text: String) : Intent()

        data class SetFilter(val displayFilter: DisplayFilter) : Intent()

        data class SetTab(val tab: Tab) : Intent()
    }

    override fun step(intent: Intent, model: Model): Model = when (intent) {
        is Intent.TaskTreeIntent -> model.copy(
            taskTree = TreeView.step(intent.delegate, model.taskTree)
        )
        is Intent.MessageTreeIntent -> model.copy(
            messageTree = TreeView.step(intent.delegate, model.messageTree)
        )
        is Intent.Copy -> {
            window.navigator.clipboard.writeText(intent.text)
            model
        }
        is Intent.SetFilter -> model.copy(
            displayFilter = intent.displayFilter
        )
        is Intent.SetTab -> model.copy(
            tab = intent.tab
        )
    }

    override fun view(model: Model): View<Intent> = div(
        attributes { className("report-wrapper") },
        div(
            attributes { className("header") },
            img(attributes { src("configuration-cache-report-logo.png") }),
            learnMore(model.documentationLink)
        ),
        div(
            attributes { className("title") },
            h1("${model.totalProblems} problems were found ${model.cacheAction} the configuration cache"),
            div(
                attributes { className("filters") },
                div(
                    span("View"),
                    div(
                        attributes { className("filters-group") },
                        displayFilterButton(DisplayFilter.All, model.displayFilter),
                        displayFilterButton(DisplayFilter.Errors, model.displayFilter),
                        displayFilterButton(DisplayFilter.Warnings, model.displayFilter)
                    )
                )
            )
        ),
        div(
            attributes { className("groups") },
            displayTabButton(Tab.ByMessage, model.tab),
            displayTabButton(Tab.ByTask, model.tab)
        ),
        div(
            attributes { className("content") },
            when (model.tab) {
                Tab.ByMessage -> viewTree(model.messageTree, Intent::MessageTreeIntent, model.displayFilter)
                Tab.ByTask -> viewTree(model.taskTree, Intent::TaskTreeIntent, model.displayFilter)
            }
        )
    )

    private
    fun displayTabButton(tab: Tab, activeTab: Tab): View<Intent> = div(
        attributes {
            className("group-selector")
            if (tab == activeTab) {
                className("group-selector--active")
            } else {
                onClick { Intent.SetTab(tab) }
            }
        },
        tab.text
    )

    private
    fun displayFilterButton(displayFilter: DisplayFilter, activeFilter: DisplayFilter): View<Intent> = span(
        attributes {
            className("btn")
            if (displayFilter == activeFilter) {
                className("btn-active")
            }
            onClick { Intent.SetFilter(displayFilter) }
        },
        displayFilter.name
    )

    private
    fun learnMore(documentationLink: String): View<Intent> = div(
        attributes { className("learn-more") },
        span("Learn more about the "),
        a(
            attributes { href(documentationLink) },
            "Gradle Configuration Cache"
        ),
        span(".")
    )

    private
    fun viewTree(model: ProblemTreeModel, treeIntent: (ProblemTreeIntent) -> Intent, displayFilter: DisplayFilter): View<Intent> = div(
        ol(
            viewSubTrees(applyFilter(displayFilter, model)) { child ->
                when (val node = child.tree.label) {
                    is ProblemNode.Error -> {
                        viewLabel(treeIntent, child, node.label, node.docLink, errorIcon)
                    }
                    is ProblemNode.Warning -> {
                        viewLabel(treeIntent, child, node.label, node.docLink, warningIcon)
                    }
                    is ProblemNode.Exception -> {
                        viewException(treeIntent, child, node)
                    }
                    else -> {
                        viewLabel(treeIntent, child, node)
                    }
                }
            }
        )
    )

    private
    fun applyFilter(displayFilter: DisplayFilter, model: ProblemTreeModel): Sequence<Tree.Focus<ProblemNode>> {
        val children = model.tree.focus().children
        return when (displayFilter) {
            DisplayFilter.All -> children
{'fraction_non_alphanumeric': 0.09170426935091983, 'fraction_numerical': 0.0014578271433530025, 'mean_word_length': 2.230044843049327, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 50, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1894545', 'n_tokens_mistral': 3705, 'n_tokens_neox': 3537, 'n_words': 996}