{-# LANGUAGE LambdaCase #-}

{- |
 Module: Language.Bitcoin.Miniscript.Syntax

 Haskell embedding of miniscript.  See http://bitcoin.sipa.be/miniscript/ for
 details.  Much of the documentation below is taken from this site.
-}
module Language.Bitcoin.Miniscript.Syntax (
    Value (..),
    var,
    literal,
    Miniscript (..),
    let_,
    key,
    keyH,
    older,
    after,
    sha256,
    ripemd160,
    hash256,
    hash160,
    thresh,
    multi,
    Annotation (..),
    MiniscriptAnnotation (..),
) where

import Data.ByteString (ByteString)
import Data.Foldable (foldr')
import Data.Text (Text)

import Language.Bitcoin.Script.Descriptors.Syntax (KeyDescriptor)

data Value a = Variable Text | Lit a
    deriving (Eq, Show, Ord)

var :: Text -> Value a
var = Variable

literal :: a -> Value a
literal = Lit

-- | The Miniscript AST with the addition of key descriptors and let bindings
data Miniscript
    = Var Text
    | Let Text Miniscript Miniscript
    | Boolean Bool
    | Number Int
    | Bytes ByteString
    | KeyDesc KeyDescriptor
    | Key (Value KeyDescriptor)
    | KeyH (Value KeyDescriptor)
    | Older (Value Int)
    | After (Value Int)
    | Sha256 (Value ByteString)
    | Ripemd160 (Value ByteString)
    | Hash256 (Value ByteString)
    | Hash160 (Value ByteString)
    | AndOr Miniscript Miniscript Miniscript
    | AndV Miniscript Miniscript
    | AndB Miniscript Miniscript
    | OrB Miniscript Miniscript
    | OrC Miniscript Miniscript
    | OrD Miniscript Miniscript
    | OrI Miniscript Miniscript
    | Thresh (Value Int) Miniscript [Miniscript]
    | Multi (Value Int) [Value KeyDescriptor]
    | AnnA Miniscript
    | AnnS Miniscript
    | AnnC Miniscript
    | AnnD Miniscript
    | AnnV Miniscript
    | AnnJ Miniscript
    | AnnN Miniscript
    deriving (Eq, Show)

-- | Check a key
key :: KeyDescriptor -> Miniscript
key = AnnC . Key . literal

-- | Check a key hash
keyH :: KeyDescriptor -> Miniscript
keyH = AnnC . KeyH . literal

older :: Int -> Miniscript
older = Older . literal

after :: Int -> Miniscript
after = After . literal

sha256 :: ByteString -> Miniscript
sha256 = Sha256 . literal

ripemd160 :: ByteString -> Miniscript
ripemd160 = Ripemd160 . literal

hash256 :: ByteString -> Miniscript
hash256 = Hash256 . literal

hash160 :: ByteString -> Miniscript
hash160 = Hash160 . literal

thresh :: Int -> Miniscript -> [Miniscript] -> Miniscript
thresh k = Thresh (Lit k)

multi :: Int -> [KeyDescriptor] -> Miniscript
multi k ks = Multi (literal k) $ literal <$> ks

let_ :: [(Text, Miniscript)] -> Miniscript -> Miniscript
let_ = flip . foldr' $ uncurry Let

class MiniscriptAnnotation a where
    (.:) :: a -> Miniscript -> Miniscript

data Annotation = A | S | C | D | V | J | N | T | L | U deriving (Eq, Show, Ord, Enum)

instance MiniscriptAnnotation Annotation where
    (.:) = \case
        A -> AnnA
        S -> AnnS
        C -> AnnC
        D -> AnnD
        V -> AnnV
        J -> AnnJ
        N -> AnnN
        T -> (`AndV` Boolean True)
        L -> OrI $ Boolean True
        U -> (`OrI` Boolean False)

instance MiniscriptAnnotation a => MiniscriptAnnotation [a] where
    (.:) = flip $ foldr' (.:)
