{-|
Module      : RatingChgkInfo.Api
Description : Функции для работы с API сайта рейтинга
Copyright   : (c) Mansur Ziiatdinov, 2018-2019
License     : BSD-3
Maintainer  : chgk@pm.me
Stability   : experimental
Portability : POSIX

Функции здесь повторяют те запросы, которые доступны в API сайта рейтинга. Те
функции, которые недоступны через API, реализованы в 'RatingChgkInfo.NoApi'.

Кроме того, есть вспомогательная функция 'getAllItems' для удобства.
-}
module RatingChgkInfo.Api
  ( -- * Работа с API
    RatingClient
  , runRatingApi
    -- * Информация об игроках
  , players
  , player
    -- ** О командах игроков
  , playerTeams
  , playerLastTeam
  , playerTeam
    -- ** О турнирах игроков
  , playerTournaments
  , playerLastTournament
  , playerTournament
    -- ** О рейтингах игроков
  , playerRatings
  , playerLastRating
  , playerRating
    -- * Информация о командах
  , teams
  , team
    -- ** Базовые составы
  , teamBaseRecaps
  , teamLastBaseRecap
  , teamBaseRecap
    -- ** Отыгранные турниры
  , teamTournaments
  , teamLastTournament
  , teamTournament
    -- ** Рейтинги
  , teamRatings
  , teamRatingA
  , teamRatingB
  , teamRating
    -- * Информация о турнирах
  , tournaments
  , tournament
    -- ** Результаты турнира
  , tournamentResults
  , tournamentResultsTown
  , tournamentResultsRegion
  , tournamentResultsCountry
  , tournamentTeamResult
    -- ** Составы на турнире
  , tournamentRecaps
  , tournamentRecap
    -- ** Спорные и апелляции
  , tournamentControversials
  , tournamentAppeals
    -- * Различные способы поиска
    --
    -- $maybeParams
  , teamSearch
  , playerSearch
  , tournamentSearch
    -- * Вспомогательные функции
  , getAllItems
  ) where

import RatingChgkInfo.Types

import Network.HTTP.Client (newManager)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Servant.API
import Servant.Client

api :: Proxy RatingApi
api = Proxy

-- | Список всех игроков
--
-- Запрос @\/players@
players :: Maybe Int            -- ^ Номер страницы в результате
        -> RatingClient (Items Player) -- ^ Список игроков, по 1000 элементов

-- | Информация об игроке
--
-- __API NOTE__. Результат должен быть 'Player', а не список игроков из одного элемента
--
-- Запрос @\/players\/:id@
player :: PlayerId                   -- ^ Идентификатор игрока
       -> RatingClient [Player] -- ^ Информация об игроке, список из единственного элемента

-- | Команды, в базовых составах которых играл игрок
--
-- Запрос @\/players\/:id\/teams@
playerTeams :: PlayerId              -- ^ Идентификатор игрока
            -> RatingClient [PlayerTeam] -- ^ Список команд игрока

-- | Команды, в базовый состав которых игрок входит в текущем сезоне
--
-- Запрос @\/players\/:id\/teams\/last@
playerLastTeam :: PlayerId           -- ^ Идентификатор игрока
               -> RatingClient [PlayerTeam] -- ^ Список команд игрока

-- | Команды, в базовый состав которых игрок входил в указанном сезоне
--
-- Запрос @\/players\/:id\/teams\/:season@
playerTeam :: PlayerId               -- ^ Идентификатор игрока
           -> Int               -- ^ Идентификатор сезона
           -> RatingClient [PlayerTeam] -- ^ Список команд игрока

-- | Турниры, которые отыграл игрок, по сезонам
--
-- Запрос @\/players\/:id\/tournaments@
playerTournaments :: PlayerId        -- ^ Идентификатор игрока
                  -> RatingClient (SeasonMap PlayerSeason) -- ^ Турниры игрока по сезонам

-- | Турниры, которые игрок отыграл в текущем сезоне
--
-- Запрос @\/players\/:id\/tournaments\/last@
playerLastTournament :: PlayerId     -- ^ Идентификатор игрока
                     -> RatingClient PlayerSeason

-- | Турниры, которые игрок отыграл в указанном сезоне
--
-- Запрос @\/players\/:id\/tournaments\/:season@
playerTournament :: PlayerId         -- ^ Идентификатор игрока
                 -> Int         -- ^ Идентификатор сезона
                 -> RatingClient PlayerSeason

-- | Рейтинги игрока
--
-- Запрос @\/players\/:id\/rating@
playerRatings :: PlayerId            -- ^ Идентификатор игрока
              -> RatingClient [PlayerRating] -- ^ Список рейтингов игрока, порядок не определён

-- | Рейтинг игрока в последнем релизе
--
-- __API NOTE__. Работает не всегда: например, для 54345 в декабре 2018 ничего не возвращало (off-by-one error?)
--
-- Запрос @\/players\/:id\/rating\/last@
playerLastRating :: PlayerId         -- ^ Идентификатор игрока
                 -> RatingClient PlayerRating

-- | Рейтинг игрока в указанном релизе
--
-- Запрос @\/players\/:id\/rating\/:release@
playerRating :: PlayerId             -- ^ Идентификатор игрока
             -> Int             -- ^ Идентификатор релиза
             -> RatingClient PlayerRating

-- | Список всех команд
--
-- Запрос @\/teams@
teams :: Maybe Int              -- ^ Номер страницы в результате
      -> RatingClient (Items Team) -- ^ Команды по 1000 элементов

-- | Информация о команде
--
-- __API NOTE__: должна быть команда, а не список из команд
--
-- Запрос @\/teams\/:id@
team :: TeamId                     -- ^ Идентификатор команды
     -> RatingClient [Team]     -- ^ Команда, список из единственного элемента

-- | Базовые составы команды
--
-- Запрос @\/teams\/:id\/recaps@
teamBaseRecaps :: TeamId           -- ^ Идентификатор команды
               -> RatingClient (SeasonMap TeamBaseRecap) -- ^ Базовые составы по сезонам

-- | Базовый состав команды в последнем сезоне
--
-- Запрос @\/teams\/:id\/recaps\/last@
teamLastBaseRecap :: TeamId        -- ^ Идентификатор команды
                  -> RatingClient TeamBaseRecap

-- | Базовый состав команды в указанном сезоне
--
-- Запрос @\/teams\/:id\/recaps\/:season@
teamBaseRecap :: TeamId            -- ^ Идентификатор команды
              -> Int            -- ^ Идентификатор сезона
              -> RatingClient TeamBaseRecap

-- | Турниры, отыгранные командой
--
-- Запрос @\/teams\/:id\/tournaments@
teamTournaments :: TeamId          -- ^ Идентификатор команды
                -> RatingClient (SeasonMap TeamTournament) -- ^ Турниры команды по сезонам

-- | Турниры, отыгранные командой в последнем сезоне
--
-- Запрос @\/teams\/:id\/tournaments\/last@
teamLastTournament :: TeamId       -- ^ Идентификатор команды
                   -> RatingClient TeamTournament

-- | Турниры, отыгранные командой в указанном сезоне
--
-- Запрос @\/teams\/:id\/tournaments\/:season@
teamTournament :: TeamId           -- ^ Идентификатор команды
               -> Int           -- ^ Идентификатор сезона
               -> RatingClient TeamTournament

-- | Рейтинги команды
--
-- Запрос @\/teams\/:id\/rating@
teamRatings :: TeamId              -- ^ Идентификатор команды
            -> RatingClient [TeamRating] -- ^ Список рейтингов команды, порядок не определён

-- | Последний рейтинг команды по формуле A
--
-- __API NOTE__. Работает не всегда: для 1 ничего не возвращает (off-by-one error?)
--
-- Запрос @\/teams\/:id\/rating\/a@
teamRatingA :: TeamId              -- ^ Идентификатор команды
            -> RatingClient TeamRating

-- | Последний рейтинг команды по формуле B
--
-- __API NOTE__. Работает не всегда: для 1 ничего не возвращает (off-by-one error?)
--
-- Запрос @\/teams\/:id\/rating\/b@
teamRatingB :: TeamId              -- ^ Идентификатор команды
            -> RatingClient TeamRating

-- | Рейтинг команды в указанном релизе
--
-- Запрос @\/teams\/:id\/rating\/:release@
teamRating :: TeamId               -- ^ Идентификатор команды
           -> Int               -- ^ Идентификатор релиза
           -> RatingClient TeamRating

-- | Список всех турниров
--
-- Запрос @\/tournaments@
tournaments :: Maybe Int        -- ^ Номер страницы (@page@) в результате
            -> RatingClient (Items TournamentShort) -- ^ Информация о турнирах по 1000 элементов

-- | Информация о турнире
--
-- __API NOTE__: должен быть турнир, а не список турниров
--
-- Запрос @\/tournaments\/:id@
tournament :: TournamentId               -- ^ Идентификатор турнира
           -> RatingClient [Tournament] -- ^ Единственный элемент списка - турнир

-- | Результаты турнира
--
-- Запрос @\/tournaments\/:id\/list@
tournamentResults :: TournamentId        -- ^ Идентификатор турнира
                  -> RatingClient [TournamentResult] -- ^ Результаты по командам, порядок не определён

-- | Результаты турнира для команд города
--
-- Запрос @\/tournaments\/:id\/list\/town\/:town@
tournamentResultsTown :: TournamentId        -- ^ Идентификатор турнира
                      -> Int        -- ^ Идентификатор города
                      -> RatingClient [TournamentResult] -- ^ Результаты по командам, порядок не определён

-- | Результаты турнира для команд региона
--
-- Запрос @\/tournaments\/:id\/list\/region\/:region@
tournamentResultsRegion :: TournamentId        -- ^ Идентификатор турнира
                        -> Int        -- ^ Идентификатор региона
                        -> RatingClient [TournamentResult] -- ^ Результаты по командам, порядок не определён

-- | Результаты турнира для команд страны
--
-- Запрос @\/tournaments\/:id\/list\/country\/:country@
tournamentResultsCountry :: TournamentId        -- ^ Идентификатор турнира
                         -> Int        -- ^ Идентификатор страны
                         -> RatingClient [TournamentResult] -- ^ Результаты по командам, порядок не определён

-- | Составы команд на турнире
--
-- Запрос @\/tournaments\/:id\/recaps@
--
-- @since 0.3.6.4
tournamentRecaps :: TournamentId -- ^ Идентификаторр турнира
                 -> RatingClient [RecapTeam]

-- | Составы указанной команды на турнире
--
-- Запрос @\/tournaments\/:id\/recaps\/:team@
tournamentRecap :: TournamentId          -- ^ Идентификатор турнира
                -> TeamId          -- ^ Идентификатор команды
                -> RatingClient [RecapPlayer] -- ^ Список игроков с флагами К\/Б\/Л

-- | Результат указанной команды на турнире
--
-- Запрос @\/tournaments\/:id\/results\/:team@
tournamentTeamResult :: TournamentId     -- ^ Идентификатор турнира
                     -> TeamId     -- ^ Идентификатор команды
                     -> RatingClient [TourResult] -- ^ Список результатов по турам

-- | Спорные на турнире
--
-- Запрос @\/tournaments\/:id\/controversials@
--
-- @since 0.3.6.3
tournamentControversials :: TournamentId -- ^ Идентификатор турнира
                         -> RatingClient [Controversial] -- ^ Спорные

-- | Апелляции на турнире
--
-- Запрос @\/tournaments\/:id\/appeals@
--
-- @since 0.3.6.3
tournamentAppeals :: TournamentId -- ^ Идентификатор турнира
                  -> RatingClient [Appeal] -- ^ Апелляции

-- $maybeParams
--
-- В функции поиска передаётся несколько значений типа 'Maybe a'. Если
-- соответствующее значение установлено, по этому параметру производится поиск.
-- Если установлено несколько значений, они объединяются при помощи логической
-- связки И.

-- | Поиск по командам
--
-- Запрос @\/teams\/search@
teamSearch :: Maybe Text        -- ^ Название (name)
           -> Maybe Text        -- ^ Город (town)
           -> Maybe Text        -- ^ Регион (region_name)
           -> Maybe Text        -- ^ Страна (country_name)
           -> Bool              -- ^ Играла в текущем сезоне (active_this_season)
           -> Maybe Int         -- ^ Номер страницы в результате
           -> RatingClient (Items Team)     -- ^ Список команд по 1000 элементов

-- | Поиск по игрокам
--
-- Запрос @\/players\/search@
playerSearch :: Maybe Text      -- ^ Фамилия
             -> Maybe Text      -- ^ Имя
             -> Maybe Text      -- ^ Отчество
             -> Maybe Int       -- ^ Номер страницы в результате
             -> RatingClient (Items Player) -- ^ Список игроков по 1000 элементов

-- | Поиск по турнирам
--
-- Запрос @\/tournaments\/search@
tournamentSearch :: Maybe TournamentType -- ^ Тип турнира
                 -> Maybe Int            -- ^ Находится ли турнир в архиве (0 - не находится, 1 - находится)
                 -> Maybe Int            -- ^ Номер страницы в результате
                 -> RatingClient (Items TournamentShort)

players :<|> player :<|> playerTeams :<|> playerLastTeam :<|> playerTeam :<|> playerTournaments :<|> playerLastTournament :<|> playerTournament :<|> playerRatings :<|> playerLastRating :<|> playerRating :<|> teams :<|> team :<|> teamBaseRecaps :<|> teamLastBaseRecap :<|> teamBaseRecap :<|> teamTournaments :<|> teamLastTournament :<|> teamTournament :<|> teamRatings :<|> teamRatingA :<|> teamRatingB :<|> teamRating :<|> tournaments :<|> tournament :<|> tournamentResults :<|> tournamentResultsTown :<|> tournamentResultsRegion :<|> tournamentResultsCountry :<|> tournamentRecaps :<|> tournamentRecap :<|> tournamentTeamResult :<|> tournamentControversials :<|> tournamentAppeals :<|> teamSearch :<|> playerSearch :<|> tournamentSearch = client api

-- | Получение всех элементов из запроса с разбиением по страницам
--
-- В функции предполагается, что сайт рейтинга, как и указано в документации,
-- разбивает ответ на куски по 1000 элементов
getAllItems :: (Maybe Int -> RatingClient (Items a)) -- ^ Запрос с разбиением на страницы
            -> RatingClient [a]                      -- ^ Список всех элементов
getAllItems getter = do
  Items cnt is0 <- getter Nothing
  let lastPage = ceiling (fromIntegral cnt / 1000 :: Double)
  iss <- forM [2..lastPage] $ fmap items . getter . Just
  pure $ concat $ is0:iss

-- | Запуск клиента API сайта рейтинга
--
-- Все запросы внутри 'RatingClient' используют один и тот же менеджер соединений
runRatingApi :: RatingClient a -- ^ Набор команд, работающих с API сайта рейтинга
             -> IO (Either ClientError a) -- ^ Результат работы, либо ошибка
runRatingApi act = do
  mgr <- newManager tlsManagerSettings
  runClientM act $ mkClientEnv mgr $ BaseUrl Https "rating.chgk.info" 443 "api"