-- |
--
-- Copyright:
--   This file is part of the package addy. It is subject to the license
--   terms in the LICENSE file found in the top-level directory of this
--   distribution and at:
--
--     https://code.devalot.com/open/addy
--
--   No part of this package, including this file, may be copied,
--   modified, propagated, or distributed except according to the terms
--   contained in the LICENSE file.
--
-- License: BSD-2-Clause
module ParserTest
  ( test,
  )
where

import Addy.Internal.Parser as P
import Addy.Internal.Render ()
import Addy.Internal.Types
import qualified Hedgehog
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit
import Test.Tasty.Hedgehog
import TestData

test :: TestTree
test =
  testGroup
    "Parser"
    [ testCase "isemail tests" testParserWithIsEmail,
      testCase "RFC 5322 examples" testRfc5322Examples,
      testCase "Wikipedia internationalization examples" testWikipediaIntExamples,
      testCase "Miscellaneous examples" testMiscExamples,
      testProperty
        "Generated short addresses"
        (testGeneratedExamples genShortEmail),
      testProperty
        "Generated long addresses"
        (testGeneratedExamples genLongEmail)
    ]

testRfc5322Examples :: Assertion
testRfc5322Examples = do
  mapM_ (fst >>> go) rfc5322Examples
  -- Only works in lenient mode:
  mapM_ (\(a, _, _) -> assertParse Lenient a) rfc5322ObsExamples
  where
    go :: Text -> Assertion
    go t = do
      assertParse Strict t
      assertParse Lenient t

testWikipediaIntExamples :: Assertion
testWikipediaIntExamples =
  mapM_ go wikipediaIntExamples
  where
    go t = do
      assertParse Strict t
      assertParse Lenient t

testMiscExamples :: Assertion
testMiscExamples =
  forM_ examples $ \t -> do
    assertParse Strict t
    assertParse Lenient t
  where
    examples =
      miscExamples
        <> [ "(((((((((())))))))))user@example.org"
           ]

testGeneratedExamples :: Hedgehog.Gen Text -> Hedgehog.Property
testGeneratedExamples gen =
  Hedgehog.property $ do
    source <- Hedgehog.forAll gen
    let result = runParse source
    Hedgehog.annotateShow result
    Hedgehog.assert (isRight result)
  where
    runParse = parseWithMode Strict

assertParse :: Mode -> Text -> Assertion
assertParse mode text =
  let r = parseWithMode mode text
   in assertBool
        (toString text <> " " <> show r <> " " <> show mode)
        (isRight r)

testParserWithIsEmail :: Assertion
testParserWithIsEmail = mapM_ go =<< isEmailTests
  where
    go :: (IsEmailTest, IsEmailCat) -> Assertion
    go (isemail, cat) = case cat of
      CatError -> do
        shouldFail Strict isemail
        shouldFail Lenient isemail
      CatDeprec -> do
        shouldFail Strict isemail
        shouldPass Lenient isemail
      CatOkay ->
        shouldPass Strict isemail
    shouldPass, shouldFail :: Mode -> IsEmailTest -> Assertion
    shouldPass = expect isRight
    shouldFail = expect isLeft
    expect ::
      (Either (NonEmpty Error) EmailAddr -> Bool) ->
      Mode ->
      IsEmailTest ->
      Assertion
    expect f mode ie =
      let result = runParse mode ie
       in assertBool (report mode ie result) (f result)
    runParse :: Mode -> IsEmailTest -> Either (NonEmpty Error) EmailAddr
    runParse mode = parseWithMode mode . ietAddr
    report :: Mode -> IsEmailTest -> Either (NonEmpty Error) EmailAddr -> String
    report mode isemail result =
      mconcat
        [ "IsEmail test id ",
          show (ietId isemail),
          " in mode: ",
          show mode,
          " did not expect: ",
          show result,
          " from: ",
          toString (ietAddr isemail)
        ]