{-

This file is part of the vimeta package. It is subject to the license
terms in the LICENSE file found in the top-level directory of this
distribution and at git://pmade.com/vimeta/LICENSE. No part of the
vimeta package, including this file, may be copied, modified,
propagated, or distributed except according to the terms contained in
the LICENSE file.

-}

-- | Search for a movie and interact with the user through the terminal.
module Vimeta.UI.Term.Movie
  ( movieSearch,
  )
where

import Byline.Menu
import Network.API.TheMovieDB
import Vimeta.Core
import Vimeta.UI.Common.Util
import Vimeta.UI.Term.Common

-- | A wrapper around a movie.
newtype MovieItem = MovieItem Movie

instance ToStylizedText MovieItem where
  toStylizedText :: MovieItem -> Stylized Text
toStylizedText (MovieItem Movie
m) =
    [Stylized Text] -> Stylized Text
forall a. Monoid a => [a] -> a
mconcat
      [ Text -> Stylized Text
text (Movie -> Text
movieTitle Movie
m),
        Text -> Stylized Text
text (Text -> Text
parens (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ Maybe Day -> Text
dayAsYear (Maybe Day -> Text) -> Maybe Day -> Text
forall a b. (a -> b) -> a -> b
$ Movie -> Maybe Day
movieReleaseDate Movie
m)
      ]

-- | Search for a movie and interact with the user through the terminal.
movieSearch :: MonadIO m => Text -> Vimeta m Movie
movieSearch :: Text -> Vimeta m Movie
movieSearch Text
initial = do
  Text
name <- Stylized Text
-> Maybe Text
-> (Text -> Vimeta m (Either (Stylized Text) Text))
-> Vimeta m Text
forall (m :: * -> *) a e b.
(MonadByline m, ToStylizedText a, ToStylizedText e) =>
a -> Maybe Text -> (Text -> m (Either e b)) -> m b
askUntil Stylized Text
searchPrompt (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
initial) (Either (Stylized Text) Text
-> Vimeta m (Either (Stylized Text) Text)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (Stylized Text) Text
 -> Vimeta m (Either (Stylized Text) Text))
-> (Text -> Either (Stylized Text) Text)
-> Text
-> Vimeta m (Either (Stylized Text) Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Stylized Text -> Text -> Either (Stylized Text) Text
notBlank Stylized Text
searchErr)
  NonEmpty MovieItem
movies <-
    TheMovieDB [Movie] -> Vimeta m [Movie]
forall (m :: * -> *) a. MonadIO m => TheMovieDB a -> Vimeta m a
tmdb (Text -> TheMovieDB [Movie]
searchMovies Text
name)
      Vimeta m [Movie]
-> ([Movie] -> Vimeta m (NonEmpty MovieItem))
-> Vimeta m (NonEmpty MovieItem)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ( [Movie] -> Maybe (NonEmpty Movie)
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty ([Movie] -> Maybe (NonEmpty Movie))
-> (Maybe (NonEmpty Movie) -> Vimeta m (NonEmpty MovieItem))
-> [Movie]
-> Vimeta m (NonEmpty MovieItem)
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> \case
              Maybe (NonEmpty Movie)
Nothing -> String -> Vimeta m (NonEmpty MovieItem)
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (String
"no matches for: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
forall a. ToString a => a -> String
toString Text
name)
              Just NonEmpty Movie
ms -> NonEmpty MovieItem -> Vimeta m (NonEmpty MovieItem)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Movie -> MovieItem
MovieItem (Movie -> MovieItem) -> NonEmpty Movie -> NonEmpty MovieItem
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty Movie
ms)
          )
  MovieItem Movie
movie <- Menu MovieItem
-> Stylized Text -> Stylized Text -> Vimeta m MovieItem
forall (m :: * -> *) a b e.
(MonadByline m, ToStylizedText a, ToStylizedText b,
 ToStylizedText e) =>
Menu a -> b -> e -> m a
askWithMenuRepeatedly (NonEmpty MovieItem -> Menu MovieItem
forall a. ToStylizedText a => NonEmpty a -> Menu a
mkMenu NonEmpty MovieItem
movies) Stylized Text
prompt Stylized Text
eprompt
  Movie -> Vimeta m ()
forall (m :: * -> *). MonadIO m => Movie -> Vimeta m ()
logID Movie
movie Vimeta m () -> Vimeta m Movie -> Vimeta m Movie
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> TheMovieDB Movie -> Vimeta m Movie
forall (m :: * -> *) a. MonadIO m => TheMovieDB a -> Vimeta m a
tmdb (ItemID -> TheMovieDB Movie
fetchMovie (Movie -> ItemID
movieID Movie
movie))
  where
    -- The Menu.
    mkMenu :: NonEmpty a -> Menu a
mkMenu NonEmpty a
movies = Stylized Text -> Menu a -> Menu a
forall b a. ToStylizedText b => b -> Menu a -> Menu a
menuBanner (Text -> Stylized Text
text Text
"Choose a movie:") (NonEmpty a -> Menu a
forall a. ToStylizedText a => NonEmpty a -> Menu a
menu NonEmpty a
movies)
    -- Search prompt.
    searchPrompt :: Stylized Text
searchPrompt = Text -> Stylized Text
text Text
"search (movie name): "
    -- Search error text.
    searchErr :: Stylized Text
searchErr = Stylized Text
"please enter a valid search term" Stylized Text -> Stylized Text -> Stylized Text
forall a. Semigroup a => a -> a -> a
<> Color -> Stylized Text
fg Color
red
    -- Menu prompt.
    prompt :: Stylized Text
prompt = Text -> Stylized Text
text Text
"Which is the correct movie? "
    -- Prompt when someone fails to pick a movie.
    eprompt :: Stylized Text
eprompt = Stylized Text
"please choose a valid movie" Stylized Text -> Stylized Text -> Stylized Text
forall a. Semigroup a => a -> a -> a
<> Color -> Stylized Text
fg Color
red
    -- Log a movie ID.
    logID :: Movie -> Vimeta m ()
logID Movie
movie =
      Text -> Vimeta m ()
forall (m :: * -> *). MonadIO m => Text -> Vimeta m ()
verbose (Text -> Vimeta m ()) -> Text -> Vimeta m ()
forall a b. (a -> b) -> a -> b
$
        Text
"using movie ID: "
          Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ItemID -> Text
forall b a. (Show a, IsString b) => a -> b
show (Movie -> ItemID
movieID Movie
movie)