"""
Robust sky-btn fixer for Vue template NButton elements.

Processes ALL .vue files in frontend/src.
For every <NButton (including <n-button) in the template that:
  - does NOT have the "text" prop
  - does NOT already have "sky-btn" in its class attribute
  - does NOT use :class
Adds class="sky-btn" (or appends " sky-btn" to existing class).

IMPORTANT: Does NOT match NButton inside quoted attribute values (fixes v1's bug).
Also fixes any files corrupted by v1's buggy run.
"""

import re
import os
import glob


def find_template_section(content):
    """Find the <template>...</template> section in a .vue file.

    Uses rfind('</template>') to find the LAST closing tag, so nested
    <template #header>...</template> inside the main template are handled
    correctly (the main template always encompasses all nested blocks).
    """
    template_start = content.find('<template>')
    if template_start < 0:
        # Try <template lang="...">, <template v-if="..." etc.
        m = re.search(r'<template\b[^>]*>', content)
        if not m:
            return None, None
        template_start = m.start()

    # Use rfind to get the LAST </template> which closes the main template block
    template_end = content.rfind('</template>')
    if template_end < 0:
        return None, None

    return template_start, template_end + len('</template>')


def find_all_nbutton_tags(text, start_pos=0):
    """
    Find all <NButton or <n-button opening tags in text, EXCLUDING those
    inside quoted attribute values.

    Returns list of (match_start, match_end, tag_text) for opening tags.
    """
    # First, let's strip out everything inside quoted strings to avoid
    # matching NButton inside attribute values
    # We'll build a map of which positions are "real" vs inside quotes

    inside_double_quote = False
    inside_single_quote = False
    positions = {}  # pos -> True if real (not in quotes)

    i = start_pos
    while i < len(text):
        ch = text[i]
        if ch == '"' and not inside_single_quote:
            inside_double_quote = not inside_double_quote
        elif ch == "'" and not inside_double_quote:
            inside_single_quote = not inside_single_quote
        elif ch == '\\' and i + 1 < len(text):
            # Skip escaped characters
            positions[i] = not (inside_double_quote or inside_single_quote)
            i += 2
            continue

        positions[i] = not (inside_double_quote or inside_single_quote)
        i += 1

    # Now find all NButton opening tags with proper regex
    # Only consider positions where the '<' is not inside quotes
    tag_pattern = re.compile(r'<[Nn][-]?[Bb]utton\b', re.DOTALL)

    results = []
    for match in tag_pattern.finditer(text, start_pos):
        tag_start = match.start()

        # Skip if the '<' is inside quotes
        if tag_start in positions and not positions[tag_start]:
            continue

        # Now find the end of this opening tag (the '>' that closes it)
        # Need to handle nested quotes within the tag
        pos = match.end()
        in_dq = False
        in_sq = False
        while pos < len(text):
            ch = text[pos]
            if ch == '"' and not in_sq:
                in_dq = not in_dq
            elif ch == "'" and not in_dq:
                in_sq = not in_sq
            elif ch == '>' and not in_dq and not in_sq:
                tag_end = pos + 1
                tag_text = text[tag_start:tag_end]
                results.append((tag_start, tag_end, tag_text))
                break
            pos += 1

    return results


def has_text_prop(tag_text):
    """Check if the NButton tag has 'text' as a boolean prop (not :text)."""
    # Remove all quoted strings
    stripped = re.sub(r'"[^"]*"', '', tag_text)
    stripped = re.sub(r"'[^']*'", '', stripped)

    # Look for 'text' as a standalone word not preceded by ':'
    for m in re.finditer(r'\btext\b', stripped):
        if m.start() > 0 and stripped[m.start()-1] == ':':
            continue  # :text, skip
        # Must be preceded by whitespace or start of tag
        pre_char = stripped[m.start()-1] if m.start() > 0 else ' '
        if pre_char in (' ', '\t', '\n', '\r', '>'):
            return True
    return False


def has_class_binding(tag_text):
    """Check if the NButton has :class."""
    # Remove quoted strings first
    stripped = re.sub(r'"[^"]*"', '', tag_text)
    stripped = re.sub(r"'[^']*'", '', stripped)
    if re.search(r':class\b', stripped):
        return True
    # Also check for v-bind:class
    if re.search(r'v-bind:class\b', stripped):
        return True
    return False


def has_skybtn(tag_text):
    """Check if the NButton already has sky-btn in class."""
    return 'sky-btn' in tag_text


def modify_nbutton_tag(tag_text):
    """Add class="sky-btn" to an NButton tag. Returns modified tag or None."""
    # Rules
    if has_text_prop(tag_text):
        return None
    if has_class_binding(tag_text):
        return None
    if has_skybtn(tag_text):
        return None

    # Check if it has class="..."
    class_match = re.search(r'class\s*=\s*"([^"]*)"', tag_text)
    if class_match:
        existing = class_match.group(1)
        if 'sky-btn' in existing:
            return None
        new_class = existing + ' sky-btn' if existing.strip() else 'sky-btn'
        result = tag_text[:class_match.start(1)] + new_class + tag_text[class_match.end(1):]
        return result

    # No class attribute - add class="sky-btn"
    # Insert before the closing >
    if tag_text.endswith('/>') or tag_text.endswith('/>'):
        return tag_text[:-2] + ' class="sky-btn" />'
    elif tag_text.endswith('>'):
        return tag_text[:-1] + ' class="sky-btn">'
    return None


def fix_corrupted_file(content):
    """
    Fix files corrupted by v1 of the fixer.
    v1's bug: it matched <NButton inside quoted attribute values like
    placeholder="...<NButton..." and inserted class="sky-btn" there,
    breaking the HTML.

    We detect this pattern and fix it by removing the misplaced class.
    Also fixes cases where NButton tags were mangled together with other tags.
    """
    # Pattern 1: NButton class="sky-btn" inside an attribute value
    # e.g., placeholder="研究目的（<NButton ... class="sky-btn">...="createReview">
    # Fix: remove the corrupted inner NButton artifacts

    # Pattern 2: Duplicate/split attributes from v1 mangling
    # e.g., class="sky-btn">freshCitations">  should be class="sky-btn">
    # and the rest should be proper attributes

    original = content

    # Fix pattern: something="text...<NButton...class="sky-btn">...some garbage>"
    # This happens when v1 matched NButton inside a placeholder or other attribute
    pattern1 = re.compile(
        r'(placeholder|title|description|label|help-text|filter)\s*=\s*"([^"]*?)(<[Nn][-]?[Bb]utton\b[^>]*?class\s*=\s*"sky-btn"[^>]*>)([^"]*?)"',
        re.DOTALL
    )
    content = pattern1.sub(r'\1="\2sky-btn-placeholder-fix\4"', content)

    # Fix pattern: "<NButton ...class="sky-btn">n size="..." ...>"
    # This happens when v1 mangled a tag name prefix
    pattern2 = re.compile(
        r'(<[Nn][-]?[Bb]utton\b[^>]*?class\s*=\s*"sky-btn">)\s*([a-zA-Z]+)\s*=\s*"',
        re.DOTALL
    )
    # Remove attribute fragments that somehow got after the closing >
    content = pattern2.sub(r'\1 ', content)

    # Fix pattern: "<NButton ... class="sky-btn">..." (where "..." is a truncated/mangled attribute)
    pattern3 = re.compile(
        r'(<[Nn][-]?[Bb]utton\b[^>]*?class\s*=\s*"sky-btn"[^>]*>)([a-zA-Z-]+)\s*=\s*"',
        re.DOTALL
    )
    content = pattern3.sub(r'\1', content)

    return content


def process_file(filepath):
    """Process a single .vue file."""
    with open(filepath, 'r', encoding='utf-8') as f:
        content = f.read()

    original = content

    # First, try to fix any corruption from v1
    content = fix_corrupted_file(content)

    # Find template section
    template_start, template_end = find_template_section(content)
    if template_start is None:
        return 0, "no <template> found"

    template_text = content[template_start:template_end]

    # Find all NButton tags in template
    nbutton_tags = find_all_nbutton_tags(template_text)

    if not nbutton_tags:
        return 0, "no NButton found"

    changes = 0
    # Process in reverse order to preserve positions
    for tag_start, tag_end, tag_text in reversed(nbutton_tags):
        modified = modify_nbutton_tag(tag_text)
        if modified:
            abs_start = template_start + tag_start
            abs_end = template_start + tag_end
            content = content[:abs_start] + modified + content[abs_end:]
            changes += 1

    if changes > 0 or content != original:
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(content)

    return changes, "ok"


def main():
    frontend_src = 'd:/ClaudeCode/frontend/src'

    # Find all .vue files
    vue_files = []
    for root, dirs, files in os.walk(frontend_src):
        for f in files:
            if f.endswith('.vue'):
                vue_files.append(os.path.join(root, f))

    vue_files.sort()

    total_changes = 0
    file_results = []
    all_files_with_nbutton = []

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

        if not re.search(r'<[Nn][-]?[Bb]utton\b', content):
            continue

        all_files_with_nbutton.append(filepath)
        changes, status = process_file(filepath)
        if changes > 0:
            rel_path = os.path.relpath(filepath, frontend_src)
            file_results.append((rel_path, changes))
            total_changes += changes
            print(f"  MODIFIED: {rel_path} ({changes} change(s))")

    print(f"\n=== Summary ===")
    print(f"Files with NButton: {len(all_files_with_nbutton)}")
    print(f"Files modified: {len(file_results)}")
    print(f"Total NButton tags modified: {total_changes}")

    if not file_results:
        print("\nNO_CHANGES")


if __name__ == '__main__':
    main()
