-- | @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 (ProjectBranch (..))
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 (ProjectBranchName (..), ProjectName (..))
import Unison.Prelude
import Unison.Project (ProjectAndBranch (..))
import Unison.Sqlite qualified as Sqlite

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

  Project
projectToDelete <-
    ((forall void. Output -> Transaction void) -> Transaction Project)
-> Cli Project
forall a.
((forall void. Output -> Transaction void) -> Transaction a)
-> Cli a
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)

  Bool -> Cli () -> Cli ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Project
projectToDelete.projectId ProjectId -> ProjectId -> Bool
forall a. Eq a => a -> a -> Bool
== Project
currentProject.projectId) do
    ProjectAndBranch ProjectId ProjectBranchId
nextLoc <- Transaction (ProjectAndBranch ProjectId ProjectBranchId)
-> Cli (ProjectAndBranch ProjectId ProjectBranchId)
forall a. Transaction a -> Cli a
Cli.runTransaction (Transaction (ProjectAndBranch ProjectId ProjectBranchId)
 -> Cli (ProjectAndBranch ProjectId ProjectBranchId))
-> Transaction (ProjectAndBranch ProjectId ProjectBranchId)
-> Cli (ProjectAndBranch ProjectId ProjectBranchId)
forall a b. (a -> b) -> a -> b
$ ProjectId
-> Transaction (Maybe (ProjectAndBranch ProjectId ProjectBranchId))
findAnyBranchInCodebaseNotInProject (Project
projectToDelete.projectId) Transaction (Maybe (ProjectAndBranch ProjectId ProjectBranchId))
-> Transaction (ProjectAndBranch ProjectId ProjectBranchId)
-> Transaction (ProjectAndBranch ProjectId ProjectBranchId)
forall (m :: * -> *) a. Monad m => m (Maybe a) -> m a -> m a
`whenNothingM` ProjectName
-> Transaction (ProjectAndBranch ProjectId ProjectBranchId)
createDummyProjectExcept Project
projectToDelete.name
    ProjectAndBranch ProjectId ProjectBranchId -> Cli ()
Cli.switchProject ProjectAndBranch ProjectId ProjectBranchId
nextLoc

  Transaction () -> Cli ()
forall a. Transaction a -> Cli a
Cli.runTransaction do
    ProjectId -> Transaction ()
Queries.deleteProject (Project
projectToDelete Project -> Getting ProjectId Project ProjectId -> ProjectId
forall s a. s -> Getting a s a -> a
^. Getting ProjectId Project ProjectId
#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
      (CausalHash
_, CausalHashId
emptyCausalHashId) <- Transaction (CausalHash, CausalHashId)
Codebase.emptyCausalHash
      ProjectName
-> ProjectBranchName
-> CausalHashId
-> Transaction (Project, ProjectBranch)
Ops.insertProjectAndBranch (Text -> ProjectName
UnsafeProjectName Text
"scratch2") (Text -> ProjectBranchName
UnsafeProjectBranchName Text
"main") CausalHashId
emptyCausalHashId
        Transaction (Project, ProjectBranch)
-> ((Project, ProjectBranch)
    -> ProjectAndBranch ProjectId ProjectBranchId)
-> Transaction (ProjectAndBranch ProjectId ProjectBranchId)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(Project
proj, ProjectBranch
branch) -> ProjectId
-> ProjectBranchId -> ProjectAndBranch ProjectId ProjectBranchId
forall a b. a -> b -> ProjectAndBranch a b
ProjectAndBranch Project
proj.projectId ProjectBranch
branch.branchId
    createDummyProjectExcept ProjectName
_ = do
      (CausalHash
_, CausalHashId
emptyCausalHashId) <- Transaction (CausalHash, CausalHashId)
Codebase.emptyCausalHash
      ProjectName
-> ProjectBranchName
-> CausalHashId
-> Transaction (Project, ProjectBranch)
Ops.insertProjectAndBranch (Text -> ProjectName
UnsafeProjectName Text
"scratch") (Text -> ProjectBranchName
UnsafeProjectBranchName Text
"main") CausalHashId
emptyCausalHashId
        Transaction (Project, ProjectBranch)
-> ((Project, ProjectBranch)
    -> ProjectAndBranch ProjectId ProjectBranchId)
-> Transaction (ProjectAndBranch ProjectId ProjectBranchId)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(Project
proj, ProjectBranch
branch) -> ProjectId
-> ProjectBranchId -> ProjectAndBranch ProjectId ProjectBranchId
forall a b. a -> b -> ProjectAndBranch a b
ProjectAndBranch Project
proj.projectId ProjectBranch
branch.branchId