{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE QuasiQuotes #-}
module Nirum.Targets.Python.TypeExpressionSpec where

import Test.Hspec.Meta
import Text.InterpolatedString.Perl6 (qq)

import Nirum.Constructs.Module
import Nirum.Constructs.TypeDeclaration
import Nirum.Constructs.TypeExpression
import Nirum.Targets.Python (Source (..))
import Nirum.Targets.Python.CodeGen
import Nirum.Targets.Python.CodeGenSpec hiding (spec)
import Nirum.Targets.Python.TypeExpression

spec :: Spec
spec = pythonVersionSpecs $ \ ver -> do
    let empty' = empty ver
        -- run' :: CodeGen a -> (Either CompileError a, CodeGenContext)
        run' c = runCodeGen c empty'
        -- code :: CodeGen a -> a
        code = either (const undefined) id . fst . run'
        builtinsPair =
            ( "__builtin__"
            , case ver of
                  Python2 -> "__builtin__"
                  Python3 -> "builtins"
            )

    specify [qq|compilePrimitiveType ($ver)|] $ do
        let (boolCode, boolContext) = run' $ compilePrimitiveType Bool
        let intTypeCode = case ver of
                Python2 -> "_numbers.Integral"
                Python3 -> "__builtin__.int"
        boolCode `shouldBe` Right "__builtin__.bool"
        standardImports boolContext `shouldBe` [builtinsPair]
        code (compilePrimitiveType Bigint) `shouldBe` intTypeCode
        let (decimalCode, decimalContext) = run' (compilePrimitiveType Decimal)
        decimalCode `shouldBe` Right "_decimal.Decimal"
        standardImports decimalContext `shouldBe` [("_decimal", "decimal")]
        code (compilePrimitiveType Int32) `shouldBe` intTypeCode
        code (compilePrimitiveType Int64) `shouldBe` intTypeCode
        code (compilePrimitiveType Float32) `shouldBe` "__builtin__.float"
        code (compilePrimitiveType Float64) `shouldBe` "__builtin__.float"
        code (compilePrimitiveType Text) `shouldBe`
            case ver of
                Python2 -> "__builtin__.unicode"
                Python3 -> "__builtin__.str"
        code (compilePrimitiveType Binary) `shouldBe` "__builtin__.bytes"
        let (dateCode, dateContext) = run' (compilePrimitiveType Date)
        dateCode `shouldBe` Right "_datetime.date"
        standardImports dateContext `shouldBe` [("_datetime", "datetime")]
        let (datetimeCode, datetimeContext) =
                run' (compilePrimitiveType Datetime)
        datetimeCode `shouldBe` Right "_datetime.datetime"
        standardImports datetimeContext `shouldBe` [("_datetime", "datetime")]
        let (uuidCode, uuidContext) = run' (compilePrimitiveType Uuid)
        uuidCode `shouldBe` Right "_uuid.UUID"
        standardImports uuidContext `shouldBe` [("_uuid", "uuid")]
        code (compilePrimitiveType Url) `shouldBe`
            case ver of
                Python2 -> "__builtin__.basestring"
                Python3 -> "__builtin__.str"

    describe [qq|compileTypeExpression ($ver)|] $ do
        let Source { sourceModule = bm } = makeDummySource $ Module [] Nothing
        specify "TypeIdentifier" $ do
            let (c, ctx) = run' $
                    compileTypeExpression bm (Just $ TypeIdentifier "binary")
            standardImports ctx `shouldBe` [builtinsPair]
            localImports ctx `shouldBe` []
            c `shouldBe` Right "__builtin__.bytes"
        specify "OptionModifier" $ do
            let (c', ctx') = run' $
                    compileTypeExpression bm (Just $ OptionModifier "binary")
            standardImports ctx' `shouldBe`
                [builtinsPair, ("_typing", "typing")]
            localImports ctx' `shouldBe` []
            c' `shouldBe` Right "_typing.Optional[__builtin__.bytes]"
        specify "SetModifier" $ do
            let (c'', ctx'') = run' $
                    compileTypeExpression bm (Just $ SetModifier "float32")
            standardImports ctx'' `shouldBe`
                [builtinsPair, ("_typing", "typing")]
            localImports ctx'' `shouldBe` []
            c'' `shouldBe` Right "_typing.AbstractSet[__builtin__.float]"
        specify "ListModifier" $ do
            let (c''', ctx''') = run' $
                    compileTypeExpression bm (Just $ ListModifier "float64")
            standardImports ctx''' `shouldBe`
                [builtinsPair, ("_typing", "typing")]
            localImports ctx''' `shouldBe` []
            c''' `shouldBe` Right "_typing.Sequence[__builtin__.float]"
        specify "MapModifier" $ do
            let (c'''', ctx'''') = run' $ compileTypeExpression bm $
                    Just $ MapModifier "uuid" "binary"
            standardImports ctx'''' `shouldBe`
                [builtinsPair, ("_typing", "typing"), ("_uuid", "uuid")]
            localImports ctx'''' `shouldBe` []
            c'''' `shouldBe`
                Right "_typing.Mapping[_uuid.UUID, __builtin__.bytes]"
