{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}

module Feature.Inference.TypeInference
  ( api,
  )
where

import Data.Kind (Type)
import Data.Morpheus.Server (interpreter)
import Data.Morpheus.Server.Types
  ( GQLRequest,
    GQLResponse,
    GQLType (..),
    RootResolver (..),
    Undefined,
    defaultRootResolver,
  )
import Data.Text
  ( Text,
    pack,
  )
import GHC.Generics (Generic)

data Power
  = Thunderbolts
  | Shapeshift
  | Hurricanes
  deriving (Generic, GQLType)

data Deity (m :: Type -> Type) = Deity
  { name :: Text,
    power :: Power
  }
  deriving (Generic, GQLType)

deityRes :: Deity m
deityRes = Deity {name = "Morpheus", power = Shapeshift}

data Hydra = Hydra
  { name :: Text,
    age :: Int
  }
  deriving (Show, Generic, GQLType)

data Monster
  = MonsterHydra Hydra
  | Cerberus {name :: Text}
  | UnidentifiedMonster
  deriving (Show, Generic, GQLType)

data Character (m :: Type -> Type)
  = CharacterDeity (Deity m) -- Only <tyCon name><type ref name> should generate direct link
  | Creature {creatureName :: Text, creatureAge :: Int}
  | BoxedDeity {boxedDeity :: Deity m}
  | ScalarRecord {scalarText :: Text}
  | CharacterAge Int
  | SomeDeity (Deity m)
  | SomeCompound Int Text
  | Zeus
  | Cronus
  deriving (Generic, GQLType)

resolveCharacter :: [Character m]
resolveCharacter =
  [ CharacterDeity deityRes,
    Creature {creatureName = "Lamia", creatureAge = 205},
    BoxedDeity {boxedDeity = deityRes},
    ScalarRecord {scalarText = "Some Text"},
    SomeDeity deityRes,
    CharacterAge 12,
    SomeCompound 21 "some text",
    Zeus,
    Cronus
  ]

newtype MonsterArgs = MonsterArgs
  { monster :: Monster
  }
  deriving (Show, Generic, GQLType)

data Query (m :: Type -> Type) = Query
  { deity :: Deity m,
    character :: [Character m],
    showMonster :: MonsterArgs -> m Text,
    unitType :: m ()
  }
  deriving (Generic, GQLType)

rootResolver :: RootResolver IO () Query Undefined Undefined
rootResolver =
  defaultRootResolver
    { queryResolver =
        Query
          { deity = deityRes,
            character = resolveCharacter,
            showMonster,
            unitType = pure ()
          }
    }
  where
    showMonster MonsterArgs {monster} = pure (pack $ show monster)

api :: GQLRequest -> IO GQLResponse
api = interpreter rootResolver
