#!/usr/bin/env python3
"""
Project Validation Tool
Runs the full /validate check on a development project.

Usage:
    python validate_project.py <project_path>
    python validate_project.py ../projects/leviathan/development
    python validate_project.py ../projects/leviathan/development --strict
"""

import sys
import re
from pathlib import Path

# Import constants from shared module
_SCRIPT_DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(_SCRIPT_DIR))
try:
    from engine_constants import (
        WORD_COUNT_MIN, WORD_COUNT_MAX,
        DIALOGUE_MAX_PERCENT, MAX_EXCHANGES,
        MAX_CONSECUTIVE_SAME_TYPE
    )
except ImportError:
    WORD_COUNT_MIN = 450
    WORD_COUNT_MAX = 500
    DIALOGUE_MAX_PERCENT = 40
    MAX_EXCHANGES = 8
    MAX_CONSECUTIVE_SAME_TYPE = 3

# ANSI colors
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
CYAN = '\033[96m'
BOLD = '\033[1m'
RESET = '\033[0m'

REQUIRED_DOCUMENTS = [
    'thematic_spine.md',
    'characters.md',  # Updated: was character_arcs.md
    'relationship_map.md',
    'structure_outline.md',
    'plant_payoff_plan.md'
]

REQUIRED_EMOTIONAL_BEATS = [10, 15, 20, 26, 30, 32, 36, 42, 45, 50, 59]


def count_checked_items(content):
    """Count checked [x] vs unchecked [ ] items in markdown."""
    checked = len(re.findall(r'\[x\]', content, re.IGNORECASE))
    unchecked = len(re.findall(r'\[\s\]', content))
    return checked, unchecked


def check_status_md(project_path):
    """Check STATUS.md for completion."""
    status_path = project_path / 'STATUS.md'
    if not status_path.exists():
        return None, "STATUS.md not found"

    with open(status_path, 'r', encoding='utf-8') as f:
        content = f.read()

    checked, unchecked = count_checked_items(content)
    total = checked + unchecked

    # Extract progress from header
    progress_match = re.search(r'Progress:\s*(\d+)/(\d+)', content)
    if progress_match:
        reported_done = int(progress_match.group(1))
        reported_total = int(progress_match.group(2))
    else:
        reported_done, reported_total = checked, 34

    return {
        'checked': checked,
        'unchecked': unchecked,
        'total': total,
        'reported_progress': f"{reported_done}/{reported_total}",
        'passed': reported_done >= 34 or unchecked == 0
    }, None


def check_documents_exist(project_path):
    """Check that all required documents exist."""
    results = {}
    all_exist = True

    for doc in REQUIRED_DOCUMENTS:
        doc_path = project_path / doc
        exists = doc_path.exists()
        has_content = False

        if exists:
            with open(doc_path, 'r', encoding='utf-8') as f:
                content = f.read().strip()
                has_content = len(content) > 100  # More than just a header

        results[doc] = {
            'exists': exists,
            'has_content': has_content,
            'passed': exists and has_content
        }

        if not (exists and has_content):
            all_exist = False

    return results, all_exist


def check_law2_tables(project_path):
    """Check that Law 2 tables have both plot and character for Ep 15, 30, 45, 60."""
    structure_path = project_path / 'structure_outline.md'
    status_path = project_path / 'STATUS.md'

    results = {15: False, 30: False, 45: False, 60: False}

    # Check both files for the key episodes
    for filepath in [structure_path, status_path]:
        if not filepath.exists():
            continue

        with open(filepath, 'r', encoding='utf-8') as f:
            content = f.read()

        for ep in [15, 30, 45, 60]:
            # Look for patterns like "Ep 15" or "Episode 15" with both plot and character info
            pattern = rf'\*?\*?{ep}\*?\*?.*?(plot|advance|character|pivot)'
            if re.search(pattern, content, re.IGNORECASE):
                results[ep] = True

    return results, all(results.values())


def check_primary_ache(project_path):
    """Check that Primary Ache is defined with threats."""
    relationship_path = project_path / 'relationship_map.md'
    if not relationship_path.exists():
        return None, False

    with open(relationship_path, 'r', encoding='utf-8') as f:
        content = f.read()

    # Look for Primary Ache section
    has_ache = 'Primary Ache' in content

    # Count threats
    threats = re.findall(r'[-•]\s*(?:The\s+)?(?:transactional|programming|guilt|mission|economy|system|connection)', content, re.IGNORECASE)
    threat_section = re.search(r'(?:threatened|threats?).*?(?=\n#|\Z)', content, re.IGNORECASE | re.DOTALL)

    if threat_section:
        threat_count = len(re.findall(r'[-•]\s+', threat_section.group()))
    else:
        threat_count = len(threats)

    return {
        'has_ache': has_ache,
        'threat_count': threat_count,
        'passed': has_ache and threat_count >= 2
    }, has_ache and threat_count >= 2


def check_relationship_markers(project_path):
    """Check for at least 8 relationship markers."""
    relationship_path = project_path / 'relationship_map.md'
    if not relationship_path.exists():
        return 0, False

    with open(relationship_path, 'r', encoding='utf-8') as f:
        content = f.read()

    # Count markers in table format
    marker_pattern = re.compile(r'\|\s*\*?\*?[^|]+\*?\*?\s*\|\s*(?:Ep(?:isode)?\s*)?\d+', re.IGNORECASE)
    markers = marker_pattern.findall(content)

    # Also count "First..." patterns
    first_patterns = re.findall(r'First\s+\w+', content)

    marker_count = max(len(markers) // 2, len(first_patterns))  # Divide by 2 for table rows

    return marker_count, marker_count >= 8


def check_characters(project_path):
    """Check character requirements (was check_character_arcs)."""
    chars_path = project_path / 'characters.md'
    if not chars_path.exists():
        return None, False

    with open(chars_path, 'r', encoding='utf-8') as f:
        content = f.read()

    results = {
        'has_arc_type': bool(re.search(r'(?:Positive|Negative|Flat|Disillusionment|Revelation)\s*(?:Arc)?', content, re.IGNORECASE)),
        'has_lie': bool(re.search(r'(?:The\s+)?Lie', content, re.IGNORECASE)),
        'has_need_layers': bool(re.search(r'(?:Surface|Deeper|Deepest)', content, re.IGNORECASE)),
        'has_counter_thesis': bool(re.search(r'counter.?thesis|Varek|antagonist', content, re.IGNORECASE)),
        'has_anchor_type': bool(re.search(r'(?:Cub|Ghost|Mirror|Asset)', content, re.IGNORECASE))
    }

    all_pass = all(results.values())
    return results, all_pass


def check_thematic_foundation(project_path):
    """Check thematic foundation requirements."""
    spine_path = project_path / 'thematic_spine.md'
    if not spine_path.exists():
        return None, False

    with open(spine_path, 'r', encoding='utf-8') as f:
        content = f.read()

    results = {
        'has_question': bool(re.search(r'(?:Thematic\s+)?Question', content, re.IGNORECASE)),
        'has_really_about': bool(re.search(r'(?:This\s+Is\s+)?Really\s+About', content, re.IGNORECASE)),
        'has_mythology': bool(re.search(r'(?:Mytholog|Prometheus|Noah|Golem|Greek|Biblical)', content, re.IGNORECASE))
    }

    all_pass = all(results.values())
    return results, all_pass


def check_plant_payoff(project_path):
    """Check plant/payoff infrastructure."""
    pp_path = project_path / 'plant_payoff_plan.md'
    if not pp_path.exists():
        return None, False

    with open(pp_path, 'r', encoding='utf-8') as f:
        content = f.read()

    # Count threads
    thread_count = len(re.findall(r'##\s*Thread\s*\d+', content, re.IGNORECASE))

    # Check for types
    has_object = bool(re.search(r'\*\*Type:\*\*\s*Object', content, re.IGNORECASE))
    has_phrase = bool(re.search(r'\*\*Type:\*\*\s*Phrase', content, re.IGNORECASE))
    has_image = bool(re.search(r'\*\*Type:\*\*\s*Image', content, re.IGNORECASE))

    # Count echo moments
    echo_count = len(re.findall(r'##\s*Echo\s*\d+', content, re.IGNORECASE))

    results = {
        'thread_count': thread_count,
        'has_object': has_object,
        'has_phrase': has_phrase,
        'has_image': has_image,
        'echo_count': echo_count,
        'passed': thread_count >= 6 and has_object and has_phrase and has_image and echo_count >= 1
    }

    return results, results['passed']


def run_validation(project_path, strict=False):
    """Run full validation."""
    project_path = Path(project_path)
    results = {
        'hard_gates': {},
        'warnings': [],
        'passed': True
    }

    # === HARD GATES ===

    # 1. Checklist completion
    status_result, err = check_status_md(project_path)
    if err:
        results['hard_gates']['checklist'] = {'passed': False, 'error': err}
    else:
        results['hard_gates']['checklist'] = status_result

    # 2. Required documents
    doc_results, docs_pass = check_documents_exist(project_path)
    results['hard_gates']['documents'] = {'items': doc_results, 'passed': docs_pass}

    # 3. Law 2 tables
    law2_results, law2_pass = check_law2_tables(project_path)
    results['hard_gates']['law2'] = {'items': law2_results, 'passed': law2_pass}

    # 4. Primary Ache
    ache_result, ache_pass = check_primary_ache(project_path)
    results['hard_gates']['primary_ache'] = ache_result if ache_result else {'passed': False}

    # 5. Relationship markers
    marker_count, markers_pass = check_relationship_markers(project_path)
    results['hard_gates']['relationship_markers'] = {
        'count': marker_count,
        'required': 8,
        'passed': markers_pass
    }

    # 6. Characters
    char_results, chars_pass = check_characters(project_path)
    results['hard_gates']['characters'] = char_results if char_results else {'passed': False}

    # 7. Thematic foundation
    theme_results, theme_pass = check_thematic_foundation(project_path)
    results['hard_gates']['thematic_foundation'] = theme_results if theme_results else {'passed': False}

    # 8. Plant/Payoff
    pp_results, pp_pass = check_plant_payoff(project_path)
    results['hard_gates']['plant_payoff'] = pp_results if pp_results else {'passed': False}

    # Calculate overall
    all_pass = all([
        results['hard_gates'].get('checklist', {}).get('passed', False),
        results['hard_gates'].get('documents', {}).get('passed', False),
        results['hard_gates'].get('law2', {}).get('passed', False),
        results['hard_gates'].get('primary_ache', {}).get('passed', False),
        results['hard_gates'].get('relationship_markers', {}).get('passed', False),
        results['hard_gates'].get('characters', {}).get('passed', False),
        results['hard_gates'].get('thematic_foundation', {}).get('passed', False),
        results['hard_gates'].get('plant_payoff', {}).get('passed', False)
    ])

    results['passed'] = all_pass
    return results


def print_report(results, project_name, strict=False):
    """Print formatted validation report."""
    print(f"\n{'═' * 65}")
    print(f"{BOLD}PROMOTION VALIDATION: {project_name.upper()}{RESET}")
    print(f"{'═' * 65}\n")

    hg = results['hard_gates']
    passed_count = sum(1 for v in hg.values() if isinstance(v, dict) and v.get('passed', False))
    total_count = len(hg)

    status = f"{GREEN}✓{RESET}" if passed_count == total_count else f"{RED}✗{RESET}"
    print(f"{BOLD}HARD GATES: {passed_count}/{total_count} {status}{RESET}\n")

    # Checklist
    cl = hg.get('checklist', {})
    status = f"{GREEN}✓{RESET}" if cl.get('passed') else f"{RED}✗{RESET}"
    print(f"  {status} Checklist: {cl.get('reported_progress', 'N/A')}")

    # Documents
    docs = hg.get('documents', {})
    status = f"{GREEN}✓{RESET}" if docs.get('passed') else f"{RED}✗{RESET}"
    print(f"  {status} Required documents exist")
    if not docs.get('passed'):
        for doc, info in docs.get('items', {}).items():
            if not info.get('passed'):
                print(f"      {RED}Missing: {doc}{RESET}")

    # Law 2
    law2 = hg.get('law2', {})
    status = f"{GREEN}✓{RESET}" if law2.get('passed') else f"{RED}✗{RESET}"
    print(f"  {status} Law 2 tables (Ep 15, 30, 45, 60)")

    # Primary Ache
    ache = hg.get('primary_ache', {})
    status = f"{GREEN}✓{RESET}" if ache.get('passed') else f"{RED}✗{RESET}"
    print(f"  {status} Primary Ache defined ({ache.get('threat_count', 0)} threats)")

    # Relationship markers
    markers = hg.get('relationship_markers', {})
    status = f"{GREEN}✓{RESET}" if markers.get('passed') else f"{RED}✗{RESET}"
    print(f"  {status} Relationship markers: {markers.get('count', 0)}/8")

    # Characters
    arcs = hg.get('characters', {})
    status = f"{GREEN}✓{RESET}" if arcs.get('passed', False) else f"{RED}✗{RESET}"
    print(f"  {status} Characters defined")

    # Thematic foundation
    theme = hg.get('thematic_foundation', {})
    status = f"{GREEN}✓{RESET}" if theme.get('passed', False) else f"{RED}✗{RESET}"
    print(f"  {status} Thematic foundation defined")

    # Plant/Payoff
    pp = hg.get('plant_payoff', {})
    status = f"{GREEN}✓{RESET}" if pp.get('passed', False) else f"{RED}✗{RESET}"
    print(f"  {status} Plant/Payoff: {pp.get('thread_count', 0)} threads, {pp.get('echo_count', 0)} echoes")

    # Final result
    print(f"\n{'═' * 65}")
    if results['passed']:
        print(f"{BOLD}{GREEN}RESULT: ✅ READY FOR PROMOTION{RESET}")
        print(f"\nRun: Promote {project_name} to production")
    else:
        print(f"{BOLD}{RED}RESULT: ❌ NOT READY{RESET}")
        print(f"\n{YELLOW}FIX REQUIRED:{RESET}")
        if not hg.get('checklist', {}).get('passed'):
            print(f"  - Complete all 34 checklist items in STATUS.md")
        if not hg.get('documents', {}).get('passed'):
            print(f"  - Create missing development documents")
        if not hg.get('law2', {}).get('passed'):
            print(f"  - Define plot + character pivot for all major turns")
        if not hg.get('primary_ache', {}).get('passed'):
            print(f"  - Define Primary Ache with at least 2 threats")
        if not hg.get('relationship_markers', {}).get('passed'):
            print(f"  - Add more relationship markers (need 8+)")
        if not hg.get('plant_payoff', {}).get('passed'):
            print(f"  - Add more plant/payoff threads (need 6+)")

    print(f"{'═' * 65}\n")

    return results['passed']


def main():
    if len(sys.argv) < 2:
        print(f"Usage: python {sys.argv[0]} <project_path> [--strict]")
        print(f"Example: python {sys.argv[0]} ../projects/leviathan/development")
        sys.exit(1)

    project_path = sys.argv[1]
    strict = '--strict' in sys.argv
    project_name = Path(project_path).name

    results = run_validation(project_path, strict)
    passed = print_report(results, project_name, strict)
    sys.exit(0 if passed else 1)


if __name__ == '__main__':
    main()
