-- | @delete.project@ input handler
module Unison.Codebase.Editor.HandleInput.DeleteProject
  ( handleDeleteProject,
  )
where

import Control.Lens
import Data.List qualified as List
import U.Codebase.Sqlite.DbId
import U.Codebase.Sqlite.Project (Project (..))
import U.Codebase.Sqlite.ProjectBranch (ProjectBranchRow (..))
import U.Codebase.Sqlite.Queries qualified as Queries
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.MonadUtils qualified as Cli
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Editor.Output qualified as Output
import Unison.Codebase.ProjectPath (ProjectPathG (..))
import Unison.Codebase.SqliteCodebase.Operations qualified as Ops
import Unison.Core.Project (ProjectName (..))
import Unison.Prelude
import Unison.Project (ProjectAndBranch (..), defaultBranchName)
import Unison.Sqlite qualified as Sqlite

-- | Delete a project
handleDeleteProject :: ProjectName -> Cli ()
handleDeleteProject :: ProjectName -> Cli ()
handleDeleteProject ProjectName
projectName = do
  ProjectPath currentProject _ _ <- Cli (ProjectPathG Project ProjectBranch)
Cli.getCurrentProjectPath

  projectToDelete <-
    Cli.runTransactionWithRollback \forall void. Output -> Transaction void
rollback -> do
      ProjectName -> Transaction (Maybe Project)
Queries.loadProjectByName ProjectName
projectName Transaction (Maybe Project)
-> (Transaction (Maybe Project) -> Transaction Project)
-> Transaction Project
forall a b. a -> (a -> b) -> b
& Transaction Project
-> Transaction (Maybe Project) -> Transaction Project
forall (m :: * -> *) a. Monad m => m a -> m (Maybe a) -> m a
onNothingM do
        Output -> Transaction Project
forall void. Output -> Transaction void
rollback (ProjectName -> Output
Output.LocalProjectDoesntExist ProjectName
projectName)

  when (projectToDelete.projectId == currentProject.projectId) do
    nextLoc <- Cli.runTransaction $ findAnyBranchInCodebaseNotInProject (projectToDelete.projectId) `whenNothingM` createDummyProjectExcept projectToDelete.name
    Cli.switchProject nextLoc

  Cli.runTransaction do
    Queries.deleteProject (projectToDelete ^. #projectId)
  where
    findAnyBranchInCodebaseNotInProject :: ProjectId -> Sqlite.Transaction (Maybe (ProjectAndBranch ProjectId ProjectBranchId))
    findAnyBranchInCodebaseNotInProject :: ProjectId
-> Transaction (Maybe (ProjectAndBranch ProjectId ProjectBranchId))
findAnyBranchInCodebaseNotInProject ProjectId
exceptProjectId = do
      Transaction
  [(ProjectAndBranch ProjectName ProjectBranchName,
    ProjectAndBranch ProjectId ProjectBranchId)]
Queries.loadAllProjectBranchNamePairs
        Transaction
  [(ProjectAndBranch ProjectName ProjectBranchName,
    ProjectAndBranch ProjectId ProjectBranchId)]
-> ([(ProjectAndBranch ProjectName ProjectBranchName,
      ProjectAndBranch ProjectId ProjectBranchId)]
    -> Maybe
         (ProjectAndBranch ProjectName ProjectBranchName,
          ProjectAndBranch ProjectId ProjectBranchId))
-> Transaction
     (Maybe
        (ProjectAndBranch ProjectName ProjectBranchName,
         ProjectAndBranch ProjectId ProjectBranchId))
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> ((ProjectAndBranch ProjectName ProjectBranchName,
  ProjectAndBranch ProjectId ProjectBranchId)
 -> Bool)
-> [(ProjectAndBranch ProjectName ProjectBranchName,
     ProjectAndBranch ProjectId ProjectBranchId)]
-> Maybe
     (ProjectAndBranch ProjectName ProjectBranchName,
      ProjectAndBranch ProjectId ProjectBranchId)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
List.find (\(ProjectAndBranch ProjectName ProjectBranchName
_, ProjectAndBranch ProjectId
projId ProjectBranchId
_) -> ProjectId
projId ProjectId -> ProjectId -> Bool
forall a. Eq a => a -> a -> Bool
/= ProjectId
exceptProjectId)
        Transaction
  (Maybe
     (ProjectAndBranch ProjectName ProjectBranchName,
      ProjectAndBranch ProjectId ProjectBranchId))
-> (Maybe
      (ProjectAndBranch ProjectName ProjectBranchName,
       ProjectAndBranch ProjectId ProjectBranchId)
    -> Maybe (ProjectAndBranch ProjectId ProjectBranchId))
-> Transaction (Maybe (ProjectAndBranch ProjectId ProjectBranchId))
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> ((ProjectAndBranch ProjectName ProjectBranchName,
  ProjectAndBranch ProjectId ProjectBranchId)
 -> ProjectAndBranch ProjectId ProjectBranchId)
-> Maybe
     (ProjectAndBranch ProjectName ProjectBranchName,
      ProjectAndBranch ProjectId ProjectBranchId)
-> Maybe (ProjectAndBranch ProjectId ProjectBranchId)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap \(ProjectAndBranch ProjectName ProjectBranchName
_, ProjectAndBranch ProjectId ProjectBranchId
pbIds) -> ProjectAndBranch ProjectId ProjectBranchId
pbIds

    createDummyProjectExcept :: ProjectName -> Sqlite.Transaction (ProjectAndBranch ProjectId ProjectBranchId)
    createDummyProjectExcept :: ProjectName
-> Transaction (ProjectAndBranch ProjectId ProjectBranchId)
createDummyProjectExcept (UnsafeProjectName Text
"scratch") = do
      (_, emptyCausalHashId) <- Transaction (CausalHash, CausalHashId)
Codebase.emptyCausalHash
      Ops.insertProjectAndBranch (UnsafeProjectName "scratch2") defaultBranchName emptyCausalHashId
        <&> \(Project
proj, ProjectBranchRow
branch) -> ProjectId
-> ProjectBranchId -> ProjectAndBranch ProjectId ProjectBranchId
forall a b. a -> b -> ProjectAndBranch a b
ProjectAndBranch Project
proj.projectId ProjectBranchRow
branch.branchId
    createDummyProjectExcept ProjectName
_ = do
      (_, emptyCausalHashId) <- Transaction (CausalHash, CausalHashId)
Codebase.emptyCausalHash
      Ops.insertProjectAndBranch (UnsafeProjectName "scratch") defaultBranchName emptyCausalHashId
        <&> \(Project
proj, ProjectBranchRow
branch) -> ProjectId
-> ProjectBranchId -> ProjectAndBranch ProjectId ProjectBranchId
forall a b. a -> b -> ProjectAndBranch a b
ProjectAndBranch Project
proj.projectId ProjectBranchRow
branch.branchId