module Language.Haskell.Tools.Refactor.Builtin.ExtensionOrganizer.Checkers.ConstraintKindsChecker where

import Name as GHC (isTyVarName)
import Type as GHC (tcReturnsConstraintKind)

import Control.Reference ((^?), (^.), (&))

import Data.Generics.Uniplate.Data()
import Data.Generics.Uniplate.Operations

import Language.Haskell.Tools.Refactor
import Language.Haskell.Tools.Refactor.Builtin.ExtensionOrganizer.ExtMonad


chkConstraintKindsDecl :: CheckNode Decl
chkConstraintKindsDecl = conditional chkConstraintKindsDecl' ConstraintKinds

chkConstraintKindsDecl' :: CheckNode Decl
chkConstraintKindsDecl' d@(TypeDecl dh rhs)
  -- Has any constraints of form (x t1 t2)
  | ctxts <- universeBi rhs :: [Context]
  , any hasTyVarHeadAsserts ctxts
  = addEvidence ConstraintKinds d
  -- Right-hand side has kind Constraint
  | otherwise = do
  let ty = typeOrKindFromId . declHeadQName $ dh
  if hasConstraintKind ty || tcReturnsConstraintKind ty
     then addEvidence ConstraintKinds d
     else return d
chkConstraintKindsDecl' d = return d

hasTyVarHeadAsserts :: Context -> Bool
hasTyVarHeadAsserts = hasAnyTyVarHeads . (^. contextAssertion)

hasAnyTyVarHeads :: Assertion -> Bool
hasAnyTyVarHeads (ClassAssert n _)
  | Just n' <- semanticsName n = isTyVarName n'
  | otherwise               = False
hasAnyTyVarHeads ta@TupleAssert{}
  | Just assertions <- ta ^? innerAsserts & annListElems
  = any hasAnyTyVarHeads assertions
hasAnyTyVarHeads _ = False