{-# LANGUAGE OverloadedStrings #-}
module Reporting.Exit
  ( Init(..), initToReport
  , Diff(..), diffToReport
  , Make(..), makeToReport
  , Bump(..), bumpToReport
  , Repl(..), replToReport
  , Publish(..), publishToReport
  , Install(..), installToReport
  , Reactor(..), reactorToReport
  , newPackageOverview
  --
  , Solver(..)
  , Outline(..)
  , OutlineProblem(..)
  , Details(..)
  , DetailsBadDep(..)
  , PackageProblem(..)
  , RegistryProblem(..)
  , BuildProblem(..)
  , BuildProjectProblem(..)
  , DocsProblem(..)
  , Generate(..)
  --
  , toString
  , toStderr
  , toJson
  )
  where


import qualified Data.ByteString as BS
import qualified Data.ByteString.UTF8 as BS_UTF8
import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.Name as N
import qualified Data.NonEmptyList as NE
import qualified Network.HTTP.Client as HTTP
import qualified Network.HTTP.Types.Header as HTTP
import qualified Network.HTTP.Types.Status as HTTP
import qualified System.FilePath as FP
import System.FilePath ((</>), (<.>))

import qualified Elm.Constraint as C
import qualified Elm.Magnitude as M
import qualified Elm.ModuleName as ModuleName
import qualified Elm.Package as Pkg
import qualified Elm.Version as V
import qualified File
import qualified Http
import qualified Json.Decode as Decode
import qualified Json.Encode as Encode
import qualified Json.String as Json
import Parse.Primitives (Row, Col)
import qualified Reporting.Annotation as A
import Reporting.Doc ((<>))
import qualified Reporting.Doc as D
import qualified Reporting.Error.Import as Import
import qualified Reporting.Error.Json as Json
import qualified Reporting.Exit.Help as Help
import qualified Reporting.Error as Error
import qualified Reporting.Render.Code as Code



-- RENDERERS


toString :: Help.Report -> String
toString report =
  Help.toString (Help.reportToDoc report)


toStderr :: Help.Report -> IO ()
toStderr report =
  Help.toStderr (Help.reportToDoc report)


toJson :: Help.Report -> Encode.Value
toJson report =
  Help.reportToJson report



-- INIT


data Init
  = InitNoSolution [Pkg.Name]
  | InitNoOfflineSolution [Pkg.Name]
  | InitSolverProblem Solver
  | InitAlreadyExists
  | InitRegistryProblem RegistryProblem


initToReport :: Init -> Help.Report
initToReport exit =
  case exit of
    InitNoSolution pkgs ->
      Help.report "NO SOLUTION" Nothing
        "I tried to create an elm.json with the following direct dependencies:"
        [ D.indent 4 $ D.vcat $
            map (D.dullyellow . D.fromChars . Pkg.toChars) pkgs
        , D.reflow $
            "I could not find compatible versions though! This should not happen, so please\
            \ ask around one of the community forums at https://elm-lang.org/community to learn\
            \ what is going on!"
        ]

    InitNoOfflineSolution pkgs ->
      Help.report "NO OFFLINE SOLUTION" Nothing
        "I tried to create an elm.json with the following direct dependencies:"
        [ D.indent 4 $ D.vcat $
            map (D.dullyellow . D.fromChars . Pkg.toChars) pkgs
        , D.reflow $
            "I could not find compatible versions though, but that may be because I could not\
            \ connect to https://package.elm-lang.org to get the latest list of packages. Are\
            \ you able to connect to the internet? Please ask around one of the community\
            \ forums at https://elm-lang.org/community for help!"
        ]

    InitSolverProblem solver ->
      toSolverReport solver

    InitAlreadyExists ->
      Help.report "EXISTING PROJECT" Nothing
        "You already have an elm.json file, so there is nothing for me to initialize!"
        [ D.fillSep
            ["Maybe",D.green (D.fromChars (D.makeLink "init")),"can","help"
            ,"you","figure","out","what","to","do","next?"
            ]
        ]

    InitRegistryProblem problem ->
      toRegistryProblemReport "PROBLEM LOADING PACKAGE LIST" problem $
        "I need the list of published packages before I can start initializing projects"



-- DIFF


data Diff
  = DiffNoOutline
  | DiffBadOutline Outline
  | DiffApplication
  | DiffNoExposed
  | DiffUnpublished
  | DiffUnknownPackage Pkg.Name [Pkg.Name]
  | DiffUnknownVersion Pkg.Name V.Version [V.Version]
  | DiffDocsProblem V.Version DocsProblem
  | DiffMustHaveLatestRegistry RegistryProblem
  | DiffBadDetails Details
  | DiffBadBuild BuildProblem


diffToReport :: Diff -> Help.Report
diffToReport diff =
  case diff of
    DiffNoOutline ->
      Help.report "DIFF WHAT?" Nothing
        "I cannot find an elm.json so I am not sure what you want me to diff.\
        \ Normally you run `elm diff` from within a project!"
        [ D.reflow $ "If you are just curious to see a diff, try running this command:"
        , D.indent 4 $ D.green $ "elm diff elm/http 1.0.0 2.0.0"
        ]

    DiffBadOutline outline ->
      toOutlineReport outline

    DiffApplication ->
      Help.report "CANNOT DIFF APPLICATIONS" (Just "elm.json")
        "Your elm.json says this project is an application, but `elm diff` only works\
        \ with packages. That way there are previously published versions of the API to\
        \ diff against!"
        [ D.reflow $ "If you are just curious to see a diff, try running this command:"
        , D.indent 4 $ D.dullyellow $ "elm diff elm/json 1.0.0 1.1.2"
        ]

    DiffNoExposed ->
      Help.report "NO EXPOSED MODULES" (Just "elm.json")
        "Your elm.json has no \"exposed-modules\" which means there is no public API at\
        \ all right now! What am I supposed to diff?"
        [ D.reflow $
            "Try adding some modules back to the \"exposed-modules\" field."
        ]

    DiffUnpublished ->
      Help.report "UNPUBLISHED" Nothing
        "This package is not published yet. There is nothing to diff against!"
        []

    DiffUnknownPackage pkg suggestions ->
      Help.report "UNKNOWN PACKAGE" Nothing
        ( "I cannot find a package called:"
        )
        [ D.indent 4 $ D.red $ D.fromChars $ Pkg.toChars pkg
        , "Maybe you want one of these instead?"
        , D.indent 4 $ D.dullyellow $ D.vcat $ map (D.fromChars . Pkg.toChars) suggestions
        , "But check <https://package.elm-lang.org> to see all possibilities!"
        ]

    DiffUnknownVersion _pkg vsn realVersions ->
      Help.docReport "UNKNOWN VERSION" Nothing
        ( D.fillSep $
            [ "Version", D.red (D.fromVersion vsn)
            , "has", "never", "been", "published,", "so", "I"
            , "cannot", "diff", "against", "it."
            ]
        )
        [ "Here are all the versions that HAVE been published:"
        , D.indent 4 $ D.dullyellow $ D.vcat $
            let
              sameMajor v1 v2 = V._major v1 == V._major v2
              mkRow vsns = D.hsep $ map D.fromVersion vsns
            in
              map mkRow $ List.groupBy sameMajor (List.sort realVersions)
        , "Want one of those instead?"
        ]

    DiffDocsProblem version problem ->
      toDocsProblemReport problem $
        "I need the docs for " ++ V.toChars version ++ " to compute this diff"

    DiffMustHaveLatestRegistry problem ->
      toRegistryProblemReport "PROBLEM UPDATING PACKAGE LIST" problem $
        "I need the latest list of published packages before I do this diff"

    DiffBadDetails details ->
      toDetailsReport details

    DiffBadBuild buildProblem ->
      toBuildProblemReport buildProblem



-- BUMP


data Bump
  = BumpNoOutline
  | BumpBadOutline Outline
  | BumpApplication
  | BumpUnexpectedVersion V.Version [V.Version]
  | BumpMustHaveLatestRegistry RegistryProblem
  | BumpCannotFindDocs Pkg.Name V.Version DocsProblem
  | BumpBadDetails Details
  | BumpNoExposed
  | BumpBadBuild BuildProblem


bumpToReport :: Bump -> Help.Report
bumpToReport bump =
  case bump of
    BumpNoOutline ->
      Help.report "BUMP WHAT?" Nothing
        "I cannot find an elm.json so I am not sure what you want me to bump."
        [ D.reflow $
            "Elm packages always have an elm.json that says current the version number. If\
            \ you run this command from a directory with an elm.json file, I will try to bump\
            \ the version in there based on the API changes."
        ]

    BumpBadOutline outline ->
      toOutlineReport outline

    BumpApplication ->
      Help.report "CANNOT BUMP APPLICATIONS" (Just "elm.json")
        "Your elm.json says this is an application. That means it cannot be published\
        \ on <https://package.elm-lang.org> and therefore has no version to bump!"
        []

    BumpUnexpectedVersion vsn versions ->
      Help.docReport "CANNOT BUMP" (Just "elm.json")
        ( D.fillSep
            ["Your","elm.json","says","I","should","bump","relative","to","version"
            ,D.red (D.fromVersion vsn) <> ","
            ,"but","I","cannot","find","that","version","on","<https://package.elm-lang.org>."
            ,"That","means","there","is","no","API","for","me","to","diff","against","and"
            ,"figure","out","if","these","are","MAJOR,","MINOR,","or","PATCH","changes."
            ]
        )
        [ D.fillSep $
            ["Try","bumping","again","after","changing","the",D.dullyellow "\"version\"","in","elm.json"]
            ++ if length versions == 1 then ["to:"] else ["to","one","of","these:"]
        , D.vcat $ map (D.green . D.fromVersion) versions
        ]

    BumpMustHaveLatestRegistry problem ->
      toRegistryProblemReport "PROBLEM UPDATING PACKAGE LIST" problem $
        "I need the latest list of published packages before I can bump any versions"

    BumpCannotFindDocs _ version problem ->
      toDocsProblemReport problem $
        "I need the docs for " ++ V.toChars version ++ " to compute the next version number"

    BumpBadDetails details ->
      toDetailsReport details

    BumpNoExposed ->
      Help.docReport "NO EXPOSED MODULES" (Just "elm.json")
        ( D.fillSep $
            [ "To", "bump", "a", "package,", "the"
            , D.dullyellow "\"exposed-modules\"", "field", "of", "your"
            , "elm.json", "must", "list", "at", "least", "one", "module."
            ]
        )
        [ D.reflow $
            "Try adding some modules back to the \"exposed-modules\" field."
        ]

    BumpBadBuild problem ->
      toBuildProblemReport problem



-- OVERVIEW OF VERSIONING


newPackageOverview :: String
newPackageOverview =
  unlines
    [ "This package has never been published before. Here's how things work:"
    , ""
    , "  - Versions all have exactly three parts: MAJOR.MINOR.PATCH"
    , ""
    , "  - All packages start with initial version " ++ V.toChars V.one
    , ""
    , "  - Versions are incremented based on how the API changes:"
    , ""
    , "        PATCH = the API is the same, no risk of breaking code"
    , "        MINOR = values have been added, existing values are unchanged"
    , "        MAJOR = existing values have been changed or removed"
    , ""
    , "  - I will bump versions for you, automatically enforcing these rules"
    , ""
    ]



-- PUBLISH


data Publish
  = PublishNoOutline
  | PublishBadOutline Outline
  | PublishBadDetails Details
  | PublishMustHaveLatestRegistry RegistryProblem
  | PublishApplication
  | PublishNotInitialVersion V.Version
  | PublishAlreadyPublished V.Version
  | PublishInvalidBump V.Version V.Version
  | PublishBadBump V.Version V.Version M.Magnitude V.Version M.Magnitude
  | PublishNoSummary
  | PublishNoExposed
  | PublishNoReadme
  | PublishShortReadme
  | PublishNoLicense
  | PublishBuildProblem BuildProblem
  | PublishMissingTag V.Version
  | PublishCannotGetTag V.Version Http.Error
  | PublishCannotGetTagData V.Version String BS.ByteString
  | PublishCannotGetZip Http.Error
  | PublishCannotDecodeZip String
  | PublishCannotGetDocs V.Version V.Version DocsProblem
  | PublishCannotRegister Http.Error
  | PublishNoGit
  | PublishLocalChanges V.Version
  --
  | PublishZipBadDetails Details
  | PublishZipApplication
  | PublishZipNoExposed
  | PublishZipBuildProblem BuildProblem


publishToReport :: Publish -> Help.Report
publishToReport publish =
  case publish of
    PublishNoOutline ->
      Help.report "PUBLISH WHAT?" Nothing
        "I cannot find an elm.json so I am not sure what you want me to publish."
        [ D.reflow $
            "Elm packages always have an elm.json that states the version number,\
            \ dependencies, exposed modules, etc."
        ]

    PublishBadOutline outline ->
      toOutlineReport outline

    PublishBadDetails problem ->
      toDetailsReport problem

    PublishMustHaveLatestRegistry problem ->
      toRegistryProblemReport "PROBLEM UPDATING PACKAGE LIST" problem $
        "I need the latest list of published packages to make sure this is safe to publish"

    PublishApplication ->
      Help.report "UNPUBLISHABLE" Nothing "I cannot publish applications, only packages!" []

    PublishNotInitialVersion vsn ->
      Help.docReport "INVALID VERSION" Nothing
        ( D.fillSep
            ["I","cannot","publish"
            ,D.red (D.fromVersion vsn)
            ,"as","the","initial","version."
            ]
        )
        [ D.fillSep
            ["Change","it","to",D.green "1.0.0","which","is"
            ,"the","initial","version","for","all","Elm","packages."
            ]
        ]

    PublishAlreadyPublished vsn ->
      Help.docReport "ALREADY PUBLISHED" Nothing
        ( D.vcat
            [ D.fillSep
                [ "Version", D.green (D.fromVersion vsn)
                , "has", "already", "been", "published.", "You", "cannot"
                , "publish", "it", "again!"
                ]
            , "Try using the `bump` command:"
            ]
        )
        [ D.dullyellow $ D.indent 4 "elm bump"
        , D.reflow $
            "It computes the version number based on API changes, ensuring\
            \ that no breaking changes end up in PATCH releases!"
        ]

    PublishInvalidBump statedVersion latestVersion ->
      Help.docReport "INVALID VERSION" (Just "elm.json")
        ( D.fillSep $
            ["Your","elm.json","says","the","next","version","should","be"
            ,D.red (D.fromVersion statedVersion) <> ","
            ,"but","that","is","not","valid","based","on","the","previously"
            ,"published","versions."
            ]
        )
        [ D.fillSep $
            ["Change","the","version","back","to"
            ,D.green (D.fromVersion latestVersion)
            ,"which","is","the","most","recently","published","version."
            ,"From","there,","have","Elm","bump","the","version","by","running:"
            ]
        , D.indent 4 $ D.green "elm bump"
        , D.reflow $
            "If you want more insight on the API changes Elm detects, you\
            \ can run `elm diff` at this point as well."
        ]

    PublishBadBump old new magnitude realNew realMagnitude ->
      Help.docReport "INVALID VERSION" (Just "elm.json")
        (
          D.fillSep $
            ["Your","elm.json","says","the","next","version","should","be"
            ,D.red (D.fromVersion new) <> ","
            ,"indicating","a",D.fromChars (M.toChars magnitude)
            ,"change","to","the","public","API."
            ,"This","does","not","match","the","API","diff","given","by:"
            ]
        )
        [ D.indent 4 $ D.fromChars $
            "elm diff " ++ V.toChars old

        , D.fillSep $
            ["This","command","says","this","is","a"
            ,D.fromChars (M.toChars realMagnitude)
            ,"change,","so","the","next","version","should","be"
            ,D.green (D.fromVersion realNew) <> "."
            ,"Double","check","everything","to","make","sure","you"
            ,"are","publishing","what","you","want!"
            ]
        , D.reflow $
            "Also, next time use `elm bump` and I'll figure all this out for you!"
        ]

    PublishNoSummary ->
      Help.docReport "NO SUMMARY" (Just "elm.json")
        ( D.fillSep $
            [ "To", "publish", "a", "package,", "your", "elm.json", "must"
            , "have", "a", D.dullyellow "\"summary\"", "field", "that", "gives"
            , "a", "consice", "overview", "of", "your", "project."
            ]
        )
        [ D.reflow $
            "The summary must be less than 80 characters. It should describe\
            \ the concrete use of your package as clearly and as plainly as possible."
        ]

    PublishNoExposed ->
      Help.docReport "NO EXPOSED MODULES" (Just "elm.json")
        ( D.fillSep $
            [ "To", "publish", "a", "package,", "the"
            , D.dullyellow "\"exposed-modules\"", "field", "of", "your"
            , "elm.json", "must", "list", "at", "least", "one", "module."
            ]
        )
        [ D.reflow $
            "Which modules do you want users of the package to have access to? Add their\
            \ names to the \"exposed-modules\" list."
        ]

    PublishNoReadme ->
      toBadReadmeReport "NO README" $
        "Every published package must have a helpful README.md\
        \ file, but I do not see one in your project."

    PublishShortReadme ->
      toBadReadmeReport "SHORT README" $
        "This README.md is too short. Having more details will help\
        \ people assess your package quickly and fairly."

    PublishNoLicense ->
      Help.report "NO LICENSE FILE" (Just "LICENSE")
        "By publishing a package you are inviting the Elm community to build\
        \ upon your work. But without knowing your license, we have no idea if\
        \ that is legal!"
        [ D.reflow $
            "Once you pick an OSI approved license from <https://spdx.org/licenses/>,\
            \ you must share that choice in two places. First, the license\
            \ identifier must appear in your elm.json file. Second, the full\
            \ license text must appear in the root of your project in a file\
            \ named LICENSE. Add that file and you will be all set!"
        ]

    PublishBuildProblem buildProblem ->
      toBuildProblemReport buildProblem

    PublishMissingTag version ->
      let vsn = V.toChars version in
      Help.docReport "NO TAG" Nothing
        ( D.fillSep $
            [ "Packages", "must", "be", "tagged", "in", "git,", "but", "I"
            , "cannot", "find", "a", D.green (D.fromChars vsn), "tag."
            ]
        )
        [ D.vcat
            [ "These tags make it possible to find this specific version on GitHub."
            , "To tag the most recent commit and push it to GitHub, run this:"
            ]
        , D.indent 4 $ D.dullyellow $ D.vcat $ map D.fromChars $
            [ "git tag -a " ++ vsn ++ " -m \"new release\""
            , "git push origin " ++ vsn
            ]
        , "The -m flag is for a helpful message. Try to make it more informative!"
        ]

    PublishCannotGetTag version httpError ->
      case httpError of
        Http.BadHttp _ (HTTP.StatusCodeException response _)
          | HTTP.statusCode (HTTP.responseStatus response) == 404 ->
              let vsn = V.toChars version in
              Help.report "NO TAG ON GITHUB" Nothing
                ("You have version " ++ vsn ++ " tagged locally, but not on GitHub.")
                [ D.reflow
                    "Run the following command to make this tag available on GitHub:"
                , D.indent 4 $ D.dullyellow $ D.fromChars $
                    "git push origin " ++ vsn
                , D.reflow
                    "This will make it possible to find your code online based on the version number."
                ]

        _ ->
          toHttpErrorReport "PROBLEM VERIFYING TAG" httpError
            "I need to check that the version tag is registered on GitHub"

    PublishCannotGetTagData version url body ->
      Help.report "PROBLEM VERIFYING TAG" Nothing
        ("I need to check that version " ++ V.toChars version ++ " is tagged on GitHub, so I fetched:")
        [ D.indent 4 $ D.dullyellow $ D.fromChars url
        , D.reflow $
            "I got the data back, but it was not what I was expecting. The response\
            \ body contains " ++ show (BS.length body) ++ " bytes. Here is the "
            ++ if BS.length body <= 76 then "whole thing:" else "beginning:"
        , D.indent 4 $ D.dullyellow $ D.fromChars $
            if BS.length body <= 76
            then BS_UTF8.toString body
            else take 73 (BS_UTF8.toString body) ++ "..."
        , D.reflow $
            "Does this error keep showing up? Maybe there is something weird with your\
            \ internet connection. We have gotten reports that schools, businesses,\
            \ airports, etc. sometimes intercept requests and add things to the body\
            \ or change its contents entirely. Could that be the problem?"
        ]

    PublishCannotGetZip httpError ->
      toHttpErrorReport "PROBLEM DOWNLOADING CODE" httpError $
        "I need to check that folks can download and build the source code when they\
        \ install this package"

    PublishCannotDecodeZip url ->
      Help.report "PROBLEM DOWNLOADING CODE" Nothing
        "I need to check that folks can download and build the source code when they\
        \ install this package, so I downloaded the code from:"
        [ D.indent 4 $ D.dullyellow $ D.fromChars url
        , D.reflow $
            "I was unable to unzip the archive though. Maybe there is something weird with\
            \ your internet connection. We have gotten reports that schools, businesses,\
            \ airports, etc. sometimes intercept requests and add things to the body or\
            \ change its contents entirely. Could that be the problem?"
        ]

    PublishCannotGetDocs old new docsProblem ->
      toDocsProblemReport docsProblem $
        "I need the docs for " ++ V.toChars old ++ " to verify that "
        ++ V.toChars new ++ " really does come next"

    PublishCannotRegister httpError ->
      toHttpErrorReport "PROBLEM PUBLISHING PACKAGE" httpError $
        "I need to send information about your package to the package website"

    PublishNoGit ->
      Help.report "NO GIT" Nothing
        "I searched your PATH environment variable for `git` and could not\
        \ find it. Is it available through your PATH?"
        [ D.reflow $
            "Who cares about this? Well, I currently use `git` to check if there\
            \ are any local changes in your code. Local changes are a good sign\
            \ that some important improvements have gotten mistagged, so this\
            \ check can be extremely helpful for package authors!"
        , D.toSimpleNote $
            "We plan to do this without the `git` binary in a future release."
        ]

    PublishLocalChanges version ->
      let vsn = V.toChars version in
      Help.docReport "LOCAL CHANGES" Nothing
        ( D.fillSep $
            [ "The", "code", "tagged", "as", D.green (D.fromChars vsn), "in"
            , "git", "does", "not", "match", "the", "code", "in", "your"
            , "working", "directory.", "This", "means", "you", "have"
            , "commits", "or", "local", "changes", "that", "are", "not"
            , "going", "to", "be", "published!"
            ]
        )
        [ D.toSimpleNote $
            "If you are sure everything is in order, you can run `git checkout "
            ++ vsn ++ "` and publish your code from there."
        ]

    PublishZipBadDetails _ ->
      badZipReport

    PublishZipApplication ->
      badZipReport

    PublishZipNoExposed ->
      badZipReport

    PublishZipBuildProblem _ ->
      badZipReport


toBadReadmeReport :: String -> String -> Help.Report
toBadReadmeReport title summary =
  Help.report title (Just "README.md") summary
    [ D.reflow $
        "When people look at your README, they are wondering:"
    , D.vcat
        [ "  - What does this package even do?"
        , "  - Will it help me solve MY problems?"
        ]
    , D.reflow $
        "So I recommend starting your README with a small example of the\
        \ most common usage scenario. Show people what they can expect if\
        \ they learn more!"
    , D.toSimpleNote $
        "By publishing your package, you are inviting people to invest time in\
        \ understanding your work. Spending an hour on your README to communicate your\
        \ knowledge more clearly can save the community days or weeks of time in\
        \ aggregate, and saving time in aggregate is the whole point of publishing\
        \ packages! People really appreciate it, and it makes the whole ecosystem feel\
        \ nicer!"
    ]


badZipReport :: Help.Report
badZipReport =
  Help.report "PROBLEM VERIFYING PACKAGE" Nothing
    "Before publishing packages, I download the code from GitHub and try to build it\
    \ from scratch. That way I can be more confident that it will work for other\
    \ people too. But I am not able to build it!"
    [ D.reflow $
        "I was just able to build your local copy though. Is there some way the version\
        \ on GitHub could be different?"
    ]



-- DOCS


data DocsProblem
  = DP_Http Http.Error
  | DP_Data String BS.ByteString
  | DP_Cache


toDocsProblemReport :: DocsProblem -> String -> Help.Report
toDocsProblemReport problem context =
  case problem of
    DP_Http httpError ->
      toHttpErrorReport "PROBLEM LOADING DOCS" httpError context

    DP_Data url body ->
      Help.report "PROBLEM LOADING DOCS" Nothing (context ++ ", so I fetched:")
        [ D.indent 4 $ D.dullyellow $ D.fromChars url
        , D.reflow $
            "I got the data back, but it was not what I was expecting. The response\
            \ body contains " ++ show (BS.length body) ++ " bytes. Here is the "
            ++ if BS.length body <= 76 then "whole thing:" else "beginning:"
        , D.indent 4 $ D.dullyellow $ D.fromChars $
            if BS.length body <= 76
            then BS_UTF8.toString body
            else take 73 (BS_UTF8.toString body) ++ "..."
        , D.reflow $
            "Does this error keep showing up? Maybe there is something weird with your\
            \ internet connection. We have gotten reports that schools, businesses,\
            \ airports, etc. sometimes intercept requests and add things to the body\
            \ or change its contents entirely. Could that be the problem?"
        ]

    DP_Cache ->
      Help.report "PROBLEM LOADING DOCS" Nothing (context ++ ", but the local copy seems to be corrupted.")
        [ D.reflow $
            "I deleted the cached version, so the next run should download a fresh copy of\
            \ the docs. Hopefully that will get you unstuck, but it will not resolve the root\
            \ problem if, for example, a 3rd party editor plugin is modifing cached files\
            \ for some reason."
        ]



-- INSTALL


data Install
  = InstallNoOutline
  | InstallBadOutline Outline
  | InstallBadRegistry RegistryProblem
  | InstallNoArgs FilePath
  | InstallNoOnlineAppSolution Pkg.Name
  | InstallNoOfflineAppSolution Pkg.Name
  | InstallNoOnlinePkgSolution Pkg.Name
  | InstallNoOfflinePkgSolution Pkg.Name
  | InstallHadSolverTrouble Solver
  | InstallUnknownPackageOnline Pkg.Name [Pkg.Name]
  | InstallUnknownPackageOffline Pkg.Name [Pkg.Name]
  | InstallBadDetails Details


installToReport :: Install -> Help.Report
installToReport exit =
  case exit of
    InstallNoOutline ->
      Help.report "NEW PROJECT?" Nothing
        "Are you trying to start a new project? Try this command instead:"
        [ D.indent 4 $ D.green "elm init"
        , D.reflow "It will help you get started!"
        ]

    InstallBadOutline outline ->
      toOutlineReport outline

    InstallBadRegistry problem ->
      toRegistryProblemReport "PROBLEM LOADING PACKAGE LIST" problem $
        "I need the list of published packages to figure out how to install things"

    InstallNoArgs elmHome ->
      Help.report "INSTALL WHAT?" Nothing
        "I am expecting commands like:"
        [ D.green $ D.indent 4 $ D.vcat $
            [ "elm install elm/http"
            , "elm install elm/json"
            , "elm install elm/random"
            ]
        , D.toFancyHint
            ["In","JavaScript","folks","run","`npm install`","to","start","projects."
            ,"\"Gotta","download","everything!\"","But","why","download","packages"
            ,"again","and","again?","Instead,","Elm","caches","packages","in"
            ,D.dullyellow (D.fromChars elmHome)
            ,"so","each","one","is","downloaded","and","built","ONCE","on","your","machine."
            ,"Elm","projects","check","that","cache","before","trying","the","internet."
            ,"This","reduces","build","times,","reduces","server","costs,","and","makes","it"
            ,"easier","to","work","offline.","As","a","result"
            ,D.dullcyan "elm install","is","only","for","adding","dependencies","to","elm.json,"
            ,"whereas",D.dullcyan "elm make","is","in","charge","of","gathering","dependencies"
            ,"and","building","everything.","So","maybe","try",D.green "elm make","instead?"
            ]
        ]

    InstallNoOnlineAppSolution pkg ->
      Help.report "CANNOT FIND COMPATIBLE VERSION" (Just "elm.json")
        (
          "I cannot find a version of " ++ Pkg.toChars pkg ++ " that is compatible\
          \ with your existing dependencies."
        )
        [ D.reflow $
            "I checked all the published versions. When that failed, I tried to find any\
            \ compatible combination of these packages, even if it meant changing all your\
            \ existing dependencies! That did not work either!"
        , D.reflow $
            "This is most likely to happen when a package is not upgraded yet. Maybe a new\
            \ version of Elm came out recently? Maybe a common package was changed recently?\
            \ Maybe a better package came along, so there was no need to upgrade this one?\
            \ Try asking around https://elm-lang.org/community to learn what might be going on\
            \ with this package."
        , D.toSimpleNote $
            "Whatever the case, please be kind to the relevant package authors! Having\
            \ friendly interactions with users is great motivation, and conversely, getting\
            \ berated by strangers on the internet sucks your soul dry. Furthermore, package\
            \ authors are humans with families, friends, jobs, vacations, responsibilities,\
            \ goals, etc. They face obstacles outside of their technical work you will never\
            \ know about, so please assume the best and try to be patient and supportive!"
        ]

    InstallNoOfflineAppSolution pkg ->
      Help.report "CANNOT FIND COMPATIBLE VERSION LOCALLY" (Just "elm.json")
        (
          "I cannot find a version of " ++ Pkg.toChars pkg ++ " that is compatible\
          \ with your existing dependencies."
        )
        [ D.reflow $
            "I was not able to connect to https://package.elm-lang.org/ though, so I was only\
            \ able to look through packages that you have downloaded in the past."
        , D.reflow $
            "Try again later when you have internet!"
        ]

    InstallNoOnlinePkgSolution pkg ->
      Help.report "CANNOT FIND COMPATIBLE VERSION" (Just "elm.json")
        (
          "I cannot find a version of " ++ Pkg.toChars pkg ++ " that is compatible\
          \ with your existing constraints."
        )
        [ D.reflow $
            "With applications, I try to broaden the constraints to see if anything works,\
            \ but messing with package constraints is much more delicate business. E.g. making\
            \ your constraints stricter may make it harder for applications to find compatible\
            \ dependencies. So fixing something here may break it for a lot of other people!"
        , D.reflow $
            "So I recommend making an application with the same dependencies as your package.\
            \ See if there is a solution at all. From there it may be easier to figure out\
            \ how to proceed in a way that will disrupt your users as little as possible. And\
            \ the solution may be to help other package authors to get their packages updated,\
            \ or to drop a dependency entirely."
        ]

    InstallNoOfflinePkgSolution pkg ->
      Help.report "CANNOT FIND COMPATIBLE VERSION LOCALLY" (Just "elm.json")
        (
          "I cannot find a version of " ++ Pkg.toChars pkg ++ " that is compatible\
          \ with your existing constraints."
        )
        [ D.reflow $
            "I was not able to connect to https://package.elm-lang.org/ though, so I was only\
            \ able to look through packages that you have downloaded in the past."
        , D.reflow $
            "Try again later when you have internet!"
        ]

    InstallHadSolverTrouble solver ->
      toSolverReport solver

    InstallUnknownPackageOnline pkg suggestions ->
      Help.docReport "UNKNOWN PACKAGE" Nothing
        (
          D.fillSep
            ["I","cannot","find","a","package","named",D.red (D.fromPackage pkg) <> "."]
        )
        [ D.reflow $
            "I looked through https://package.elm-lang.org for packages with similar names\
            \ and found these:"
        , D.indent 4 $ D.dullyellow $ D.vcat $ map D.fromPackage suggestions
        , D.reflow $ "Maybe you want one of these instead?"
        ]

    InstallUnknownPackageOffline pkg suggestions ->
      Help.docReport "UNKNOWN PACKAGE" Nothing
        (
          D.fillSep
            ["I","cannot","find","a","package","named",D.red (D.fromPackage pkg) <> "."]
        )
        [ D.reflow $
            "I could not connect to https://package.elm-lang.org though, so new packages may\
            \ have been published since I last updated my local cache of package names."
        , D.reflow $
            "Looking through the locally cached names, the closest ones are:"
        , D.indent 4 $ D.dullyellow $ D.vcat $ map D.fromPackage suggestions
        , D.reflow $ "Maybe you want one of these instead?"
        ]

    InstallBadDetails details ->
      toDetailsReport details



-- SOLVER


data Solver
  = SolverBadCacheData Pkg.Name V.Version
  | SolverBadHttpData Pkg.Name V.Version String
  | SolverBadHttp Pkg.Name V.Version Http.Error


toSolverReport :: Solver -> Help.Report
toSolverReport problem =
  case problem of
    SolverBadCacheData pkg vsn ->
      Help.report "PROBLEM SOLVING PACKAGE CONSTRAINTS" Nothing
        (
          "I need the elm.json of " ++ Pkg.toChars pkg ++ " " ++ V.toChars vsn ++ " to\
          \ help me search for a set of compatible packages. I had it cached locally, but\
          \ it looks like the file was corrupted!"
        )
        [ D.reflow $
            "I deleted the cached version, so the next run should download a fresh copy.\
            \ Hopefully that will get you unstuck, but it will not resolve the root\
            \ problem if a 3rd party tool is modifing cached files for some reason."
        ]

    SolverBadHttpData pkg vsn url ->
      Help.report "PROBLEM SOLVING PACKAGE CONSTRAINTS" Nothing
        (
          "I need the elm.json of " ++ Pkg.toChars pkg ++ " " ++ V.toChars vsn ++ " to\
          \ help me search for a set of compatible packages, but I ran into corrupted\
          \ information from:"
        )
        [ D.indent 4 $ D.dullyellow $ D.fromChars url
        , D.reflow $
            "Is something weird with your internet connection. We have gotten reports that\
            \ schools, businesses, airports, etc. sometimes intercept requests and add things\
            \ to the body or change its contents entirely. Could that be the problem?"
        ]

    SolverBadHttp pkg vsn httpError ->
      toHttpErrorReport "PROBLEM SOLVING PACKAGE CONSTRAINTS" httpError $
        "I need the elm.json of " ++ Pkg.toChars pkg ++ " " ++ V.toChars vsn
        ++ " to help me search for a set of compatible packages"



-- OUTLINE


data Outline
  = OutlineHasBadStructure (Decode.Error OutlineProblem)
  | OutlineHasMissingSrcDirs FilePath [FilePath]
  | OutlineHasDuplicateSrcDirs FilePath FilePath FilePath
  | OutlineNoPkgCore
  | OutlineNoAppCore
  | OutlineNoAppJson


data OutlineProblem
  = OP_BadType
  | OP_BadPkgName Row Col
  | OP_BadVersion Row Col
  | OP_BadConstraint C.Error
  | OP_BadModuleName Row Col
  | OP_BadModuleHeaderTooLong
  | OP_BadDependencyName Row Col
  | OP_BadLicense Json.String [Json.String]
  | OP_BadSummaryTooLong
  | OP_NoSrcDirs


toOutlineReport :: Outline -> Help.Report
toOutlineReport problem =
  case problem of
    OutlineHasBadStructure decodeError ->
      Json.toReport "elm.json" (Json.FailureToReport toOutlineProblemReport) decodeError $
        Json.ExplicitReason "I ran into a problem with your elm.json file."

    OutlineHasMissingSrcDirs dir dirs ->
      case dirs of
        [] ->
          Help.report "MISSING SOURCE DIRECTORY" (Just "elm.json")
            "I need a valid elm.json file, but the \"source-directories\" field lists the following directory:"
            [ D.indent 4 $ D.red $ D.fromChars dir
            , D.reflow $
                "I cannot find it though. Is it missing? Is there a typo?"
            ]

        _:_ ->
          Help.report "MISSING SOURCE DIRECTORIES" (Just "elm.json")
            "I need a valid elm.json file, but the \"source-directories\" field lists the following directories:"
            [ D.indent 4 $ D.vcat $
                map (D.red . D.fromChars) (dir:dirs)
            , D.reflow $
                "I cannot find them though. Are they missing? Are there typos?"
            ]

    OutlineHasDuplicateSrcDirs canonicalDir dir1 dir2 ->
      if dir1 == dir2 then
        Help.report "REDUNDANT SOURCE DIRECTORIES" (Just "elm.json")
          "I need a valid elm.json file, but the \"source-directories\" field lists the same directory twice:"
          [ D.indent 4 $ D.vcat $
              map (D.red . D.fromChars) [dir1,dir2]
          , D.reflow $
              "Remove one of the entries!"
          ]
      else
        Help.report "REDUNDANT SOURCE DIRECTORIES" (Just "elm.json")
          "I need a valid elm.json file, but the \"source-directories\" field has some redundant directories:"
          [ D.indent 4 $ D.vcat $
              map (D.red . D.fromChars) [dir1,dir2]
          , D.reflow $
              "These are two different ways of refering to the same directory:"
          , D.indent 4 $ D.dullyellow $ D.fromChars canonicalDir
          , D.reflow $
              "Remove one of the redundant entries from your \"source-directories\" field."
          ]

    OutlineNoPkgCore ->
      Help.report "MISSING DEPENDENCY" (Just "elm.json")
        "I need to see an \"elm/core\" dependency your elm.json file. The default imports\
        \ of `List` and `Maybe` do not work without it."
        [ D.reflow $
            "If you modified your elm.json by hand, try to change it back! And if you are\
            \ having trouble getting back to a working elm.json, it may be easier to find a\
            \ working package and start fresh with their elm.json file."
        ]

    OutlineNoAppCore ->
      Help.report "MISSING DEPENDENCY" (Just "elm.json")
        "I need to see an \"elm/core\" dependency your elm.json file. The default imports\
        \ of `List` and `Maybe` do not work without it."
        [ D.reflow $
            "If you modified your elm.json by hand, try to change it back! And if you are\
            \ having trouble getting back to a working elm.json, it may be easier to delete it\
            \ and use `elm init` to start fresh."
        ]

    OutlineNoAppJson ->
      Help.report "MISSING DEPENDENCY" (Just "elm.json")
        "I need to see an \"elm/json\" dependency your elm.json file. It helps me handle\
        \ flags and ports."
        [ D.reflow $
            "If you modified your elm.json by hand, try to change it back! And if you are\
            \ having trouble getting back to a working elm.json, it may be easier to delete it\
            \ and use `elm init` to start fresh."
        ]


toOutlineProblemReport :: FilePath -> Code.Source -> Json.Context -> A.Region -> OutlineProblem -> Help.Report
toOutlineProblemReport path source _ region problem =
  let
    toHighlight row col =
      Just $ A.Region (A.Position row col) (A.Position row col)

    toSnippet title highlight pair =
      Help.jsonReport title (Just path) $
        Code.toSnippet source region highlight pair
  in
  case problem of
    OP_BadType ->
      toSnippet "UNEXPECTED TYPE" Nothing
        ( D.reflow $
            "I got stuck while reading your elm.json file. I cannot handle a \"type\" like this:"
        , D.fillSep
            ["Try","changing","the","\"type\"","to"
            ,D.green "\"application\"","or",D.green "\"package\"","instead."
            ]
        )

    OP_BadPkgName row col ->
      toSnippet "INVALID PACKAGE NAME" (toHighlight row col)
        ( D.reflow $
            "I got stuck while reading your elm.json file. I ran into trouble with the package name:"
        , D.stack
            [ D.fillSep
                ["Package","names","are","always","written","as"
                ,D.green "\"author/project\""
                ,"so","I","am","expecting","to","see","something","like:"
                ]
            , D.dullyellow $ D.indent 4 $ D.vcat $
                [ "\"mdgriffith/elm-ui\""
                , "\"w0rm/elm-physics\""
                , "\"Microsoft/elm-json-tree-view\""
                , "\"FordLabs/elm-star-rating\""
                , "\"1602/json-schema\""
                ]
            , D.reflow
                "The author name should match your GitHub name exactly, and the project name\
                \ needs to follow these rules:"
            , D.indent 4 $ D.vcat $
                [ "+--------------------------------------+-----------+-----------+"
                , "| RULE                                 | BAD       | GOOD      |"
                , "+--------------------------------------+-----------+-----------+"
                , "| only lower case, digits, and hyphens | elm-HTTP  | elm-http  |"
                , "| no leading digits                    | 3D        | elm-3d    |"
                , "| no non-ASCII characters              | elm-bjørn | elm-bear  |"
                , "| no underscores                       | elm_ui    | elm-ui    |"
                , "| no double hyphens                    | elm--hash | elm-hash  |"
                , "| no starting or ending hyphen         | -elm-tar- | elm-tar   |"
                , "+--------------------------------------+-----------+-----------+"
                ]
            , D.toSimpleNote $
                "These rules only apply to the project name, so you should never need\
                \ to change your GitHub name!"
            ]
        )

    OP_BadVersion row col ->
      toSnippet "PROBLEM WITH VERSION" (toHighlight row col)
        ( D.reflow $
            "I got stuck while reading your elm.json file. I was expecting a version number here:"
        , D.fillSep
            ["I","need","something","like",D.green "\"1.0.0\"","or",D.green "\"2.0.4\""
            ,"that","explicitly","states","all","three","numbers!"
            ]
        )

    OP_BadConstraint constraintError ->
      case constraintError of
        C.BadFormat row col ->
          toSnippet "PROBLEM WITH CONSTRAINT" (toHighlight row col)
            ( D.reflow $
                "I got stuck while reading your elm.json file. I do not understand this version constraint:"
            , D.stack
                [ D.fillSep
                    ["I","need","something","like",D.green "\"1.0.0 <= v < 2.0.0\""
                    ,"that","explicitly","lists","the","lower","and","upper","bounds."
                    ]
                , D.toSimpleNote $
                    "The spaces in there are required! Taking them out will confuse me. Adding\
                    \ extra spaces confuses me too. I recommend starting with a valid example\
                    \ and just changing the version numbers."
                ]
            )

        C.InvalidRange before after ->
          if before == after then
            toSnippet "PROBLEM WITH CONSTRAINT" Nothing
              ( D.reflow $
                  "I got stuck while reading your elm.json file. I ran into an invalid version constraint:"
              , D.fillSep
                  ["Elm","checks","that","all","package","APIs","follow","semantic","versioning,"
                  ,"so","it","is","best","to","use","wide","constraints.","I","recommend"
                  ,D.green $ "\"" <> D.fromVersion before <> " <= v < " <> D.fromVersion (V.bumpMajor after) <> "\""
                  ,"since","it","is","guaranteed","that","breaking","API","changes","cannot"
                  ,"happen","in","any","of","the","versions","in","that","range."
                  ]
              )

          else
            toSnippet "PROBLEM WITH CONSTRAINT" Nothing
              ( D.reflow $
                  "I got stuck while reading your elm.json file. I ran into an invalid version constraint:"
              , D.fillSep
                  ["Maybe","you","want","something","like"
                  ,D.green $ "\"" <> D.fromVersion before <> " <= v < " <> D.fromVersion (V.bumpMajor before) <> "\""
                  ,"instead?","Elm","checks","that","all","package","APIs","follow","semantic"
                  ,"versioning,","so","it","is","guaranteed","that","breaking","API","changes"
                  ,"cannot","happen","in","any","of","the","versions","in","that","range."
                  ]
              )

    OP_BadModuleName row col ->
      toSnippet "PROBLEM WITH MODULE NAME" (toHighlight row col)
        ( D.reflow $
            "I got stuck while reading your elm.json file. I was expecting a module name here:"
        , D.fillSep
            ["I","need","something","like",D.green "\"Html.Events\""
            ,"or",D.green "\"Browser.Navigation\""
            ,"where","each","segment","starts","with","a","capital"
            ,"letter","and","the","segments","are","separated","by","dots."
            ]
        )

    OP_BadModuleHeaderTooLong ->
      toSnippet "HEADER TOO LONG" Nothing
        ( D.reflow $
            "I got stuck while reading your elm.json file. This section header is too long:"
        , D.stack
            [ D.fillSep
                ["I","need","it","to","be"
                ,D.green "under",D.green "20",D.green "bytes"
                ,"so","it","renders","nicely","on","the","package","website!"
                ]
            , D.toSimpleNote
                "I count the length in bytes, so using non-ASCII characters costs extra.\
                \ Please report your case at https://github.com/elm/compiler/issues if this seems\
                \ overly restrictive for your needs."
            ]
        )

    OP_BadDependencyName row col ->
      toSnippet "PROBLEM WITH DEPENDENCY NAME" (toHighlight row col)
        ( D.reflow $
            "I got stuck while reading your elm.json file. There is something wrong with this dependency name:"
        , D.stack
            [ D.fillSep
                ["Package","names","always","include","the","name","of","the","author,"
                ,"so","I","am","expecting","to","see","dependencies","like"
                ,D.dullyellow "\"mdgriffith/elm-ui\"","and"
                ,D.dullyellow "\"Microsoft/elm-json-tree-view\"" <> "."
                ]
            , D.fillSep $
                ["I","generally","recommend","finding","the","package","you","want","on"
                ,"the","package","website,","and","installing","it","with","the"
                ,D.green "elm install","command!"
                ]
            ]
        )

    OP_BadLicense _ suggestions ->
      toSnippet "UNKNOWN LICENSE" Nothing
        ( D.reflow $
            "I got stuck while reading your elm.json file. I do not know about this type of license:"
        ,
          D.stack
            [ D.fillSep
                ["Elm","packages","generally","use"
                ,D.green "\"BSD-3-Clause\"","or",D.green "\"MIT\"" <> ","
                ,"but","I","accept","any","OSI","approved","SPDX","license."
                ,"Here","some","that","seem","close","to","what","you","wrote:"
                ]
            , D.indent 4 $ D.dullyellow $ D.vcat $ map (D.fromChars . Json.toChars) suggestions
            , D.reflow $
                "Check out https://spdx.org/licenses/ for the full list of options."
            ]
        )

    OP_BadSummaryTooLong ->
      toSnippet "SUMMARY TOO LONG" Nothing
        ( D.reflow $
            "I got stuck while reading your elm.json file. Your \"summary\" is too long:"
        , D.stack
            [ D.fillSep
                ["I","need","it","to","be"
                ,D.green "under",D.green "80",D.green "bytes"
                ,"so","it","renders","nicely","on","the","package","website!"
                ]
            , D.toSimpleNote
                "I count the length in bytes, so using non-ASCII characters costs extra.\
                \ Please report your case at https://github.com/elm/compiler/issues if this seems\
                \ overly restrictive for your needs."
            ]
        )

    OP_NoSrcDirs ->
      toSnippet "NO SOURCE DIRECTORIES" Nothing
        ( D.reflow $
            "I got stuck while reading your elm.json file. You do not have any \"source-directories\" listed here:"
        , D.fillSep
            ["I","need","something","like",D.green "[\"src\"]"
            ,"so","I","know","where","to","look","for","your","modules!"
            ]
        )



-- DETAILS


data Details
  = DetailsNoSolution
  | DetailsNoOfflineSolution
  | DetailsSolverProblem Solver
  | DetailsBadElmInPkg C.Constraint
  | DetailsBadElmInAppOutline V.Version
  | DetailsHandEditedDependencies
  | DetailsBadOutline Outline
  | DetailsCannotGetRegistry RegistryProblem
  | DetailsBadDeps FilePath [DetailsBadDep]


data DetailsBadDep
  = BD_BadDownload Pkg.Name V.Version PackageProblem
  | BD_BadBuild Pkg.Name V.Version (Map.Map Pkg.Name V.Version)


toDetailsReport :: Details -> Help.Report
toDetailsReport details =
  case details of
    DetailsNoSolution ->
      Help.report "INCOMPATIBLE DEPENDENCIES" (Just "elm.json")
        "The dependencies in your elm.json are not compatible."
        [ D.fillSep
            ["Did","you","change","them","by","hand?","Try","to","change","it","back!"
            ,"It","is","much","more","reliable","to","add","dependencies","with",D.green "elm install"
            ,"or","the","dependency","management","tool","in",D.green "elm reactor" <> "."
            ]
        , D.reflow $
            "Please ask for help on the community forums if you try those paths and are still\
            \ having problems!"
        ]

    DetailsNoOfflineSolution ->
      Help.report "TROUBLE VERIFYING DEPENDENCIES" (Just "elm.json")
        "I could not connect to https://package.elm-lang.org to get the latest list of\
        \ packages, and I was unable to verify your dependencies with the information I\
        \ have cached locally."
        [ D.reflow $
            "Are you able to connect to the internet? These dependencies may work once you\
            \ get access to the registry!"
        , D.toFancyNote
            ["If","you","changed","your","dependencies","by","hand,","try","to","change","them","back!"
            ,"It","is","much","more","reliable","to","add","dependencies","with",D.green "elm install"
            ,"or","the","dependency","management","tool","in",D.green "elm reactor" <> "."
            ]
        ]

    DetailsSolverProblem solver ->
      toSolverReport solver

    DetailsBadElmInPkg constraint ->
      Help.report "ELM VERSION MISMATCH" (Just "elm.json")
        "Your elm.json says this package needs a version of Elm in this range:"
        [ D.indent 4 $ D.dullyellow $ D.fromChars $ C.toChars constraint
        , D.fillSep
            [ "But", "you", "are", "using", "Elm"
            , D.red (D.fromVersion V.compiler)
            , "right", "now."
            ]
        ]

    DetailsBadElmInAppOutline version ->
      Help.report "ELM VERSION MISMATCH" (Just "elm.json")
        "Your elm.json says this application needs a different version of Elm."
        [ D.fillSep
            [ "It", "requires"
            , D.green (D.fromVersion version) <> ","
            , "but", "you", "are", "using"
            , D.red (D.fromVersion V.compiler)
            , "right", "now."
            ]
        ]

    DetailsHandEditedDependencies ->
      Help.report "ERROR IN DEPENDENCIES" (Just "elm.json")
        "It looks like the dependencies elm.json in were edited by hand (or by a 3rd\
        \ party tool) leaving them in an invalid state."
        [ D.fillSep
            ["Try","to","change","them","back","to","what","they","were","before!"
            ,"It","is","much","more","reliable","to","add","dependencies","with",D.green "elm install"
            ,"or","the","dependency","management","tool","in",D.green "elm reactor" <> "."
            ]
        , D.reflow $
            "Please ask for help on the community forums if you try those paths and are still\
            \ having problems!"
        ]

    DetailsBadOutline outline ->
      toOutlineReport outline

    DetailsCannotGetRegistry problem ->
      toRegistryProblemReport "PROBLEM LOADING PACKAGE LIST" problem $
        "I need the list of published packages to verify your dependencies"

    DetailsBadDeps cacheDir deps ->
      case List.sortOn toBadDepRank deps of
        [] ->
          Help.report "PROBLEM BUILDING DEPENDENCIES" Nothing
            "I am not sure what is going wrong though."
            [ D.reflow $
                "I would try deleting the " ++ cacheDir ++ " and elm-stuff/ directories, then\
                \ trying to build again. That will work if some cached files got corrupted\
                \ somehow."
            , D.reflow $
                "If that does not work, go to https://elm-lang.org/community and ask for\
                \ help. This is a weird case!"
            ]

        d:_ ->
          case d of
            BD_BadDownload pkg vsn packageProblem ->
              toPackageProblemReport pkg vsn packageProblem

            BD_BadBuild pkg vsn fingerprint ->
              Help.report "PROBLEM BUILDING DEPENDENCIES" Nothing
                "I ran into a compilation error when trying to build the following package:"
                [ D.indent 4 $ D.red $ D.fromChars $ Pkg.toChars pkg ++ " " ++ V.toChars vsn
                , D.reflow $
                    "This probably means it has package constraints that are too wide. It may be\
                    \ possible to tweak your elm.json to avoid the root problem as a stopgap. Head\
                    \ over to https://elm-lang.org/community to get help figuring out how to take\
                    \ this path!"
                , D.toSimpleNote $
                    "To help with the root problem, please report this to the package author along\
                    \ with the following information:"
                , D.indent 4 $ D.vcat $
                    map (\(p,v) -> D.fromChars $ Pkg.toChars p ++ " " ++ V.toChars v) $
                      Map.toList fingerprint
                , D.reflow $
                    "If you want to help out even more, try building the package locally. That should\
                    \ give you much more specific information about why this package is failing to\
                    \ build, which will in turn make it easier for the package author to fix it!"
                ]


toBadDepRank :: DetailsBadDep -> Int -- lower is better
toBadDepRank badDep =
  case badDep of
    BD_BadDownload _ _ _ -> 0
    BD_BadBuild _ _ _ -> 1



-- PACKAGE PROBLEM


data PackageProblem
  = PP_BadEndpointRequest Http.Error
  | PP_BadEndpointContent String
  | PP_BadArchiveRequest Http.Error
  | PP_BadArchiveContent String
  | PP_BadArchiveHash String String String


toPackageProblemReport :: Pkg.Name -> V.Version -> PackageProblem -> Help.Report
toPackageProblemReport pkg vsn problem =
  let
    thePackage =
      Pkg.toChars pkg ++ " " ++ V.toChars vsn
  in
  case problem of
    PP_BadEndpointRequest httpError ->
      toHttpErrorReport "PROBLEM DOWNLOADING PACKAGE" httpError $
        "I need to find the latest download link for " ++ thePackage

    PP_BadEndpointContent url ->
      Help.report "PROBLEM DOWNLOADING PACKAGE" Nothing
        (
          "I need to find the latest download link for " ++ thePackage ++ ", but I ran into corrupted information from:"
        )
        [ D.indent 4 $ D.dullyellow $ D.fromChars url
        , D.reflow $
            "Is something weird with your internet connection. We have gotten reports that\
            \ schools, businesses, airports, etc. sometimes intercept requests and add things\
            \ to the body or change its contents entirely. Could that be the problem?"
        ]

    PP_BadArchiveRequest httpError ->
      toHttpErrorReport "PROBLEM DOWNLOADING PACKAGE" httpError $
        "I was trying to download the source code for " ++ thePackage

    PP_BadArchiveContent url ->
      Help.report "PROBLEM DOWNLOADING PACKAGE" Nothing
        (
          "I downloaded the source code for " ++ thePackage ++ " from:"
        )
        [ D.indent 4 $ D.dullyellow $ D.fromChars url
        , D.reflow $
           "But I was unable to unzip the data. Maybe there is something weird with\
            \ your internet connection. We have gotten reports that schools, businesses,\
            \ airports, etc. sometimes intercept requests and add things to the body or\
            \ change its contents entirely. Could that be the problem?"
        ]

    PP_BadArchiveHash url expectedHash actualHash ->
      Help.report "CORRUPT PACKAGE DATA" Nothing
        (
          "I downloaded the source code for " ++ thePackage ++ " from:"
        )
        [ D.indent 4 $ D.dullyellow $ D.fromChars url
        , D.reflow "But it looks like the hash of the archive has changed since publication:"
        , D.vcat $ map D.fromChars $
            [ "  Expected: " ++ expectedHash
            , "    Actual: " ++ actualHash
            ]
        , D.reflow $
            "This usually means that the package author moved the version\
            \ tag, so report it to them and see if that is the issue. Folks\
            \ on Elm slack can probably help as well."
        ]



-- REGISTRY PROBLEM


data RegistryProblem
  = RP_Http Http.Error
  | RP_Data String BS.ByteString


toRegistryProblemReport :: String -> RegistryProblem -> String -> Help.Report
toRegistryProblemReport title problem context =
  case problem of
    RP_Http err ->
      toHttpErrorReport title err context

    RP_Data url body ->
      Help.report title Nothing (context ++ ", so I fetched:")
        [ D.indent 4 $ D.dullyellow $ D.fromChars url
        , D.reflow $
            "I got the data back, but it was not what I was expecting. The response\
            \ body contains " ++ show (BS.length body) ++ " bytes. Here is the "
            ++ if BS.length body <= 76 then "whole thing:" else "beginning:"
        , D.indent 4 $ D.dullyellow $ D.fromChars $
            if BS.length body <= 76
            then BS_UTF8.toString body
            else take 73 (BS_UTF8.toString body) ++ "..."
        , D.reflow $
            "Does this error keep showing up? Maybe there is something weird with your\
            \ internet connection. We have gotten reports that schools, businesses,\
            \ airports, etc. sometimes intercept requests and add things to the body\
            \ or change its contents entirely. Could that be the problem?"
        ]


toHttpErrorReport :: String -> Http.Error -> String -> Help.Report
toHttpErrorReport title err context =
  let
    toHttpReport intro url details =
      Help.report title Nothing intro $
        D.indent 4 (D.dullyellow (D.fromChars url)) : details
  in
  case err of
    Http.BadUrl url reason ->
      toHttpReport (context ++ ", so I wanted to fetch:") url
        [ D.reflow $ "But my HTTP library is saying this is not a valid URL. It is saying:"
        , D.indent 4 $ D.fromChars reason
        , D.reflow $
            "This may indicate that there is some problem in the compiler, so please open an\
            \ issue at https://github.com/elm/compiler/issues listing your operating system, Elm\
            \ version, the command you ran, the terminal output, and any additional information\
            \ that might help others reproduce the error."
        ]

    Http.BadHttp url httpExceptionContent ->
      case httpExceptionContent of
        HTTP.StatusCodeException response _ ->
          let
            (HTTP.Status code message) = HTTP.responseStatus response
          in
          toHttpReport (context ++ ", so I tried to fetch:") url
            [ D.fillSep $
                ["But","it","came","back","as",D.red (D.fromInt code)]
                ++ map D.fromChars (words (BS_UTF8.toString message))
            , D.reflow $
                "This may mean some online endpoint changed in an unexpected way, so if does not\
                \ seem like something on your side is causing this (e.g. firewall) please report\
                \ this to https://github.com/elm/compiler/issues with your operating system, Elm\
                \ version, the command you ran, the terminal output, and any additional information\
                \ that can help others reproduce the error!"
            ]

        HTTP.TooManyRedirects responses ->
          toHttpReport (context ++ ", so I tried to fetch:") url
            [ D.reflow $ "But I gave up after following these " ++ show (length responses) ++ " redirects:"
            , D.indent 4 $ D.vcat $ map toRedirectDoc responses
            , D.reflow $
                "Is it possible that your internet connection intercepts certain requests? That\
                \ sometimes causes problems for folks in schools, businesses, airports, hotels,\
                \ and certain countries. Try asking for help locally or in a community forum!"
            ]

        otherException ->
          toHttpReport (context ++ ", so I tried to fetch:") url
            [ D.reflow $ "But my HTTP library is giving me the following error message:"
            , D.indent 4 $ D.fromChars (show otherException)
            , D.reflow $
                "Are you somewhere with a slow internet connection? Or no internet?\
                \ Does the link I am trying to fetch work in your browser? Maybe the\
                \ site is down? Does your internet connection have a firewall that\
                \ blocks certain domains? It is usually something like that!"
            ]

    Http.BadMystery url someException ->
      toHttpReport (context ++ ", so I tried to fetch:") url
        [ D.reflow $ "But I ran into something weird! I was able to extract this error message:"
        , D.indent 4 $ D.fromChars (show someException)
        , D.reflow $
            "Is it possible that your internet connection intercepts certain requests? That\
            \ sometimes causes problems for folks in schools, businesses, airports, hotels,\
            \ and certain countries. Try asking for help locally or in a community forum!"
        ]


toRedirectDoc :: HTTP.Response body -> D.Doc
toRedirectDoc response =
  let
    (HTTP.Status code message) = HTTP.responseStatus response
  in
  case List.lookup HTTP.hLocation (HTTP.responseHeaders response) of
    Just loc -> D.red (D.fromInt code) <> " - " <> D.fromChars (BS_UTF8.toString loc)
    Nothing  -> D.red (D.fromInt code) <> " - " <> D.fromChars (BS_UTF8.toString message)



-- MAKE


data Make
  = MakeNoOutline
  | MakeCannotOptimizeAndDebug
  | MakeBadDetails Details
  | MakeAppNeedsFileNames
  | MakePkgNeedsExposing
  | MakeMultipleFilesIntoHtml
  | MakeNoMain
  | MakeNonMainFilesIntoJavaScript ModuleName.Raw [ModuleName.Raw]
  | MakeCannotBuild BuildProblem
  | MakeBadGenerate Generate


makeToReport :: Make -> Help.Report
makeToReport make =
  case make of
    MakeNoOutline ->
      Help.report "NO elm.json FILE" Nothing
        "It looks like you are starting a new Elm project. Very exciting! Try running:"
        [ D.indent 4 $ D.green $ "elm init"
        , D.reflow $
            "It will help you get set up. It is really simple!"
        ]

    MakeCannotOptimizeAndDebug ->
      Help.docReport "CLASHING FLAGS" Nothing
        ( D.fillSep
            ["I","cannot","compile","with",D.red "--optimize","and"
            ,D.red "--debug","at","the","same","time."
            ]
        )
        [ D.reflow
            "I need to take away information to optimize things, and I need to\
            \ add information to add the debugger. It is impossible to do both\
            \ at once though! Pick just one of those flags and it should work!"
        ]

    MakeBadDetails detailsProblem ->
      toDetailsReport detailsProblem

    MakeAppNeedsFileNames ->
      Help.report "NO INPUT" Nothing
        "What should I make though? I need specific files like:"
        [ D.vcat
            [ D.indent 4 $ D.green "elm make src/Main.elm"
            , D.indent 4 $ D.green "elm make src/This.elm src/That.elm"
            ]
        , D.reflow $
            "I recommend reading through https://guide.elm-lang.org for guidance on what to\
            \ actually put in those files!"
        ]

    MakePkgNeedsExposing ->
      Help.report "NO INPUT" Nothing
        "What should I make though? I need specific files like:"
        [ D.vcat
            [ D.indent 4 $ D.green "elm make src/Main.elm"
            , D.indent 4 $ D.green "elm make src/This.elm src/That.elm"
            ]
        , D.reflow $
            "You can also entries to the \"exposed-modules\" list in your elm.json file, and\
            \ I will try to compile the relevant files."
        ]

    MakeMultipleFilesIntoHtml ->
      Help.report "TOO MANY FILES" Nothing
        (
          "When producing an HTML file, I can only handle one file."
        )
        [ D.fillSep
            ["Switch","to",D.dullyellow "--output=/dev/null","if","you","just","want"
            ,"to","get","compile","errors.","This","skips","the","code","gen","phase,"
            ,"so","it","can","be","a","bit","faster","than","other","options","sometimes."
            ]
        , D.fillSep
            ["Switch","to",D.dullyellow "--output=elm.js","if","you","want","multiple"
            ,"`main`","values","available","in","a","single","JavaScript","file.","Then"
            ,"you","can","make","your","own","customized","HTML","file","that","embeds"
            ,"multiple","Elm","nodes.","The","generated","JavaScript","also","shares"
            ,"dependencies","between","modules,","so","it","should","be","smaller","than"
            ,"compiling","each","module","separately."
            ]
        ]

    MakeNoMain ->
      Help.report "NO MAIN" Nothing
        (
          "When producing an HTML file, I require that the given file has a `main` value.\
          \ That way I have something to show on screen!"
        )
        [ D.reflow $
            "Try adding a `main` value to your file? Or if you just want to verify that this\
            \ module compiles, switch to --output=/dev/null to skip the code gen phase\
            \ altogether."
        , D.toSimpleNote $
            "Adding a `main` value can be as brief as adding something like this:"
        , D.vcat
            [ D.fillSep [D.cyan "import","Html"]
            , ""
            , D.fillSep [D.green "main","="]
            , D.indent 2 $ D.fillSep [D.cyan "Html" <> ".text",D.dullyellow "\"Hello!\""]
            ]
        , D.reflow $
            "From there I can create an HTML file that says \"Hello!\" on screen. I recommend\
            \ looking through https://guide.elm-lang.org for more guidance on how to fill in\
            \ the `main` value."
        ]

    MakeNonMainFilesIntoJavaScript m ms ->
      case ms of
        [] ->
          Help.report "NO MAIN" Nothing
            (
              "When producing a JS file, I require that the given file has a `main` value. That\
              \ way Elm." ++ ModuleName.toChars m ++ ".init() is definitely defined in the\
              \ resulting file!"
            )
            [ D.reflow $
                "Try adding a `main` value to your file? Or if you just want to verify that this\
                \ module compiles, switch to --output=/dev/null to skip the code gen phase\
                \ altogether."
            , D.toSimpleNote $
                "Adding a `main` value can be as brief as adding something like this:"
            , D.vcat
                [ D.fillSep [D.cyan "import","Html"]
                , ""
                , D.fillSep [D.green "main","="]
                , D.indent 2 $ D.fillSep [D.cyan "Html" <> ".text",D.dullyellow "\"Hello!\""]
                ]
            , D.reflow $
                "Or use https://package.elm-lang.org/packages/elm/core/latest/Platform#worker to\
                \ make a `main` with no user interface."
            ]

        _:_ ->
          Help.report "NO MAIN" Nothing
            (
              "When producing a JS file, I require that given files all have `main` values.\
              \ That way functions like Elm." ++ ModuleName.toChars m ++ ".init() are\
              \ definitely defined in the resulting file. I am missing `main` values in:"
            )
            [ D.indent 4 $ D.red $ D.vcat $ map D.fromName (m:ms)
            , D.reflow $
                "Try adding a `main` value to them? Or if you just want to verify that these\
                \ modules compile, switch to --output=/dev/null to skip the code gen phase\
                \ altogether."
            , D.toSimpleNote $
                "Adding a `main` value can be as brief as adding something like this:"
            , D.vcat
                [ D.fillSep [D.cyan "import","Html"]
                , ""
                , D.fillSep [D.green "main","="]
                , D.indent 2 $ D.fillSep [D.cyan "Html" <> ".text",D.dullyellow "\"Hello!\""]
                ]
            , D.reflow $
                "Or use https://package.elm-lang.org/packages/elm/core/latest/Platform#worker to\
                \ make a `main` with no user interface."
            ]

    MakeCannotBuild buildProblem ->
      toBuildProblemReport buildProblem

    MakeBadGenerate generateProblem ->
      toGenerateReport generateProblem



-- BUILD PROBLEM


data BuildProblem
  = BuildBadModules FilePath Error.Module [Error.Module]
  | BuildProjectProblem BuildProjectProblem


data BuildProjectProblem
  = BP_PathUnknown FilePath
  | BP_WithBadExtension FilePath
  | BP_WithAmbiguousSrcDir FilePath FilePath FilePath
  | BP_MainPathDuplicate FilePath FilePath
  | BP_RootNameDuplicate ModuleName.Raw FilePath FilePath
  | BP_RootNameInvalid FilePath FilePath [String]
  | BP_CannotLoadDependencies
  | BP_Cycle ModuleName.Raw [ModuleName.Raw]
  | BP_MissingExposed (NE.List (ModuleName.Raw, Import.Problem))


toBuildProblemReport :: BuildProblem -> Help.Report
toBuildProblemReport problem =
  case problem of
    BuildBadModules root e es ->
      Help.compilerReport root e es

    BuildProjectProblem projectProblem ->
      toProjectProblemReport projectProblem


toProjectProblemReport :: BuildProjectProblem -> Help.Report
toProjectProblemReport projectProblem =
  case projectProblem of
    BP_PathUnknown path ->
      Help.report "FILE NOT FOUND" Nothing
        "I cannot find this file:"
        [ D.indent 4 $ D.red $ D.fromChars path
        , D.reflow $ "Is there a typo?"
        , D.toSimpleNote $
            "If you are just getting started, try working through the examples in the\
            \ official guide https://guide.elm-lang.org to get an idea of the kinds of things\
            \ that typically go in a src/Main.elm file."
        ]

    BP_WithBadExtension path ->
      Help.report "UNEXPECTED FILE EXTENSION" Nothing
        "I can only compile Elm files (with a .elm extension) but you want me to compile:"
        [ D.indent 4 $ D.red $ D.fromChars path
        , D.reflow $ "Is there a typo? Can the file extension be changed?"
        ]

    BP_WithAmbiguousSrcDir path srcDir1 srcDir2 ->
      Help.report "CONFUSING FILE" Nothing
        "I am getting confused when I try to compile this file:"
        [ D.indent 4 $ D.red $ D.fromChars path
        , D.reflow $
            "I always check if files appear in any of the \"source-directories\" listed in\
            \ your elm.json to see if there might be some cached information about them. That\
            \ can help me compile faster! But in this case, it looks like this file may be in\
            \ either of these directories:"
        , D.indent 4 $ D.red $ D.vcat $ map D.fromChars [srcDir1,srcDir2]
        , D.reflow $
            "Try to make it so no source directory contains another source directory!"
        ]

    BP_MainPathDuplicate path1 path2 ->
      Help.report "CONFUSING FILES" Nothing
        "You are telling me to compile these two files:"
        [ D.indent 4 $ D.red $ D.vcat $ map D.fromChars [ path1, path2 ]
        , D.reflow $
            if path1 == path2 then
              "Why are you telling me twice? Is something weird going on with a script?\
              \ I figured I would let you know about it just in case something is wrong.\
              \ Only list it once and you should be all set!"
            else
              "But seem to be the same file though... It makes me think something tricky is\
              \ going on with symlinks in your project, so I figured I would let you know\
              \ about it just in case. Remove one of these files from your command to get\
              \ unstuck!"
        ]

    BP_RootNameDuplicate name outsidePath otherPath ->
      Help.report "MODULE NAME CLASH" Nothing
        "These two files are causing a module name clash:"
        [ D.indent 4 $ D.red $ D.vcat $ map D.fromChars [ outsidePath, otherPath ]
        , D.reflow $
            "They both say `module " ++ ModuleName.toChars name ++ " exposing (..)` up\
            \ at the top, but they cannot have the same name!"
        , D.reflow $
            "Try changing to a different module name in one of them!"
        ]

    BP_RootNameInvalid givenPath srcDir _ ->
      Help.report "UNEXPECTED FILE NAME" Nothing
        "I am having trouble with this file name:"
        [ D.indent 4 $ D.red $ D.fromChars givenPath
        , D.reflow $
            "I found it in your " ++ FP.addTrailingPathSeparator srcDir ++ " directory\
            \ which is good, but I expect all of the files in there to use the following\
            \ module naming convention:"
        , toModuleNameConventionTable srcDir [ "Main", "HomePage", "Http.Helpers" ]
        , D.reflow $
            "Notice that the names always start with capital letters! Can you make your file\
            \ use this naming convention?"
        , D.toSimpleNote $
            "Having a strict naming convention like this makes it a lot easier to find\
            \ things in large projects. If you see a module imported, you know where to look\
            \ for the corresponding file every time!"
        ]

    BP_CannotLoadDependencies ->
      corruptCacheReport

    BP_Cycle name names ->
      Help.report "IMPORT CYCLE" Nothing
        "Your module imports form a cycle:"
        [ D.cycle 4 name names
        , D.reflow $
            "Learn more about why this is disallowed and how to break cycles here:"
            ++ D.makeLink "import-cycles"
        ]

    BP_MissingExposed (NE.List (name, problem) _) ->
      case problem of
        Import.NotFound ->
          Help.report "MISSING MODULE" (Just "elm.json")
            "The  \"exposed-modules\" of your elm.json lists the following module:"
            [ D.indent 4 $ D.red $ D.fromName name
            , D.reflow $
                "But I cannot find it in your src/ directory. Is there a typo? Was it renamed?"
            ]

        Import.Ambiguous _ _ pkg _ ->
          Help.report "AMBIGUOUS MODULE NAME" (Just "elm.json")
            "The  \"exposed-modules\" of your elm.json lists the following module:"
            [ D.indent 4 $ D.red $ D.fromName name
            , D.reflow $
                "But a module from " ++ Pkg.toChars pkg ++ " already uses that name. Try\
                \ choosing a different name for your local file."
            ]

        Import.AmbiguousLocal path1 path2 paths ->
          Help.report "AMBIGUOUS MODULE NAME" (Just "elm.json")
            "The  \"exposed-modules\" of your elm.json lists the following module:"
            [ D.indent 4 $ D.red $ D.fromName name
            , D.reflow $
                "But I found multiple files with that name:"
            , D.dullyellow $ D.indent 4 $ D.vcat $
                map D.fromChars (path1:path2:paths)
            , D.reflow $
                "Change the module names to be distinct!"
            ]

        Import.AmbiguousForeign _ _ _ ->
          Help.report "MISSING MODULE" (Just "elm.json")
            "The  \"exposed-modules\" of your elm.json lists the following module:"
            [ D.indent 4 $ D.red $ D.fromName name
            , D.reflow $
                "But I cannot find it in your src/ directory. Is there a typo? Was it renamed?"
            , D.toSimpleNote $
                "It is not possible to \"re-export\" modules from other packages. You can only\
                \ expose modules that you define in your own code."
            ]


toModuleNameConventionTable :: FilePath -> [String] -> D.Doc
toModuleNameConventionTable srcDir names =
  let
    toPair name =
      ( name
      , srcDir </> map (\c -> if c == '.' then FP.pathSeparator else c) name <.> "elm"
      )

    namePairs = map toPair names
    nameWidth = maximum (11 : map (length . fst) namePairs)
    pathWidth = maximum ( 9 : map (length . snd) namePairs)

    padded width str =
      str ++ replicate (width - length str) ' '

    toRow (name, path) =
      D.fromChars $
        "| " ++ padded nameWidth name ++ " | " ++ padded pathWidth path ++ " |"

    bar =
      D.fromChars $
        "+-" ++ replicate nameWidth '-' ++ "-+-" ++ replicate pathWidth '-' ++ "-+"
  in
  D.indent 4 $ D.vcat $
    [ bar, toRow ("Module Name", "File Path"), bar ] ++ map toRow namePairs ++ [ bar ]



-- GENERATE


data Generate
  = GenerateCannotLoadArtifacts
  | GenerateCannotOptimizeDebugValues ModuleName.Raw [ModuleName.Raw]


toGenerateReport :: Generate -> Help.Report
toGenerateReport problem =
  case problem of
    GenerateCannotLoadArtifacts ->
      corruptCacheReport

    GenerateCannotOptimizeDebugValues m ms ->
      Help.report "DEBUG REMNANTS" Nothing
        "There are uses of the `Debug` module in the following modules:"
        [ D.indent 4 $ D.red $ D.vcat $ map (D.fromChars . ModuleName.toChars) (m:ms)
        , D.reflow "But the --optimize flag only works if all `Debug` functions are removed!"
        , D.toSimpleNote $
            "The issue is that --optimize strips out info needed by `Debug` functions.\
            \ Here are two examples:"
        , D.indent 4 $ D.reflow $
            "(1) It shortens record field names. This makes the generated JavaScript is\
            \ smaller, but `Debug.toString` cannot know the real field names anymore."
        , D.indent 4 $ D.reflow $
            "(2) Values like `type Height = Height Float` are unboxed. This reduces\
            \ allocation, but it also means that `Debug.toString` cannot tell if it is\
            \ looking at a `Height` or `Float` value."
        , D.reflow $
            "There are a few other cases like that, and it will be much worse once we start\
            \ inlining code. That optimization could move `Debug.log` and `Debug.todo` calls,\
            \ resulting in unpredictable behavior. I hope that clarifies why this restriction\
            \ exists!"
        ]



-- CORRUPT CACHE


corruptCacheReport :: Help.Report
corruptCacheReport =
  Help.report "CORRUPT CACHE" Nothing
    "It looks like some of the information cached in elm-stuff/ has been corrupted."
    [ D.reflow $
        "Try deleting your elm-stuff/ directory to get unstuck."
    , D.toSimpleNote $
        "This almost certainly means that a 3rd party tool (or editor plugin) is\
        \ causing problems your the elm-stuff/ directory. Try disabling 3rd party tools\
        \ one by one until you figure out which it is!"
    ]



-- REACTOR


data Reactor
  = ReactorNoOutline
  | ReactorBadDetails Details
  | ReactorBadBuild BuildProblem
  | ReactorBadGenerate Generate


reactorToReport :: Reactor -> Help.Report
reactorToReport problem =
  case problem of
    ReactorNoOutline ->
      Help.report "NEW PROJECT?" Nothing
        "Are you trying to start a new project? Try this command in the terminal:"
        [ D.indent 4 $ D.green "elm init"
        , D.reflow "It will help you get started!"
        ]

    ReactorBadDetails details ->
      toDetailsReport details

    ReactorBadBuild buildProblem ->
      toBuildProblemReport buildProblem

    ReactorBadGenerate generate ->
      toGenerateReport generate



-- REPL


data Repl
  = ReplBadDetails Details
  | ReplBadInput BS.ByteString Error.Error
  | ReplBadLocalDeps FilePath Error.Module [Error.Module]
  | ReplProjectProblem BuildProjectProblem
  | ReplBadGenerate Generate
  | ReplBadCache
  | ReplBlocked


replToReport :: Repl -> Help.Report
replToReport problem =
  case problem of
    ReplBadDetails details ->
      toDetailsReport details

    ReplBadInput source err ->
      Help.compilerReport "/" (Error.Module N.replModule "REPL" File.zeroTime source err) []

    ReplBadLocalDeps root e es ->
      Help.compilerReport root e es

    ReplProjectProblem projectProblem ->
      toProjectProblemReport projectProblem

    ReplBadGenerate generate ->
      toGenerateReport generate

    ReplBadCache ->
      corruptCacheReport

    ReplBlocked ->
      corruptCacheReport
