-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Galley.API.LegalHold.Team
  ( isLegalHoldEnabledForTeam,
    computeLegalHoldFeatureStatus,
    assertLegalHoldEnabledForTeam,
    ensureNotTooLargeToActivateLegalHold,
    teamSizeBelowLimit,
    ensureReAuthorised,
  )
where

import Data.Code qualified as Code
import Data.Id
import Data.Misc (PlainTextPassword6)
import Data.Range
import Imports
import Polysemy
import Polysemy.Error
import Polysemy.Input (Input, input)
import Wire.API.Error
import Wire.API.Error.Galley
import Wire.API.Team.Feature
import Wire.API.Team.FeatureFlags as Team (FanoutLimit, FeatureDefaults (..))
import Wire.API.Team.Size
import Wire.API.User (VerificationAction)
import Wire.API.User.Auth.ReAuth
import Wire.BrigAPIAccess
import Wire.FeaturesConfigSubsystem (FeaturesConfigSubsystem, getDbFeatureRawInternal)
import Wire.LegalHold
import Wire.LegalHoldStore (LegalHoldStore)

assertLegalHoldEnabledForTeam ::
  forall r.
  ( Member LegalHoldStore r,
    Member (Input (FeatureDefaults LegalholdConfig)) r,
    Member (ErrorS 'LegalHoldNotEnabled) r,
    Member FeaturesConfigSubsystem r
  ) =>
  TeamId ->
  Sem r ()
assertLegalHoldEnabledForTeam tid =
  unlessM (isLegalHoldEnabledForTeam tid) $
    throwS @'LegalHoldNotEnabled

isLegalHoldEnabledForTeam ::
  forall r.
  ( Member LegalHoldStore r,
    Member FeaturesConfigSubsystem r,
    Member (Input (FeatureDefaults LegalholdConfig)) r
  ) =>
  TeamId ->
  Sem r Bool
isLegalHoldEnabledForTeam tid = do
  dbFeature <- getDbFeatureRawInternal tid
  status <- computeLegalHoldFeatureStatus tid dbFeature
  pure $ status == FeatureStatusEnabled

ensureNotTooLargeToActivateLegalHold ::
  ( Member BrigAPIAccess r,
    Member (ErrorS 'CannotEnableLegalHoldServiceLargeTeam) r,
    Member (Input FanoutLimit) r,
    Member (Input (FeatureDefaults LegalholdConfig)) r
  ) =>
  TeamId ->
  Sem r ()
ensureNotTooLargeToActivateLegalHold tid = do
  teamSize <- getSize tid
  unlessM (teamSizeBelowLimit teamSize) $
    throwS @'CannotEnableLegalHoldServiceLargeTeam

teamSizeBelowLimit ::
  ( Member (Input FanoutLimit) r,
    Member (Input (FeatureDefaults LegalholdConfig)) r
  ) =>
  TeamSize ->
  Sem r Bool
teamSizeBelowLimit (fromIntegral . teamSizeTotal -> teamSize) = do
  limit :: Int <- fromIntegral . fromRange <$> input @FanoutLimit
  let withinLimit = teamSize <= limit
  featureLegalHold <- input @(FeatureDefaults LegalholdConfig)
  case featureLegalHold of
    FeatureLegalHoldDisabledPermanently -> pure withinLimit
    FeatureLegalHoldDisabledByDefault -> pure withinLimit
    FeatureLegalHoldWhitelistTeamsAndImplicitConsent ->
      -- unlimited, see docs of 'ensureNotTooLargeForLegalHold'
      pure True

ensureReAuthorised ::
  ( Member BrigAPIAccess r,
    Member (Error AuthenticationError) r
  ) =>
  UserId ->
  Maybe PlainTextPassword6 ->
  Maybe Code.Value ->
  Maybe VerificationAction ->
  Sem r ()
ensureReAuthorised u secret mbAction mbCode =
  reauthUser u (ReAuthUser secret mbAction mbCode) >>= fromEither
