{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
-- See Note [Arbitary Maybe Void]
{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}

module Language.LSP.Protocol.QuickCheck.Message where

import Data.Row qualified as R
import Data.Row.Records qualified as R
import Data.Void
import Language.LSP.Protocol.Message
import Language.LSP.Protocol.QuickCheck.Types
import Language.LSP.Protocol.Types
import Test.QuickCheck
import Test.QuickCheck.Arbitrary.Generic
import Test.QuickCheck.Instances ()

deriving via (GenericArbitrary RequestMessage) instance Arbitrary RequestMessage
deriving via (GenericArbitrary ResponseMessage) instance Arbitrary ResponseMessage
deriving via (GenericArbitrary NotificationMessage) instance Arbitrary NotificationMessage

-- See Note [Arbitary Maybe Void]
instance {-# OVERLAPS #-} Arbitrary (Maybe Void) where
  arbitrary = pure Nothing

-- FIXME: there is something funny with the way we're representing error data.
-- Should be able to have these instances!
{-
deriving via (GenericArbitrary ResponseError) instance Arbitrary ResponseError

deriving via
  (GenericArbitrary (TResponseError m))
  instance
    (Arbitrary (ErrorData m)) => Arbitrary (TResponseError m)

deriving via
  (GenericArbitrary (TResponseMEssage m))
  instance
    (Arbitrary (MessageResult m), Arbitrary (ErrorData m)) => Arbitrary (TResponseMEssage m)
-}

instance (ErrorData m ~ Maybe Void) => Arbitrary (TResponseError m) where
  arbitrary = TResponseError <$> arbitrary <*> arbitrary <*> pure Nothing
  shrink = genericShrink

instance Arbitrary ResponseError where
  arbitrary = ResponseError <$> arbitrary <*> arbitrary <*> pure Nothing
  shrink = genericShrink

-- See Note [Arbitary Maybe Void]
deriving via
  (GenericArbitrary (TResponseMessage m))
  instance
    (Arbitrary (MessageResult m), ErrorData m ~ Maybe Void) => Arbitrary (TResponseMessage m)

-- These require a method singleton. We need something like SingI from
-- singletons to pass that into the class instance
{-
instance (Arbitrary (MessageParams m)) => Arbitrary (TRequestMessage m) where
  arbitrary = TRequestMessage <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
  shrink = genericShrink

instance (Arbitrary (MessageParams m)) => Arbitrary (TNotificationMessage m) where
  arbitrary = TNotificationMessage <$> arbitrary <*> arbitrary <*> arbitrary
  shrink = genericShrink
-}

{- Note [Arbitary Maybe Void]
For methods that don't have error data, we say that their error data type is 'Void'.
This means that the error data field has type 'Maybe Void', i.e. can only be 'Nothing',
which is what we want.

However, we have a problem with the Arbitrary instance. There is an 'Arbitrary (Maybe a)'
instance which depends on an 'Arbitrary a' instance - but there is no 'Arbitrary Void'
instance, and apparently can't be since we can't make an empty generator.

So we cheat a bit:
- Define an overlapping 'Arbitrary (Maybe Void)' instance
- Define the instances for response messages to require 'Arbitrary (Maybe (ErrorData m))',
  which delays GHC picking the actual instance until the use site, so it can pick
  the overlapping one.
-}
