{-# OPTIONS --safe #-}

module Govenv.Kernel.Roadmap where

open import Agda.Builtin.Bool using (Bool; true; false)
open import Agda.Builtin.Equality using (_≡_)
open import Agda.Builtin.List using (List; []; _∷_)
open import Agda.Builtin.Maybe using (Maybe; just; nothing)
open import Agda.Builtin.Nat using (Nat; zero; suc)
open import Agda.Builtin.String using (String)
open import Govenv.Kernel.Identifier

data ItemState : Set where
  done todo cancelled : ItemState
  superseded : GovernanceRef → ItemState

data PhaseState : Set where
  finished active future : PhaseState

data BelongsTo
  {gvIdx phaseIdx : Nat}
  {gvDescription phaseDescription : String}
  (governance : GovernanceId gvIdx gvDescription)
  (phase : PhaseId phaseIdx phaseDescription) : Set where
  belongs : BelongsTo governance phase

record GovernanceSpec : Set where
  constructor governanceSpec
  field
    {idx} : Nat
    {description} : String
    governanceId : GovernanceId idx description
    governanceState : ItemState

record Membership
  {phaseIdx : Nat}
  {phaseDescription : String}
  (phase : PhaseId phaseIdx phaseDescription) : Set where
  constructor membership
  field
    {gvIdx} : Nat
    {gvDescription} : String
    governanceId : GovernanceId gvIdx gvDescription
    governanceState : ItemState
    relation : BelongsTo governanceId phase

record PhaseNode (state : PhaseState) : Set where
  constructor phaseNode
  field
    {idx} : Nat
    {description} : String
    phaseId : PhaseId idx description
    phaseItems : List (Membership phaseId)

data Roadmap : Set where
  progressing :
    List (PhaseNode finished) →
    PhaseNode active →
    List (PhaseNode future) →
    Roadmap
  complete :
    List (PhaseNode finished) →
    Roadmap

record Forest (A : Set) : Set where
  constructor forest
  field
    nodes : List A

private
  _++_ : {A : Set} → List A → List A → List A
  [] ++ ys = ys
  (x ∷ xs) ++ ys = x ∷ (xs ++ ys)

  singleton : {A : Set} → A → Forest A
  singleton x = forest (x ∷ [])

  _and_ : Bool → Bool → Bool
  true and right = right
  false and right = false

  not : Bool → Bool
  not true = false
  not false = true

  equalNat : Nat → Nat → Bool
  equalNat zero zero = true
  equalNat zero (suc right) = false
  equalNat (suc left) zero = false
  equalNat (suc left) (suc right) = equalNat left right

  lessNat : Nat → Nat → Bool
  lessNat zero zero = false
  lessNat zero (suc right) = true
  lessNat (suc left) zero = false
  lessNat (suc left) (suc right) = lessNat left right

  containsNat : Nat → List Nat → Bool
  containsNat value [] = false
  containsNat value (x ∷ xs) with equalNat value x
  ... | true = true
  ... | false = containsNat value xs

  lookupMembership :
    {phaseIdx : Nat} {phaseDescription : String}
    {phase : PhaseId phaseIdx phaseDescription} →
    Nat → List (Membership phase) → Maybe SomeGovernanceId
  lookupMembership idx [] = nothing
  lookupMembership idx (membership governanceId state relation ∷ rest)
    with equalNat idx (indexOf governanceId)
  ... | true = just (someIdentifier governanceId)
  ... | false = lookupMembership idx rest

  lookupPhase : {state : PhaseState} → Nat → PhaseNode state → Maybe SomeGovernanceId
  lookupPhase idx (phaseNode phaseId items) = lookupMembership idx items

  lookupPhases :
    {state : PhaseState} → Nat → List (PhaseNode state) → Maybe SomeGovernanceId
  lookupPhases idx [] = nothing
  lookupPhases idx (phase ∷ rest) with lookupPhase idx phase
  ... | just governanceId = just governanceId
  ... | nothing = lookupPhases idx rest

  uniqueNats : List Nat → Bool
  uniqueNats [] = true
  uniqueNats (x ∷ xs) = not (containsNat x xs) and uniqueNats xs

  strictlyIncreasing : List Nat → Bool
  strictlyIncreasing [] = true
  strictlyIncreasing (x ∷ []) = true
  strictlyIncreasing (x ∷ y ∷ rest) =
    lessNat x y and strictlyIncreasing (y ∷ rest)

attach :
  {phaseIdx : Nat} {phaseDescription : String} →
  (phase : PhaseId phaseIdx phaseDescription) →
  List GovernanceSpec →
  List (Membership phase)
attach phase [] = []
attach phase (governanceSpec governanceId governanceState ∷ rest) =
  membership governanceId governanceState belongs ∷ attach phase rest

lookupGovernance : Nat → Roadmap → Maybe SomeGovernanceId
lookupGovernance idx (progressing finishedPhases current futurePhases)
  with lookupPhases idx finishedPhases
... | just governanceId = just governanceId
... | nothing with lookupPhase idx current
...   | just governanceId = just governanceId
...   | nothing = lookupPhases idx futurePhases
lookupGovernance idx (complete finishedPhases) =
  lookupPhases idx finishedPhases

lookupGovernanceRef : GovernanceRef → Roadmap → Maybe SomeGovernanceId
lookupGovernanceRef replacement =
  lookupGovernance (IdentifierRef.referenceIndex replacement)

governanceReferenceExists : GovernanceRef → Roadmap → Bool
governanceReferenceExists reference roadmap
  with lookupGovernanceRef reference roadmap
... | just governance = true
... | nothing = false

record GovernanceTarget (roadmap : Roadmap) : Set where
  constructor governanceTarget
  field
    reference : GovernanceRef
    validReference : governanceReferenceExists reference roadmap ≡ true

data ChainShape : Set where
  finishedOnly activeAndFuture futureOnly progressingShape invalidShape : ChainShape

data RoadmapChain : ChainShape → Set where
  finishedChain : List (PhaseNode finished) → RoadmapChain finishedOnly
  activeChain : PhaseNode active → List (PhaseNode future) → RoadmapChain activeAndFuture
  futureChain : List (PhaseNode future) → RoadmapChain futureOnly
  progressingChain :
    List (PhaseNode finished) →
    PhaseNode active →
    List (PhaseNode future) →
    RoadmapChain progressingShape
  invalidChain : RoadmapChain invalidShape

private
  membershipIndices :
    {phaseIdx : Nat} {phaseDescription : String}
    {phase : PhaseId phaseIdx phaseDescription} →
    List (Membership phase) → List Nat
  membershipIndices [] = []
  membershipIndices (membership governanceId state relation ∷ rest) =
    indexOf governanceId ∷ membershipIndices rest

  phaseIndex : {state : PhaseState} → PhaseNode state → Nat
  phaseIndex (phaseNode phaseId items) = indexOf phaseId

  phaseIndices : {state : PhaseState} → List (PhaseNode state) → List Nat
  phaseIndices [] = []
  phaseIndices (phase ∷ rest) = phaseIndex phase ∷ phaseIndices rest

  phaseGovernanceIndices :
    {state : PhaseState} → List (PhaseNode state) → List Nat
  phaseGovernanceIndices [] = []
  phaseGovernanceIndices (phaseNode phaseId items ∷ rest) =
    membershipIndices items ++ phaseGovernanceIndices rest

  itemsDone :
    {phaseIdx : Nat} {phaseDescription : String}
    {phase : PhaseId phaseIdx phaseDescription} →
    List (Membership phase) → Bool
  itemsDone [] = true
  itemsDone (membership governanceId done relation ∷ rest) = itemsDone rest
  itemsDone (membership governanceId todo relation ∷ rest) = false
  itemsDone (membership governanceId cancelled relation ∷ rest) = itemsDone rest
  itemsDone (membership governanceId (superseded replacement) relation ∷ rest) =
    itemsDone rest

  itemsPending :
    {phaseIdx : Nat} {phaseDescription : String}
    {phase : PhaseId phaseIdx phaseDescription} →
    List (Membership phase) → Bool
  itemsPending [] = false
  itemsPending (membership governanceId done relation ∷ rest) = itemsPending rest
  itemsPending (membership governanceId todo relation ∷ rest) = true
  itemsPending (membership governanceId cancelled relation ∷ rest) = itemsPending rest
  itemsPending (membership governanceId (superseded replacement) relation ∷ rest) =
    itemsPending rest

  finishedPhasesDone : List (PhaseNode finished) → Bool
  finishedPhasesDone [] = true
  finishedPhasesDone (phaseNode phaseId items ∷ rest) =
    itemsDone items and finishedPhasesDone rest

  activePhasePending : PhaseNode active → Bool
  activePhasePending (phaseNode phaseId items) = itemsPending items

  chainPhaseIndices : {shape : ChainShape} → RoadmapChain shape → List Nat
  chainPhaseIndices (finishedChain phases) = phaseIndices phases
  chainPhaseIndices (activeChain current futures) =
    phaseIndex current ∷ phaseIndices futures
  chainPhaseIndices (futureChain futures) = phaseIndices futures
  chainPhaseIndices (progressingChain finishedPhases current futures) =
    phaseIndices finishedPhases ++ (phaseIndex current ∷ phaseIndices futures)
  chainPhaseIndices invalidChain = []

  chainGovernanceIndices :
    {shape : ChainShape} → RoadmapChain shape → List Nat
  chainGovernanceIndices (finishedChain phases) =
    phaseGovernanceIndices phases
  chainGovernanceIndices (activeChain current futures) =
    phaseGovernanceIndices (current ∷ []) ++ phaseGovernanceIndices futures
  chainGovernanceIndices (futureChain futures) =
    phaseGovernanceIndices futures
  chainGovernanceIndices (progressingChain finishedPhases current futures) =
    phaseGovernanceIndices finishedPhases ++
    (phaseGovernanceIndices (current ∷ []) ++
     phaseGovernanceIndices futures)
  chainGovernanceIndices invalidChain = []

  chainFinishedPhasesDone :
    {shape : ChainShape} → RoadmapChain shape → Bool
  chainFinishedPhasesDone (finishedChain phases) = finishedPhasesDone phases
  chainFinishedPhasesDone (activeChain current futures) = true
  chainFinishedPhasesDone (futureChain futures) = true
  chainFinishedPhasesDone (progressingChain finishedPhases current futures) =
    finishedPhasesDone finishedPhases
  chainFinishedPhasesDone invalidChain = false

  chainActivePhasePending :
    {shape : ChainShape} → RoadmapChain shape → Bool
  chainActivePhasePending (finishedChain phases) = true
  chainActivePhasePending (activeChain current futures) = activePhasePending current
  chainActivePhasePending (futureChain futures) = true
  chainActivePhasePending (progressingChain finishedPhases current futures) =
    activePhasePending current
  chainActivePhasePending invalidChain = false

  supersessionsValidItems :
    {phaseIdx : Nat} {phaseDescription : String}
    {phase : PhaseId phaseIdx phaseDescription} →
    List Nat → List (Membership phase) → Bool
  supersessionsValidItems allIndices [] = true
  supersessionsValidItems allIndices
    (membership governanceId done relation ∷ rest) =
      supersessionsValidItems allIndices rest
  supersessionsValidItems allIndices
    (membership governanceId todo relation ∷ rest) =
      supersessionsValidItems allIndices rest
  supersessionsValidItems allIndices
    (membership governanceId cancelled relation ∷ rest) =
      supersessionsValidItems allIndices rest
  supersessionsValidItems allIndices
    (membership governanceId (superseded replacement) relation ∷ rest) =
      (lessNat (indexOf governanceId) (IdentifierRef.referenceIndex replacement) and
       containsNat (IdentifierRef.referenceIndex replacement) allIndices) and
      supersessionsValidItems allIndices rest

  supersessionsValidPhases :
    {state : PhaseState} →
    List Nat → List (PhaseNode state) → Bool
  supersessionsValidPhases allIndices [] = true
  supersessionsValidPhases allIndices (phaseNode phaseId items ∷ rest) =
    supersessionsValidItems allIndices items and
    supersessionsValidPhases allIndices rest

  chainSupersessionsValid :
    {shape : ChainShape} → RoadmapChain shape → Bool
  chainSupersessionsValid chain@(finishedChain phases) =
    supersessionsValidPhases (chainGovernanceIndices chain) phases
  chainSupersessionsValid chain@(activeChain current futures) =
    supersessionsValidPhases (chainGovernanceIndices chain) (current ∷ []) and
    supersessionsValidPhases (chainGovernanceIndices chain) futures
  chainSupersessionsValid chain@(futureChain futures) =
    supersessionsValidPhases (chainGovernanceIndices chain) futures
  chainSupersessionsValid chain@(progressingChain finishedPhases current futures) =
    supersessionsValidPhases (chainGovernanceIndices chain) finishedPhases and
    (supersessionsValidPhases (chainGovernanceIndices chain) (current ∷ []) and
     supersessionsValidPhases (chainGovernanceIndices chain) futures)
  chainSupersessionsValid invalidChain = false

  integrity : {shape : ChainShape} → RoadmapChain shape → Bool
  integrity chain =
    strictlyIncreasing (chainPhaseIndices chain) and
    (uniqueNats (chainGovernanceIndices chain) and
     (chainFinishedPhasesDone chain and
      (chainActivePhasePending chain and chainSupersessionsValid chain)))

  appendShape : ChainShape → ChainShape → ChainShape
  appendShape finishedOnly finishedOnly = finishedOnly
  appendShape finishedOnly activeAndFuture = progressingShape
  appendShape finishedOnly progressingShape = progressingShape
  appendShape activeAndFuture futureOnly = activeAndFuture
  appendShape futureOnly futureOnly = futureOnly
  appendShape progressingShape futureOnly = progressingShape
  appendShape _ _ = invalidShape

roadmapIntegrity : {shape : ChainShape} → RoadmapChain shape → Bool
roadmapIntegrity = integrity

infixr 5 _├_
infixr 4 _┬_
infixr 2 _╟_
infix 8 _✓ _◇ _×
infix 8 _↪_
infix 7 _■ _▣ _□

_├_ : {A : Set} → Forest A → Forest A → Forest A
forest xs ├ forest ys = forest (xs ++ ys)

_┬_ : {A B : Set} → (A → B) → A → B
f ┬ x = f x

_╟_ :
  {left right : ChainShape} →
  RoadmapChain left →
  RoadmapChain right →
  RoadmapChain (appendShape left right)
finishedChain xs ╟ finishedChain ys = finishedChain (xs ++ ys)
finishedChain xs ╟ activeChain current futures =
  progressingChain xs current futures
finishedChain xs ╟ progressingChain ys current futures =
  progressingChain (xs ++ ys) current futures
activeChain current xs ╟ futureChain ys = activeChain current (xs ++ ys)
futureChain xs ╟ futureChain ys = futureChain (xs ++ ys)
progressingChain finishedPhases current xs ╟ futureChain ys =
  progressingChain finishedPhases current (xs ++ ys)
finishedChain _ ╟ futureChain _ = invalidChain
finishedChain _ ╟ invalidChain = invalidChain
activeChain _ _ ╟ finishedChain _ = invalidChain
activeChain _ _ ╟ activeChain _ _ = invalidChain
activeChain _ _ ╟ progressingChain _ _ _ = invalidChain
activeChain _ _ ╟ invalidChain = invalidChain
futureChain _ ╟ finishedChain _ = invalidChain
futureChain _ ╟ activeChain _ _ = invalidChain
futureChain _ ╟ progressingChain _ _ _ = invalidChain
futureChain _ ╟ invalidChain = invalidChain
progressingChain _ _ _ ╟ finishedChain _ = invalidChain
progressingChain _ _ _ ╟ activeChain _ _ = invalidChain
progressingChain _ _ _ ╟ progressingChain _ _ _ = invalidChain
progressingChain _ _ _ ╟ invalidChain = invalidChain
invalidChain ╟ _ = invalidChain

_✓ :
  {idx : Nat} {description : String} →
  GovernanceId idx description →
  Forest GovernanceSpec
_✓ governanceId = singleton (governanceSpec governanceId done)

_◇ :
  {idx : Nat} {description : String} →
  GovernanceId idx description →
  Forest GovernanceSpec
_◇ governanceId = singleton (governanceSpec governanceId todo)

_× :
  {idx : Nat} {description : String} →
  GovernanceId idx description →
  Forest GovernanceSpec
_× governanceId = singleton (governanceSpec governanceId cancelled)

_↪_ :
  {idx : Nat} {description : String} →
  GovernanceId idx description →
  GovernanceRef →
  Forest GovernanceSpec
governanceId ↪ replacement =
  singleton (governanceSpec governanceId (superseded replacement))

private
  makePhase :
    {idx : Nat} {description : String} →
    (state : PhaseState) →
    PhaseId idx description →
    Forest GovernanceSpec →
    PhaseNode state
  makePhase state phaseId (forest items) =
    phaseNode phaseId (attach phaseId items)

_■ :
  {idx : Nat} {description : String} →
  PhaseId idx description →
  Forest GovernanceSpec →
  RoadmapChain finishedOnly
_■ phaseId items = finishedChain (makePhase finished phaseId items ∷ [])

_▣ :
  {idx : Nat} {description : String} →
  PhaseId idx description →
  Forest GovernanceSpec →
  RoadmapChain activeAndFuture
_▣ phaseId items = activeChain (makePhase active phaseId items) []

_□ :
  {idx : Nat} {description : String} →
  PhaseId idx description →
  Forest GovernanceSpec →
  RoadmapChain futureOnly
_□ phaseId items = futureChain (makePhase future phaseId items ∷ [])

IntegrityResult : Bool → Set
IntegrityResult true = Roadmap
IntegrityResult false = RoadmapChain invalidShape

RoadmapResult : {shape : ChainShape} → RoadmapChain shape → Set
RoadmapResult {finishedOnly} chain = IntegrityResult (integrity chain)
RoadmapResult {activeAndFuture} chain = IntegrityResult (integrity chain)
RoadmapResult {progressingShape} chain = IntegrityResult (integrity chain)
RoadmapResult {futureOnly} chain = RoadmapChain futureOnly
RoadmapResult {invalidShape} chain = RoadmapChain invalidShape

roadmapOf :
  {shape : ChainShape} →
  (chain : RoadmapChain shape) →
  RoadmapResult chain
roadmapOf chain@(finishedChain phases) with integrity chain
... | true = complete phases
... | false = invalidChain
roadmapOf chain@(activeChain current futures) with integrity chain
... | true = progressing [] current futures
... | false = invalidChain
roadmapOf chain@(progressingChain phases current futures) with integrity chain
... | true = progressing phases current futures
... | false = invalidChain
roadmapOf chain@(futureChain _) = chain
roadmapOf invalidChain = invalidChain