#!/usr/bin/env python3
"""
Memory Dedup Check — Move 1b of Brain Maintenance

Checks MEMORY.md for duplicates, contradictions, and staleness.
Can also validate a proposed new entry before adding it.

Usage:
    python3 scripts/memory-dedup-check.py audit              # Audit current MEMORY.md
    python3 scripts/memory-dedup-check.py check "new fact"    # Check if a new fact duplicates existing
    python3 scripts/memory-dedup-check.py stats               # Show stats

Rules:
- Hard cap: 40 lines of content (excluding headers/blank lines)
- Exact duplicates: flagged for removal
- Near-duplicates (>80% word overlap): flagged for merge
- Contradictions: manual review required
- Entries without dates: flagged for dating

Output: Report to stdout. Does NOT modify MEMORY.md (human review required).
"""

import os
import re
import sys
from pathlib import Path
from difflib import SequenceMatcher
from collections import Counter

MEMORY_PATH = Path(os.environ.get("MEMORY_PATH", os.path.expanduser("~/.openclaw/workspace/MEMORY.md")))
HARD_CAP = 40  # Max content lines


def load_memory() -> tuple[list[str], list[tuple[int, str]]]:
    """Load MEMORY.md, return (all_lines, content_entries as (line_num, text))."""
    if not MEMORY_PATH.exists():
        print(f"ERROR: {MEMORY_PATH} not found")
        sys.exit(1)

    all_lines = MEMORY_PATH.read_text(encoding="utf-8").split("\n")
    entries = []

    for i, line in enumerate(all_lines, 1):
        stripped = line.strip()
        # Skip headers, blank lines, horizontal rules, and comment-style lines
        if (
            not stripped
            or stripped.startswith("#")
            or stripped.startswith("---")
            or stripped.startswith("*")
            or stripped.startswith("<!--")
        ):
            continue
        entries.append((i, stripped))

    return all_lines, entries


def normalize(text: str) -> str:
    """Normalize text for comparison."""
    # Remove markdown formatting
    text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text)
    text = re.sub(r"\*([^*]+)\*", r"\1", text)
    text = re.sub(r"`([^`]+)`", r"\1", text)
    text = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", text)
    # Remove leading bullet/dash
    text = re.sub(r"^[-*•]\s+", "", text)
    # Lowercase and collapse whitespace
    return " ".join(text.lower().split())


def word_overlap(a: str, b: str) -> float:
    """Calculate word-level Jaccard similarity."""
    words_a = set(normalize(a).split())
    words_b = set(normalize(b).split())
    if not words_a or not words_b:
        return 0.0
    intersection = words_a & words_b
    union = words_a | words_b
    return len(intersection) / len(union)


def sequence_similarity(a: str, b: str) -> float:
    """Calculate sequence similarity ratio."""
    return SequenceMatcher(None, normalize(a), normalize(b)).ratio()


def audit(entries: list[tuple[int, str]]) -> dict:
    """Run full audit on MEMORY.md entries."""
    results = {
        "total_entries": len(entries),
        "over_cap": len(entries) > HARD_CAP,
        "cap_headroom": HARD_CAP - len(entries),
        "exact_duplicates": [],
        "near_duplicates": [],
        "potential_contradictions": [],
        "undated": [],
    }

    # Check for exact duplicates
    seen_normalized = {}
    for line_num, text in entries:
        norm = normalize(text)
        if norm in seen_normalized:
            results["exact_duplicates"].append({
                "original_line": seen_normalized[norm],
                "duplicate_line": line_num,
                "text": text,
            })
        else:
            seen_normalized[norm] = line_num

    # Check for near-duplicates (pairwise)
    for i, (line_a, text_a) in enumerate(entries):
        for j, (line_b, text_b) in enumerate(entries):
            if j <= i:
                continue
            overlap = word_overlap(text_a, text_b)
            seq_sim = sequence_similarity(text_a, text_b)
            if overlap > 0.8 or seq_sim > 0.85:
                # Skip if already flagged as exact duplicate
                norm_a = normalize(text_a)
                norm_b = normalize(text_b)
                if norm_a == norm_b:
                    continue
                results["near_duplicates"].append({
                    "line_a": line_a,
                    "text_a": text_a[:80],
                    "line_b": line_b,
                    "text_b": text_b[:80],
                    "word_overlap": f"{overlap:.0%}",
                    "sequence_sim": f"{seq_sim:.0%}",
                })

    # Check for potential contradictions (same topic, different values)
    # Simple heuristic: entries that share 3+ significant words but have "not", "no", "never" in one but not the other
    negation_words = {"not", "no", "never", "don't", "doesn't", "can't", "cannot", "won't"}
    for i, (line_a, text_a) in enumerate(entries):
        for j, (line_b, text_b) in enumerate(entries):
            if j <= i:
                continue
            words_a = set(normalize(text_a).split())
            words_b = set(normalize(text_b).split())
            common = words_a & words_b - {"the", "a", "an", "is", "are", "was", "were", "in", "on", "at", "to", "for", "of", "and", "or"}
            if len(common) >= 3:
                neg_a = bool(words_a & negation_words)
                neg_b = bool(words_b & negation_words)
                if neg_a != neg_b:
                    results["potential_contradictions"].append({
                        "line_a": line_a,
                        "text_a": text_a[:80],
                        "line_b": line_b,
                        "text_b": text_b[:80],
                    })

    return results


def check_new_entry(entries: list[tuple[int, str]], new_text: str) -> dict:
    """Check if a proposed new entry duplicates or contradicts existing entries."""
    results = {
        "new_text": new_text,
        "is_duplicate": False,
        "similar_entries": [],
        "would_exceed_cap": len(entries) >= HARD_CAP,
    }

    norm_new = normalize(new_text)

    for line_num, existing_text in entries:
        norm_existing = normalize(existing_text)

        # Exact duplicate
        if norm_new == norm_existing:
            results["is_duplicate"] = True
            results["similar_entries"].append({
                "line": line_num,
                "text": existing_text,
                "match": "exact",
            })
            continue

        # Near duplicate
        overlap = word_overlap(new_text, existing_text)
        seq_sim = sequence_similarity(new_text, existing_text)

        if overlap > 0.6 or seq_sim > 0.7:
            results["similar_entries"].append({
                "line": line_num,
                "text": existing_text[:100],
                "word_overlap": f"{overlap:.0%}",
                "sequence_sim": f"{seq_sim:.0%}",
                "match": "near" if overlap > 0.8 else "partial",
            })

    return results


def print_audit_report(results: dict):
    """Pretty-print audit results."""
    print("=" * 60)
    print("MEMORY.md AUDIT REPORT")
    print("=" * 60)

    # Stats
    print(f"\n📊 Total content entries: {results['total_entries']}/{HARD_CAP}")
    if results["over_cap"]:
        print(f"   ⚠️  OVER CAP by {-results['cap_headroom']} entries — PRUNE REQUIRED")
    else:
        print(f"   ✅ {results['cap_headroom']} entries remaining before cap")

    # Exact duplicates
    if results["exact_duplicates"]:
        print(f"\n🔴 EXACT DUPLICATES ({len(results['exact_duplicates'])} found):")
        for d in results["exact_duplicates"]:
            print(f"   Line {d['original_line']} = Line {d['duplicate_line']}: {d['text'][:70]}")
    else:
        print("\n✅ No exact duplicates")

    # Near duplicates
    if results["near_duplicates"]:
        print(f"\n🟡 NEAR DUPLICATES ({len(results['near_duplicates'])} found):")
        for d in results["near_duplicates"]:
            print(f"   Line {d['line_a']}: {d['text_a']}")
            print(f"   Line {d['line_b']}: {d['text_b']}")
            print(f"   Overlap: {d['word_overlap']} words, {d['sequence_sim']} sequence")
            print()
    else:
        print("✅ No near duplicates")

    # Contradictions
    if results["potential_contradictions"]:
        print(f"\n🔴 POTENTIAL CONTRADICTIONS ({len(results['potential_contradictions'])} found):")
        for c in results["potential_contradictions"]:
            print(f"   Line {c['line_a']}: {c['text_a']}")
            print(f"   Line {c['line_b']}: {c['text_b']}")
            print()
    else:
        print("✅ No potential contradictions detected")


def main():
    if len(sys.argv) < 2:
        print("Usage: memory-dedup-check.py [audit|check|stats] [text]")
        sys.exit(1)

    command = sys.argv[1]
    all_lines, entries = load_memory()

    if command == "audit":
        results = audit(entries)
        print_audit_report(results)

    elif command == "check":
        if len(sys.argv) < 3:
            print("Usage: memory-dedup-check.py check \"new fact to check\"")
            sys.exit(1)
        new_text = " ".join(sys.argv[2:])
        results = check_new_entry(entries, new_text)

        print(f"\nChecking: \"{new_text}\"")
        if results["is_duplicate"]:
            print("🔴 EXACT DUPLICATE — do not add")
        elif results["similar_entries"]:
            print(f"🟡 Similar entries found ({len(results['similar_entries'])}):")
            for s in results["similar_entries"]:
                print(f"   Line {s['line']}: {s['text']}")
                print(f"   Match: {s['match']} ({s.get('word_overlap', 'N/A')} overlap)")
        else:
            print("✅ No duplicates found — safe to add")

        if results["would_exceed_cap"]:
            print(f"⚠️  Adding this would exceed the {HARD_CAP}-line cap. Remove an entry first.")

    elif command == "stats":
        print(f"MEMORY.md: {len(entries)} content lines / {HARD_CAP} cap")
        print(f"File: {len(all_lines)} total lines")
        print(f"Headroom: {HARD_CAP - len(entries)} entries")

    else:
        print(f"Unknown command: {command}")
        sys.exit(1)


if __name__ == "__main__":
    main()
