module Language.PureScript.Ide.StateSpec where

import Protolude
import Control.Lens (Ixed(..), folded)
import Language.PureScript.Ide.Types (IdeDeclarationAnn, IdeInstance(..), ModuleMap, _IdeDeclTypeClass, anyOf, idaDeclaration, ideTCInstances)
import Language.PureScript.Ide.State (resolveDataConstructorsForModule, resolveInstances, resolveOperatorsForModule)
import Language.PureScript.Ide.Test (ideDtor, ideType, ideTypeClass, ideTypeOp, ideValue, ideValueOp, mn)
import Language.PureScript qualified as P
import Test.Hspec (Spec, describe, it, shouldSatisfy)
import Data.Map qualified as Map

valueOperator :: Maybe P.SourceType -> IdeDeclarationAnn
valueOperator =
  ideValueOp "<$>" (P.Qualified (P.ByModuleName (mn "Test")) (Left "function")) 2 Nothing

ctorOperator :: Maybe P.SourceType -> IdeDeclarationAnn
ctorOperator =
  ideValueOp ":" (P.Qualified (P.ByModuleName (mn "Test")) (Right "Cons")) 2 Nothing

typeOperator :: Maybe P.SourceType -> IdeDeclarationAnn
typeOperator =
  ideTypeOp ":" (P.Qualified (P.ByModuleName (mn "Test")) "List") 2 Nothing

testModule :: (P.ModuleName, [IdeDeclarationAnn])
testModule =
  (mn "Test",
    [ ideValue "function" (Just P.srcREmpty)
    , ideDtor "Cons" "List" (Just P.tyString)
    , ideType "List" Nothing []
    , valueOperator Nothing
    , ctorOperator Nothing
    , typeOperator Nothing
    ])

testState :: ModuleMap [IdeDeclarationAnn]
testState = Map.fromList [testModule]

-- The accessor fields for these data types are not exposed unfortunately
ef :: P.ExternsFile
ef = P.ExternsFile
  -- { efVersion =
    mempty
  -- , efModuleName =
    (mn "InstanceModule")
  -- , efExports =
    mempty
  -- , efImports =
    mempty
  -- , efFixities =
    mempty
  -- , efTypeFixities =
    mempty
  --, efDeclarations =
    [ P.EDInstance
      -- { edInstanceClassName =
      (P.Qualified (P.ByModuleName (mn "ClassModule")) (P.ProperName "MyClass"))
      -- , edInstanceName =
      (P.Ident "myClassInstance")
      -- . edInstanceForAll =
      []
      -- , edInstanceKinds =
      mempty
      -- , edInstanceTypes =
      mempty
      -- , edInstanceConstraints =
      mempty
      -- , edInstanceChain =
      Nothing
      -- , edInstanceChainIndex =
      0
      -- , edInstanceNameSource =
      P.UserNamed
      -- , edInstanceSourceSpan =
      P.NullSourceSpan
 --     }
    ]
  --, efSourceSpan =
    (P.internalModuleSourceSpan "<tests>")
 -- }

moduleMap :: ModuleMap [IdeDeclarationAnn]
moduleMap = Map.singleton (mn "ClassModule") [ideTypeClass "MyClass" P.kindType []]

ideInstance :: IdeInstance
ideInstance = IdeInstance (mn "InstanceModule") (P.Ident "myClassInstance") mempty mempty

spec :: Spec
spec = do
  describe "resolving operators" $ do
    it "resolves the type for a value operator" $
      resolveOperatorsForModule testState (snd testModule) `shouldSatisfy` elem (valueOperator (Just P.srcREmpty))
    it "resolves the type for a constructor operator" $
      resolveOperatorsForModule testState (snd testModule) `shouldSatisfy` elem (ctorOperator (Just P.tyString))
    it "resolves the kind for a type operator" $
      resolveOperatorsForModule testState (snd testModule) `shouldSatisfy` elem (typeOperator (Just P.kindType))
  describe "resolving instances for type classes" $ do
    it "resolves an instance for an existing type class" $ do
      resolveInstances (Map.singleton (mn "InstanceModule") ef) moduleMap
        `shouldSatisfy`
        anyOf (ix (mn "ClassModule") . ix 0 . idaDeclaration . _IdeDeclTypeClass . ideTCInstances . folded) (ideInstance ==)
  describe "resolving data constructors" $ do
    it "resolves a constructor" $ do
      resolveDataConstructorsForModule (snd testModule)
        `shouldSatisfy`
        elem (ideType "List" Nothing [(P.ProperName "Cons", P.tyString)])
