import Std.Data.HashMap.Lemmas

import Provenance.AnnotatedDatabase
import Provenance.Query
import Provenance.Util.KeyAccValueList

variable {T: Type} [ValueType T] [AddCommSemigroup T] [Sub T] [Mul T]
variable {K: Type} [SemiringWithMonus K] [DecidableEq K]

def Filter.evalDecidableAnnotated (φ : Filter T n) :
  DecidablePred (λ (ta: AnnotatedTuple T K n) ↦ φ.eval ta.fst) :=
    λ t => match φ.evalDecidable t.fst with
      | isTrue h  => isTrue (by simp [h])
      | isFalse h => isFalse  (by simp [h])

def groupByKey (m : Multiset (Tuple T n × K)) :=
  m.foldr KeyValueList.addKVFold ⟨[], by simp[KeyValueList]⟩

def Query.evaluateAnnotated (q: Query T n) (hq: q.noAgg) (d: AnnotatedDatabase T K) : AnnotatedRelation T K n := match q with
| Rel   n  s  =>
  match h : d.find n s with
  | none => (∅: Multiset (AnnotatedTuple T K n))
  | some rn => rn
| @Proj _ n m ts q' =>
  let r := evaluateAnnotated q' (noAggProj hq rfl) d
  r.map (λ t ↦ ⟨λ k ↦ (ts k).eval t.fst, t.snd⟩)
| Sel   φ  q  =>
  let r := evaluateAnnotated q (noAggSel hq rfl) d
  @Multiset.filter _ (λ ta ↦ φ.eval ta.fst) φ.evalDecidableAnnotated r
| @Prod _ n₁ n₂ n hn q₁ q₂ =>
  let r₁ := evaluateAnnotated q₁ (noAggProd hq rfl).left d
  let r₂ := evaluateAnnotated q₂ (noAggProd hq rfl).right d
  Multiset.map (λ (x,y) ↦ ⟨
    Eq.mp (by simp[hn]; rfl)
    (Fin.append x.fst y.fst),
    x.snd*y.snd
  ⟩) (Multiset.product r₁ r₂)
| Sum   q₁ q₂ =>
  let r₁ := evaluateAnnotated q₁ (noAggSum hq rfl).left d
  let r₂ := evaluateAnnotated q₂ (noAggSum hq rfl).right d
  r₁+r₂
| Dedup q     =>
  let r := evaluateAnnotated q (noAggDedup hq rfl) d
  Multiset.ofList ((groupByKey r).val)
| Diff  q₁ q₂ =>
  let r₁ := evaluateAnnotated q₁ (noAggDiff hq rfl).left d
  let r₂ := evaluateAnnotated q₂ (noAggDiff hq rfl).right d
  let grouped₂ := groupByKey r₂
  r₁.map
    λ (u,α) ↦ ⟨u, α - (((grouped₂.val.find? (·.1=u)).map Prod.snd).getD 0)⟩
| Agg _ _ _ _ => False.elim (by
  simp[noAgg] at hq
)
