content: module Test.Asdf.ClassSpec where
  
  import Conduit
  import Data.ByteString (ByteString)
  import Data.Massiv.Array (Array, Comp (..), D, Ix1, P)
  import Data.Massiv.Array qualified as M
  import Data.Text (Text)
  import Effectful.Resource
  import GHC.Generics (Generic, from)
  import GHC.Int (Int32, Int64)
  import Skeletest
  import System.ByteOrder
  import Telescope.Asdf.Class
  import Telescope.Asdf.Encoding
  import Telescope.Asdf.Encoding.File
  import Telescope.Asdf.Error
  import Telescope.Asdf.NDArray
  import Telescope.Asdf.Node
  import Telescope.Data.Axes
  import Telescope.Data.Parser
  import Test.Asdf.DecodeSpec (ExampleTreeFix (..), parseIO)
  import Text.Libyaml qualified as Yaml
  
  
  spec :: Spec
  spec = do
    describe "FromAsdf" $ do
      fromAsdfSpec
      describe "nulls" nullSpec
    describe "FromAsdf" fromAsdfSpec
    describe "ToAsdf" toAsdfSpec
    describe "GObject" gObjectSpec
  
  
  fromAsdfSpec :: Spec
  fromAsdfSpec = do
    it "parses object" $ do
      let (tree :: Object) =
            [ ("foo", fromValue $ Integer 42)
            , ("name", fromValue $ String "Monty")
            , ("sequence", fromValue $ Array $ fmap (fromValue . Integer) [0 .. 99])
            , ("random", fromValue $ NDArray $ NDArrayData "" BigEndian Float64 (axesRowMajor [0]))
            ]
      ex <- parseIO $ parseValue @Example (Object tree)
      ex.foo `shouldBe` 42
      ex.name `shouldBe` "Monty"
      ex.sequence `shouldBe` [0 .. 99]
  
    it "parses sequence from example.asdf as an NDArray" $ do
      ExampleTreeFix (Tree tree) <- getFixture
      (Sequence s) <- parseIO $ parseValue @Sequence (Object tree)
      s `shouldBe` M.delay (M.fromLists' @P Seq [0 .. 99])
  
    it "resolves anchors" $ do
      let ex = AnchorExample "world" "asdf"
      out <- runAsdfM $ encode ex
      ex2 <- runAsdfM $ decode @AnchorExample out
      ex2.message `shouldBe` "world"
      ex2.alias `shouldBe` "world"
  
  
  nullSpec :: Spec
  nullSpec = do
    it "parses 'key: null'" $ do
      let Encoded out = encodeTree "{key: null, key2: ~, key3: value}"
      ex <- decodeM @Object out
      lookup "key" ex `shouldBe` Just (toNode Null)
      lookup "key2" ex `shouldBe` Just (toNode Null)
      lookup "key3" ex `shouldBe` Just (toNode (String "value"))
  
    it "parses Null as Maybe" $ do
      val <- parseIO $ parseValue @(Maybe Text) Null
      val `shouldBe` Nothing
  
      val2 <- parseIO $ parseNode @(Maybe Text) (Node mempty Nothing Null)
      val2 `shouldBe` Nothing
  
    it "parses null fields as Maybe" $ do
      let input = [("key", fromValue Null)]
      val <- parseIO $ do
        res :: Maybe Text <- input .: "key"
        pure res
      val `shouldBe` Nothing
  
    it "parses null fields as Maybe with (.:?)" $ do
      let input = [("key", fromValue Null)]
      val <- parseIO $ do
        res :: Maybe Text <- input .:? "key"
        pure res
      val `shouldBe` Nothing
  
  
  toAsdfSpec :: Spec
  toAsdfSpec = do
    it "should serialize Example" $ do
      let ex = Example{foo = 40, name = "Marty", sequence = [], powers = Nothing, random = M.empty}
      let Node s anc val = toNode ex
      s `shouldBe` schema ex
      anc `shouldBe` Just "example"
      o <- expectObject val
      lookup "foo" o `shouldBe` Just (Node mempty Nothing (Integer 40))
      lookup "name" o `shouldBe` Just (Node mempty Nothing (String "Marty"))
  
    it "should serialize Example sequence as list" $ do
      let val = toValue $ Example{foo = 40, name = "Marty", sequence = [0 .. 99], powers = Nothing, random = M.empty}
      o <- expectObject val
      ns <- expectArray $ lookup "sequence" o
      ns `shouldBe` fmap (fromValue . Integer) [0 .. 99]
  
    it "should serialize list to Array" $ do
      let nums = [0 .. 99] :: [Int]
      toValue nums `shouldBe` Array (fmap (Node mempty Nothing . Integer) [0 .. 99])
  
    it "should forward schema to maybes" $ do
      schema @(Maybe Example) Nothing `shouldBe` mempty
      schema @(Maybe Example) (Just undefined) `shouldBe` schema @Example undefined
  
  
  -- it "should produce similar example.asdf" $ do
  --   ExampleFileFix inp _ <- getFixture
  --   e <- decodeM @Example inp
  --   -- o <- encodeM e
  --   -- BS.writeFile "dump/test.asdf" o
  --   -- print e
  --   fail ":NOPE"
  
  gObjectSpec :: Spec
  gObjectSpec = do
    it "should gen object" $ do
      gToObject (from (TinyGen "world")) `shouldBe` [("hello", fromValue (String "world"))]
  
    it "should gen tiny type" $ do
      toValue (TinyGen "world") `shouldBe` Object [("hello", fromValue (String "world"))]
  
    it "should gen maybe type" $ do
      toValue (MaybeGen (Just "world")) `shouldBe` Object [("hello", fromValue (String "world"))]
      toValue (MaybeGen Nothing) `shouldBe` Object [("hello", fromValue Null)]
  
    it "should allow maybes" $ do
      let val = Object [("hello", fromValue (String "world"))]
      m1 <- parseIO (parseValue val)
      m1 `shouldBe` MaybeGen (Just "world")
  
      m2 <- parseIO (parseValue $ Object [])
      m2 `shouldBe` MaybeGen Nothing
  
  
  -- newtype ExampleAsdfFix = ExampleAsdfFix Asdf
  -- instance Fixture ExampleAsdfFix where
  --   fixtureAction = do
  --     ExampleFileFix _ f <- getFixture
  --     a <- runAsdfM $ fromAsdfFile f.tree f.blocks
  --     pure $ noCleanup $ ExampleAsdfFix a
  
  dumpEvents :: ByteString -> IO ()
  dumpEvents inp = do
    runAsdfM $ do
      a <- splitAsdfFile inp
      runResource $ runConduit $ Yaml.decode a.tree.bytes .| takeC 100 .| mapM_C (liftIO . print)
  
  
  data TinyGen = TinyGen
    { hello :: Text
    }
    deriving (Generic, FromAsdf, ToAsdf)
  
  
  data MaybeGen = MaybeGen
    { hello :: Maybe Text
    }
    deriving (Generic, FromAsdf, ToAsdf, Eq)
  
  
  data Example = Example
    { foo :: Int32
    , name :: Text
    , powers :: Maybe Powers
    , sequence :: [Int64]
    , random :: Array D Ix1 Double
    }
    deriving (Generic, Show)
  instance ToAsdf Example where
    schema _ = "example/woot-1.0"
    anchor _ = Just "example"
  instance FromAsdf Example where
    parseValue = \case
      Object o -> do
        foo <- o .: "foo"
        name <- o .: "name"
        sq <- o .: "sequence" >>= parseSequence
        powers <- o .:? "powers"
        random <- o .: "random"
        pure $ Example{foo, name, sequence = sq, powers, random}
      val -> expected "Example" val
     where
      parseSequence = \case
        Array ns -> mapM parseNode ns
        NDArray nd -> fromNDArray nd
        val -> expected "Array or NDArray" val
  
  
  data Powers = Powers
    {squares :: Array D Ix1 Int64}
    deriving (Generic, Show, ToAsdf, FromAsdf)
  
  
  data Sequence = Sequence (Array D Ix1 Int64)
    deriving (Eq)
  instance FromAsdf Sequence where
    parseValue = \case
      Object o -> do
        sql <- o .: "sequence"
        pure $ Sequence sql
      node -> expected "Example Sequence" node
  
  
  data AnchorExample = AnchorExample
    { message :: Text
    , alias :: Text
    }
    deriving (Generic, FromAsdf)
  instance ToAsdf AnchorExample where
    toValue ae =
      Object
        [ ("message", Node mempty (Just "message") $ String ae.message)
        , ("alias", toNode $ Alias "message")
        ]
  
  
  expectArray :: Maybe Node -> IO [Node]
  expectArray = \case
    Just (Node _ _ (Array ns)) -> pure ns
    n -> fail $ "Expected Array, but got: " ++ show n
  
  
  expectObject :: Value -> IO Object
  expectObject = \case
    Object o -> pure o
    n -> fail $ "Expected Object, but got: " ++ show n
  