"""
Skill: Personal Assistant
==========================
Verify budget rules, task management, and personal data consistency.

Real-world use cases:
  - "Prove my budget categories add up correctly when merged"  
  - "Verify combining two to-do lists doesn't lose any tasks"
  - "Show that splitting and re-merging a task list is safe"

Types:
  Budget   — NoBudget | Item(category, amount, rest)
  TaskList — NoTasks  | Task(name, priority, rest)

Functions:
  total_budget(NoBudget)          = Z
  total_budget(Item(cat, amt, r)) = add(amt, total_budget(r))

  item_count(NoBudget)            = Z
  item_count(Item(cat, amt, r))   = S(item_count(r))

  merge_budget(NoBudget, b)            = b
  merge_budget(Item(cat, amt, r), b)   = Item(cat, amt, merge_budget(r, b))

  task_count(NoTasks)             = Z
  task_count(Task(n, p, r))       = S(task_count(r))

  merge_tasks(NoTasks, tl)             = tl
  merge_tasks(Task(n, p, r), tl)       = Task(n, p, merge_tasks(r, tl))
"""

from nexus_gate_pkg.engine.skills import Skill, TheoremDef, register_skill
from nexus_gate_pkg.engine.terms import Var, Const, App, RewriteRule, Equation
from nexus_gate_pkg.engine.proof import register_induction_scheme
from nexus_gate_pkg.corpus.domain import Z, S, add


# ─── Budget ──────────────────────────────────────────────

def NoBudget():
    return Const("NoBudget")

def BItem(cat, amt, rest):
    return App("BItem", [cat, amt, rest])

def total_budget(b):
    return App("total_budget", [b])

def item_count(b):
    return App("item_count", [b])

def merge_budget(b1, b2):
    return App("merge_budget", [b1, b2])


# ─── Tasks ───────────────────────────────────────────────

def NoTasks():
    return Const("NoTasks")

def Task(name, prio, rest):
    return App("Task", [name, prio, rest])

def task_count(tl):
    return App("task_count", [tl])

def merge_tasks(tl1, tl2):
    return App("merge_tasks", [tl1, tl2])


# ─── Variables ───────────────────────────────────────────

b, b2, b3 = Var("b"), Var("b2"), Var("b3")
tl, tl2, tl3 = Var("tl"), Var("tl2"), Var("tl3")
cat, amt, nm, pri, rest = Var("cat"), Var("amt"), Var("nm"), Var("pri"), Var("rest")


ASSISTANT_DEFS = [
    # Budget
    RewriteRule("total_budget_empty", total_budget(NoBudget()), Z()),
    RewriteRule("total_budget_item", total_budget(BItem(cat, amt, rest)),
                add(amt, total_budget(rest))),
    RewriteRule("item_count_empty", item_count(NoBudget()), Z()),
    RewriteRule("item_count_item", item_count(BItem(cat, amt, rest)),
                S(item_count(rest))),
    RewriteRule("merge_budget_empty", merge_budget(NoBudget(), b), b),
    RewriteRule("merge_budget_item", merge_budget(BItem(cat, amt, rest), b),
                BItem(cat, amt, merge_budget(rest, b))),
    # Tasks
    RewriteRule("task_count_empty", task_count(NoTasks()), Z()),
    RewriteRule("task_count_task", task_count(Task(nm, pri, rest)),
                S(task_count(rest))),
    RewriteRule("merge_tasks_empty", merge_tasks(NoTasks(), tl), tl),
    RewriteRule("merge_tasks_task", merge_tasks(Task(nm, pri, rest), tl),
                Task(nm, pri, merge_tasks(rest, tl))),
]


def _thm(name, desc, lhs, rhs, ctx=None, diff="medium", tags=None):
    return TheoremDef(name, desc, lhs, rhs, context=ctx or [],
                      difficulty=diff, tags=tags or ["assistant"])


assistant_skill = Skill(
    id="assistant",
    name="Personal Assistant",
    description="Verify budgets, task lists, and personal data rules",
    version="1.0.0",
    definitions=ASSISTANT_DEFS,
    theorems=[
        # Budget
        _thm("empty_budget_zero",
             "A fresh budget starts at zero — verified clean state",
             total_budget(NoBudget()), Z(), diff="trivial",
             tags=["budget"]),

        _thm("merge_budget_total",
             "Combined budget total equals sum of individual totals — no money vanishes",
             total_budget(merge_budget(b, b2)),
             add(total_budget(b), total_budget(b2)),
             ctx=[Equation("add_assoc",
                 add(add(Var("x"), Var("y")), Var("z")),
                 add(Var("x"), add(Var("y"), Var("z"))))],
             diff="medium", tags=["budget"]),

        _thm("merge_budget_items",
             "Merging budgets preserves all line items — nothing silently removed",
             item_count(merge_budget(b, b2)),
             add(item_count(b), item_count(b2)),
             diff="medium", tags=["budget"]),

        _thm("budget_merge_safe",
             "Merging with empty budget is a no-op — safe default",
             merge_budget(b, NoBudget()), b,
             diff="easy", tags=["budget"]),

        # Tasks
        _thm("empty_tasks_zero",
             "An empty task list has zero tasks — clean starting point",
             task_count(NoTasks()), Z(), diff="trivial",
             tags=["tasks"]),

        _thm("merge_tasks_count",
             "Combining task lists preserves all tasks — nothing lost",
             task_count(merge_tasks(tl, tl2)),
             add(task_count(tl), task_count(tl2)),
             diff="medium", tags=["tasks"]),

        _thm("merge_tasks_noop",
             "Merging with empty task list changes nothing",
             merge_tasks(tl, NoTasks()), tl,
             diff="easy", tags=["tasks"]),

        _thm("merge_tasks_assoc",
             "Three-way task merge is order-independent — consistent results",
             merge_tasks(merge_tasks(tl, tl2), tl3),
             merge_tasks(tl, merge_tasks(tl2, tl3)),
             diff="medium", tags=["tasks"]),
    ],
    vocabulary={
        "NoBudget": "empty budget", "BItem": "budget line item",
        "total_budget": "budget total", "item_count": "line item count",
        "merge_budget": "combine budgets",
        "NoTasks": "empty task list", "Task": "task (name, priority, rest)",
        "task_count": "number of tasks", "merge_tasks": "combine task lists",
    },
    examples=[
        "Does merging budgets lose any money?",
        "Are all tasks preserved when I combine two lists?",
        "Is three-way task merging consistent?",
    ],
)

register_skill(assistant_skill)

register_induction_scheme(
    type_name="Budget",
    base_constructor="NoBudget",
    rec_constructor="BItem",
    rec_arg_count=3,
    rec_positions=[2],
    var_hints={"b", "b2", "b3", "budget"},
)

register_induction_scheme(
    type_name="TaskList",
    base_constructor="NoTasks",
    rec_constructor="Task",
    rec_arg_count=3,
    rec_positions=[2],
    var_hints={"tl", "tl2", "tl3", "tasks", "todo"},
)
