#!/usr/bin/env python3
"""
SCH-024 v2: Remove sequential file upload gates.
Uses brace-depth tracking for reliable Gate 4 close detection.
"""

import re
import os
import shutil

BASE = '/var/www/html/dev_scheduler/SCHEDULER/system/application/views'
ORIG_DIR = '/var/www/html/dev_scheduler/ENHANCEMENTS/SCH-024_remove_upload_gates/original'

FILES = [
    'admin/job_pv.php', 'admin/job_pv_34181.php',
    'admin/job_pv_proposal.php', 'admin/job_pv_proposal_34181.php',
    'admin/job_ac.php', 'admin/job_ac_34181.php',
    'admin/job_swh.php', 'admin/job_swh_34181.php',
    'admin/job_swh_proposal_pm.php',
    'admin/job_pl.php', 'admin/job_pl_34181.php',
    'admin/job_pm.php', 'admin/job_pm_34181.php',
    'admin/job_installer_pv.php', 'admin/job_installer_pv_proposal.php',
    'admin/job_installer_ac.php', 'admin/job_installer_pl.php',
    'admin/job_installer_swh.php',
    'homecharging/job_pv.php', 'saf/job_pv.php',
    'roofproposal/job_pv.php', 'swhproposal/job_pv.php',
    'admin/admin.php', 'job_status/admin.php', 'proposal/admin.php',
]


def find_gate2_blocks(lines):
    """Find Gate 2: $upload_process_started blocks.
    Returns list of (start_line, end_line) to remove."""
    removes = []
    i = 0
    while i < len(lines):
        s = lines[i].strip()
        # Look for $upload_process_started = false
        if '$upload_process_started' in s and 'false' in s:
            # Scan backwards to find the <?php that started this block
            block_start = i
            for back in range(i - 1, max(i - 5, -1), -1):
                bs = lines[back].strip()
                if bs == '<?php' or bs.endswith('<?php'):
                    block_start = back
                    break

            # Scan forward to find the closing ?>
            block_end = i
            for fwd in range(i + 1, min(i + 15, len(lines))):
                fs = lines[fwd].strip()
                if 'if($upload_process_started)' in fs or 'if( $upload_process_started)' in fs or 'if ($upload_process_started)' in fs:
                    # Find the ?> after this
                    for fwd2 in range(fwd, min(fwd + 5, len(lines))):
                        if '?>' in lines[fwd2]:
                            block_end = fwd2
                            break
                    break

            removes.append((block_start, block_end))
            i = block_end + 1
        else:
            i += 1
    return removes


def find_combined_close_open(lines):
    """Find lines like: <?php }  if( count($predesignsketch_files) > 0 ) { ?>
    These close Gate 2 and open Gate 3. Remove entire line."""
    removes = []
    for i, line in enumerate(lines):
        s = line.strip()
        # Match: } followed by if(count($predesignsketch_files) > 0) {
        if re.search(r'\}\s*if\s*\(\s*count\s*\(\s*\$predesignsketch_files\s*\)\s*>\s*0\s*\)\s*\{', s):
            removes.append(i)
    return removes


def find_gate3_close_and_gate4(lines):
    """Find Gate 3 close (<? } ?>) and Gate 4 open on adjacent lines.
    Returns list of (gate3_close_line, gate4_open_line, gate4_close_line)."""
    results = []

    for i, line in enumerate(lines):
        s = line.strip()
        # Gate 4 open pattern: if( count($presale_files) > 0 && count($predesignsketch_files) > 0 ) {
        # Also variant: if( count($presale_files) > 0 ) {
        is_gate4 = False
        if re.search(r'<\?(php)?\s*if\s*\(\s*count\s*\(\s*\$presale_files\s*\)\s*>\s*0\s*&&\s*count\s*\(\s*\$predesignsketch_files\s*\)\s*>\s*0\s*\)', s):
            is_gate4 = True
        elif re.search(r'<\?(php)?\s*if\s*\(\s*count\s*\(\s*\$presale_files\s*\)\s*>\s*0\s*\)\s*\{', s):
            # Only if it's a standalone gate (not inside other logic like a foreach)
            if s.startswith('<?'):
                is_gate4 = True

        if not is_gate4:
            continue

        # Find Gate 3 close: look backwards for <? } ?> or <?php } ?>
        gate3_close = None
        for back in range(i - 1, max(i - 5, -1), -1):
            bs = lines[back].strip()
            if re.match(r'^<\?(php)?\s*\}\s*\?>$', bs):
                gate3_close = back
                break
            elif bs == '':
                continue
            else:
                break

        # Find Gate 4 close: track PHP brace depth from gate4 open
        # Count { and } in PHP blocks, find the matching close
        gate4_close = find_matching_close(lines, i)

        results.append((gate3_close, i, gate4_close))

    return results


def find_matching_close(lines, gate_open_line):
    """From a gate open line like <?php if(...) { ?>, find the matching <? } ?> close.
    Uses brace-depth tracking across PHP blocks."""
    depth = 0
    # Count braces on the opening line itself
    open_line = lines[gate_open_line]
    depth += count_php_braces(open_line)

    for j in range(gate_open_line + 1, min(gate_open_line + 300, len(lines))):
        line = lines[j]
        stripped = line.strip()

        # Count PHP braces in this line
        brace_delta = count_php_braces(line)
        depth += brace_delta

        if depth <= 0:
            # This line contains the matching close
            # But only if it's a standalone <? } ?> type line
            if re.match(r'^<\?(php)?\s*\}\s*\?>\s*$', stripped):
                return j
            # If depth went to 0 but line isn't standalone, it might be an inline close
            # Reset and continue (some files have inline PHP that resets)

    return None


def count_php_braces(line):
    """Count net PHP braces ({ minus }) in a line, only within PHP blocks.
    Ignores braces inside HTML or strings."""
    # Simple approach: find PHP sections and count braces in them
    # PHP sections: between <? or <?php and ?>
    # Also handle lines that are entirely PHP

    stripped = line.strip()
    net = 0

    # Check if line has PHP code
    # Strategy: extract PHP portions and count { and }
    # For simplicity, count all { and } on lines that contain PHP markers
    # or are within PHP context

    # Lines with <?...?> inline
    if '<?' in line or '?>' in line or '{' in line or '}' in line:
        # Simple heuristic: count { and } that appear to be PHP flow control
        # Remove string contents first (rough)
        cleaned = re.sub(r"'[^']*'", '', line)
        cleaned = re.sub(r'"[^"]*"', '', cleaned)

        net = cleaned.count('{') - cleaned.count('}')

    return net


def remove_gates(filepath):
    """Process a single file, removing all upload gates."""
    with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
        lines = f.readlines()

    lines_to_remove = set()
    changes = []

    # 1. Find and mark Gate 2 blocks
    gate2_blocks = find_gate2_blocks([l.rstrip('\n') for l in lines])
    for start, end in gate2_blocks:
        for idx in range(start, end + 1):
            lines_to_remove.add(idx)
        changes.append(f"  Gate 2 open: lines {start+1}-{end+1}")

    # 2. Find and mark combined close/open lines
    combined = find_combined_close_open([l.rstrip('\n') for l in lines])
    for idx in combined:
        lines_to_remove.add(idx)
        changes.append(f"  Gate 2 close + Gate 3 open: line {idx+1}")

    # 3. Find Gate 3 close + Gate 4 open + Gate 4 close
    gate_sets = find_gate3_close_and_gate4([l.rstrip('\n') for l in lines])
    for g3_close, g4_open, g4_close in gate_sets:
        if g3_close is not None:
            lines_to_remove.add(g3_close)
            changes.append(f"  Gate 3 close: line {g3_close+1}")
        lines_to_remove.add(g4_open)
        changes.append(f"  Gate 4 open: line {g4_open+1}")
        if g4_close is not None:
            lines_to_remove.add(g4_close)
            changes.append(f"  Gate 4 close: line {g4_close+1}")
        else:
            changes.append(f"  !! Gate 4 close NOT FOUND for gate at line {g4_open+1}")

    if not lines_to_remove:
        return 0, []

    # Write modified file
    new_lines = [lines[i] for i in range(len(lines)) if i not in lines_to_remove]
    with open(filepath, 'w', encoding='utf-8') as f:
        f.writelines(new_lines)

    return len(lines_to_remove), changes


def main():
    total_files = 0
    total_removed = 0
    issues = []

    for rel_path in FILES:
        full_path = os.path.join(BASE, rel_path)
        if not os.path.exists(full_path):
            continue

        removed, changes = remove_gates(full_path)
        if removed > 0:
            print(f"OK  {rel_path}: {removed} lines removed")
            for c in changes:
                print(c)
                if '!!' in c:
                    issues.append(f"{rel_path}: {c}")
            total_files += 1
            total_removed += removed
        else:
            print(f"--  {rel_path}: no gates found")

    print(f"\nSUMMARY: {total_files} files modified, {total_removed} total lines removed")
    if issues:
        print(f"\n!! ISSUES ({len(issues)}):")
        for i in issues:
            print(f"  {i}")


if __name__ == '__main__':
    main()
