{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}

module Fraxl.FakeDB
  ( Band (..),
    Member (..),
    Instrument (..),
    Query,
    runQuery,
    getInstruments,
    getBandMembersByInstrumentID,
    getBandMembersByBandID,
    getBandsByID,
  )
where

import Data.Functor.Identity (Identity (..), runIdentity)
import Data.Map (Map)
import qualified Data.Map as M
import Data.Set (Set, toList)
import Data.Text (Text)
import Prelude
  ( Applicative (..),
    Functor (..),
    Int,
    Monad (..),
    Ord (..),
    elem,
    filter,
    foldMap,
    (.),
    (==),
  )

data Band = Band
  { name :: Text,
    id :: Int
  }

bandDB :: [Band]
bandDB =
  [ Band "Jem and the Holograms" 0,
    Band "The Misfits" 1
  ]

data Member = Member
  { name :: Text,
    instrumentID :: Int,
    bandID :: Int
  }

memberDB :: [Member]
memberDB =
  [ Member "Jerrica" 0 0,
    Member "Kimber" 1 0,
    Member "Aja" 2 0,
    Member "Shana" 3 0,
    Member "Carmen" 4 0,
    Member "Glenn" 0 1,
    Member "Franché" 2 1,
    Member "Jerry" 3 1,
    Member "Jim" 4 1
  ]

data Instrument = Instrument
  { name :: Text,
    id :: Int
  }

instrumentDB :: [Instrument]
instrumentDB =
  [ Instrument "Vocals" 0,
    Instrument "Synthesizer" 1,
    Instrument "Guitar" 2,
    Instrument "Bass Guitar" 3,
    Instrument "Drums" 4
  ]

newtype Query a = Query (Identity a) deriving (Functor, Applicative, Monad)

runQuery :: Query a -> a
runQuery (Query a) = runIdentity a

getInstruments :: Query [Instrument]
getInstruments = pure instrumentDB

lookupOneToMany :: (Ord b) => (a -> b) -> [a] -> Set b -> Map b [a]
lookupOneToMany x ys xs =
  foldMap
    (\x' -> M.singleton x' (filter (\y -> x y == x') ys))
    (toList xs)

lookupOneToOne :: (Ord b) => (a -> b) -> [a] -> Set b -> Map b a
lookupOneToOne x ys xs =
  foldMap
    (\y -> M.singleton (x y) y)
    (filter (\y -> x y `elem` xs) ys)

getBandMembersByInstrumentID :: Set Int -> Query (Map Int [Member])
getBandMembersByInstrumentID = pure . lookupOneToMany instrumentID memberDB

getBandMembersByBandID :: Set Int -> Query (Map Int [Member])
getBandMembersByBandID = pure . lookupOneToMany bandID memberDB

getBandsByID :: Set Int -> Query (Map Int Band)
getBandsByID = pure . lookupOneToOne (\Band {id = i} -> i) bandDB
