#!/usr/bin/env python
"""
SCH-018: Nav Tabs Shared Partial Extraction
Phase A: Add $data['active_tab'] to admin.php controller methods
Phases B-D: Replace inline nav tabs in view files with shared partial include

Usage:
  python enh018_apply.py --phase A    # Controller modifications only
  python enh018_apply.py --phase B    # Core job specs views
  python enh018_apply.py --phase C    # Comment/customer/financing/referral/status/permit/document views
  python enh018_apply.py --phase D    # Remaining financing variants and edge cases
  python enh018_apply.py --phase ALL  # All phases
"""
import os
import re
import sys
import shutil

BASE = '/var/www/html/dev_scheduler/SCHEDULER/system/application'
VIEWS = os.path.join(BASE, 'views/admin')
CONTROLLER = os.path.join(BASE, 'controllers/admin.php')
BACKUP_DIR = '/var/www/html/dev_scheduler/ENHANCEMENTS/SCH-018_nav_tabs_shared_partial/original'

INCLUDE_LINE = '<?php $this->load->view(\'common/job_nav_tabs\'); ?>'

# Regex to match the entire generic-nav-tabs block
# From <div class="generic-nav-tabs"> to its closing </div> that contains <div class="clear-left">
NAV_TABS_PATTERN = re.compile(
    r'(\s*)<div\s+class="generic-nav-tabs">'  # opening div with leading whitespace
    r'.*?'                                      # content
    r'<div\s+class="clear-left"\s*>\s*</div>'   # clear-left div
    r'\s*</div>',                               # closing div of generic-nav-tabs
    re.DOTALL
)

# Pattern to match just the $deal_type if-block (not the $CI line)
# This matches:  if ( $job['job_type_id']==2 && $job['proposal_id']>0){ $deal_type=...; }
DEAL_TYPE_ONLY_PATTERN = re.compile(
    r'\n\s*if\s*\(\s*\$job\[.job_type_id.\]\s*==\s*2\s*&&\s*\$job\[.proposal_id.\]\s*>\s*0\s*\)\s*\{[^}]*\$deal_type\s*=\s*\$CI->getproposaltype[^}]*\}\s*\n',
    re.DOTALL
)

# Pattern to match the $CI + $deal_type block together (when $CI is ONLY used for deal_type)
DEAL_TYPE_WITH_CI_PATTERN = re.compile(
    r'\n\s*\$CI\s*=\s*&?\s*get_instance\(\);\s*\n'
    r'\s*if\s*\(\s*\$job\[.job_type_id.\]\s*==\s*2\s*&&\s*\$job\[.proposal_id.\]\s*>\s*0\s*\)\s*\{[^}]*\$deal_type\s*=\s*\$CI->getproposaltype[^}]*\}\s*\n',
    re.DOTALL
)

# Phase B: Core job specs views
PHASE_B_FILES = [
    'job_pv.php', 'job_ac.php', 'job_swh.php', 'job_saf.php',
    'job_ev.php', 'job_gc.php', 'job_el.php', 'job_service.php',
    'job_wm.php', 'job_tdpb.php', 'job_sp.php', 'job_pl.php',
    'job_pm.php', 'job_rfpm.php',
    # 34181 variants
    'job_pv_34181.php', 'job_ac_34181.php', 'job_swh_34181.php',
    'job_saf_34181.php', 'job_ev_34181.php', 'job_gc_34181.php',
    'job_el_34181.php', 'job_service_34181.php', 'job_wm_34181.php',
    'job_pl_34181.php', 'job_pm_34181.php',
]

# Phase C: Comment/customer/financing/referral/status/permit/document/photos views
PHASE_C_FILES = [
    'job_comment.php', 'job_comment_34181.php',
    'job_customer.php', 'job_customer_34181.php',
    'job_swhcustomer.php', 'job_swhcustomer_34181.php',
    'service_customer.php',
    'el_customer.php',
    'job_financing.php', 'job_financing_34181.php',
    'job_referrals.php', 'job_referrals_34181.php',
    'job_status.php', 'job_status_34181.php',
    'job_permit.php', 'job_permit_34181.php',
    'job_files_status.php', 'job_files_status_34181.php',
    'job_photos.php',
    'fileview.php',
    'add_referral.php', 'edit_referral.php',
    'job_file.php',
]

# Phase D: Remaining financing variants and other active views
PHASE_D_FILES = [
    'ac_financing.php', 'ac_financing_34181.php',
    'ac_financing_viewl.php', 'ac_financing_viewl_34181.php',
    'ac_financing_view.php',
    'el_financing.php',
    'ev_job_financing.php', 'ev_job_financing_34181.php',
    'ev_job_financing_view.php', 'ev_job_financing_view_34181.php',
    'gc_job_financing.php', 'gc_job_financing_34181.php',
    'gc_job_financing_view.php', 'gc_job_financing_view_34181.php',
    'saf_job_financing.php', 'saf_job_financing_34181.php',
    'saf_job_financing_view.php', 'saf_job_financing_view_34181.php',
    'service_financing.php', 'serviceplan_financing.php',
    'tdpb_job_financing.php', 'tdpb_job_financing_view.php',
    'rf_job_financing.php', 'hc_job_financing.php',
    'jobswh_financing.php', 'jobswh_financing_34181.php',
    'jobswh_financing_proposal.php', 'jobswh_financing_proposal_34181.php',
    'jobswh_financing_proposal_view.php', 'jobswh_financing_proposal_view_34181.php',
    'job_financing_view.php',
    'job_pv_proposal.php', 'job_pv_proposal_34181.php',
    'job_customer_proposal.php',
    'job_swh_proposal_pm.php',
    'job_safpm.php',
    'job_file1.php',
    'job_gc1.php',
    # Installer views - these have different tab structure, leaving as-is for now
    # 'job_installer.php', 'job_installer_34181.php',
    # 'job_installer_pv.php', etc.
]


def backup_file(filepath):
    """Copy original file to ORIGINAL/ backup dir."""
    filename = os.path.basename(filepath)
    # For controller, add 'controllers_' prefix
    if 'controllers' in filepath:
        backup_path = os.path.join(BACKUP_DIR, 'controllers_' + filename)
    else:
        backup_path = os.path.join(BACKUP_DIR, filename)

    if not os.path.exists(backup_path):
        shutil.copy2(filepath, backup_path)
        print("  Backed up: %s" % backup_path)
    return backup_path


def phase_a():
    """Add $data['active_tab'] to admin.php controller methods."""
    print("\n=== Phase A: Adding active_tab to admin.php controller ===\n")

    backup_file(CONTROLLER)

    with open(CONTROLLER, 'r') as f:
        content = f.read()

    original_len = len(content)
    changes = 0

    # Define insertions: (search_pattern, replacement_with_active_tab)
    # Strategy: find the line just before view loading where we can insert active_tab

    # 1. job() method - specs - insert before $this->load->view("common/header") at ~line 3492
    # Find the unique context: $data['savedata'] line followed by header load
    marker = "$data['savedata'] =  $this->input->get(\"savedata\");\n                $this->load->view(\"common/header\");"
    if marker in content and "$data['active_tab'] = 'specs'" not in content:
        content = content.replace(marker,
            "$data['savedata'] =  $this->input->get(\"savedata\");\n"
            "                $data['active_tab'] = 'specs';\n"
            "                $this->load->view(\"common/header\");")
        changes += 1
        print("  + Added active_tab='specs' to job() method")

    # 2. job_financing() method - cost
    # Find unique context near line 4350-4360
    # Look for the header load in financing method
    marker2 = "function job_financing($job_id='')"
    if marker2 in content and "$data['active_tab'] = 'cost'" not in content:
        # Find the $this->load->view("common/header") that follows this function
        # We'll use a more targeted approach
        pos = content.find(marker2)
        # Find the next "common/header" after this function
        header_pos = content.find('$this->load->view("common/header");', pos)
        if header_pos > 0:
            # Insert active_tab line before header
            insert_text = "$data['active_tab'] = 'cost';\n\t\t"
            content = content[:header_pos] + insert_text + content[header_pos:]
            changes += 1
            print("  + Added active_tab='cost' to job_financing() method")

    # 3. job_referrals() method - referrals
    marker3 = "function job_referrals($job_id='')"
    if marker3 in content and "$data['active_tab'] = 'referrals'" not in content:
        pos = content.find(marker3)
        header_pos = content.find('$this->load->view("common/header");', pos)
        if header_pos > 0:
            insert_text = "$data['active_tab'] = 'referrals';\n\t\t"
            content = content[:header_pos] + insert_text + content[header_pos:]
            changes += 1
            print("  + Added active_tab='referrals' to job_referrals() method")

    # 4. customer_job() method - customer
    marker4 = "function customer_job($job_id){"
    if marker4 in content and "$data['active_tab'] = 'customer'" not in content:
        pos = content.find(marker4)
        header_pos = content.find('$this->load->view("common/header");', pos)
        if header_pos > 0:
            insert_text = "$data['active_tab'] = 'customer';\n\t\t"
            content = content[:header_pos] + insert_text + content[header_pos:]
            changes += 1
            print("  + Added active_tab='customer' to customer_job() method")

    # 5. job_comment() method - comments
    marker5 = "function job_comment($job_id){"
    if marker5 in content and "$data['active_tab'] = 'comments'" not in content:
        pos = content.find(marker5)
        header_pos = content.find('$this->load->view("common/header");', pos)
        if header_pos > 0:
            insert_text = "$data['active_tab'] = 'comments';\n                "
            content = content[:header_pos] + insert_text + content[header_pos:]
            changes += 1
            print("  + Added active_tab='comments' to job_comment() method")

    # 6. fileview_job() method - reqphotos
    marker6 = "function fileview_job($job_id){"
    if marker6 in content and "$data['active_tab'] = 'reqphotos'" not in content:
        pos = content.find(marker6)
        header_pos = content.find('$this->load->view("common/header");', pos)
        if header_pos > 0:
            insert_text = "$data['active_tab'] = 'reqphotos';\n\t\t"
            content = content[:header_pos] + insert_text + content[header_pos:]
            changes += 1
            print("  + Added active_tab='reqphotos' to fileview_job() method")

    # 7. permit_job() method - permits
    marker7 = "function permit_job($job_id){"
    if marker7 in content and "$data['active_tab'] = 'permits'" not in content:
        pos = content.find(marker7)
        header_pos = content.find('$this->load->view("common/header");', pos)
        if header_pos > 0:
            insert_text = "$data['active_tab'] = 'permits';\n\t\t"
            content = content[:header_pos] + insert_text + content[header_pos:]
            changes += 1
            print("  + Added active_tab='permits' to permit_job() method")

    # 8. job_photos() method - photos
    marker8 = "function job_photos($job_id){"
    if marker8 in content and "$data['active_tab'] = 'photos'" not in content:
        pos = content.find(marker8)
        header_pos = content.find('$this->load->view("common/header");', pos)
        if header_pos > 0:
            insert_text = "$data['active_tab'] = 'photos';\n\t\t"
            content = content[:header_pos] + insert_text + content[header_pos:]
            changes += 1
            print("  + Added active_tab='photos' to job_photos() method")

    # 9. job_status - look for job_status function
    # This may be missing from admin.php (handled by job_status_tab controller)
    marker9a = "function job_status($job_id){"
    marker9b = "function job_status($job_id) {"
    found_status = False
    for m in [marker9a, marker9b]:
        if m in content:
            found_status = True
            if "$data['active_tab'] = 'status'" not in content:
                pos = content.find(m)
                header_pos = content.find('$this->load->view("common/header");', pos)
                if header_pos > 0:
                    insert_text = "$data['active_tab'] = 'status';\n\t\t"
                    content = content[:header_pos] + insert_text + content[header_pos:]
                    changes += 1
                    print("  + Added active_tab='status' to job_status() method")
            break
    if not found_status:
        print("  ! job_status() method not found in admin.php (may be in separate controller)")

    # 10. job_file_status() method - documents
    marker10 = "function job_file_status($job_id){"
    if marker10 in content and "$data['active_tab'] = 'documents'" not in content:
        pos = content.find(marker10)
        header_pos = content.find('$this->load->view("common/header");', pos)
        if header_pos > 0:
            insert_text = "$data['active_tab'] = 'documents';\n\t\t"
            content = content[:header_pos] + insert_text + content[header_pos:]
            changes += 1
            print("  + Added active_tab='documents' to job_file_status() method")

    # 11. job_installer / installers_job - might load job_installer views
    # Find the function that loads job_installer views
    marker11 = 'function job_installer($job_id){'
    if marker11 in content:
        print("  ! job_installer() found - installer views are out of scope for this ENH")

    if changes > 0:
        with open(CONTROLLER, 'w') as f:
            f.write(content)
        print("\n  Total: %d active_tab assignments added to admin.php" % changes)
    else:
        print("\n  No changes needed (already applied or methods not found)")

    return changes


def replace_nav_tabs_in_file(filepath):
    """Replace inline nav tabs block with shared partial include."""
    if not os.path.exists(filepath):
        print("  SKIP (not found): %s" % os.path.basename(filepath))
        return False

    with open(filepath, 'r') as f:
        content = f.read()

    if 'generic-nav-tabs' not in content:
        print("  SKIP (no nav tabs): %s" % os.path.basename(filepath))
        return False

    if "load->view('common/job_nav_tabs')" in content:
        print("  SKIP (already updated): %s" % os.path.basename(filepath))
        return False

    # Backup original
    backup_file(filepath)

    # Find and replace the nav tabs block
    match = NAV_TABS_PATTERN.search(content)
    if not match:
        print("  WARN (regex no match): %s - manual review needed" % os.path.basename(filepath))
        return False

    # Get the indentation from the matched block
    leading_ws = match.group(1)

    # Replace the nav tabs block with the include
    new_content = content[:match.start()] + leading_ws + INCLUDE_LINE + content[match.end():]

    # Remove standalone $deal_type extraction code (now handled by partial)
    # Only remove if $deal_type is not used elsewhere in the file
    deal_match = DEAL_TYPE_ONLY_PATTERN.search(new_content)
    if deal_match:
        # Check if $deal_type is used elsewhere in the file
        before = new_content[:deal_match.start()]
        after = new_content[deal_match.end():]
        if '$deal_type' not in after and '@$deal_type' not in after:
            new_content = new_content[:deal_match.start()] + '\n' + new_content[deal_match.end():]
            print("  - Removed $deal_type if-block")

            # Now check if $CI =& get_instance() is still used elsewhere
            ci_match = re.search(r'\n(\s*\$CI\s*=\s*&?\s*get_instance\(\);\s*)\n', new_content)
            if ci_match:
                after_ci = new_content[ci_match.end():]
                if '$CI' not in after_ci:
                    new_content = new_content[:ci_match.start()] + '\n' + new_content[ci_match.end():]
                    print("  - Removed unused $CI =& get_instance()")
        else:
            print("  ! Kept $deal_type extraction (used elsewhere in file)")

    with open(filepath, 'w') as f:
        f.write(new_content)

    print("  OK: %s" % os.path.basename(filepath))
    return True


def run_phase(phase_name, file_list):
    """Run a phase of view replacements."""
    print("\n=== Phase %s: Replacing nav tabs in %d files ===" % (phase_name, len(file_list)))

    success = 0
    failed = 0
    skipped = 0

    for filename in file_list:
        filepath = os.path.join(VIEWS, filename)
        result = replace_nav_tabs_in_file(filepath)
        if result:
            success += 1
        elif result is False and os.path.exists(filepath):
            skipped += 1
        else:
            failed += 1

    print("\n  Phase %s complete: %d updated, %d skipped" % (phase_name, success, skipped))
    return success


def main():
    if len(sys.argv) < 3 or sys.argv[1] != '--phase':
        print(__doc__)
        sys.exit(1)

    phase = sys.argv[2].upper()

    # Ensure backup dir exists
    os.makedirs(BACKUP_DIR, exist_ok=True)

    if phase in ('A', 'ALL'):
        phase_a()

    if phase in ('B', 'ALL'):
        run_phase('B', PHASE_B_FILES)

    if phase in ('C', 'ALL'):
        run_phase('C', PHASE_C_FILES)

    if phase in ('D', 'ALL'):
        run_phase('D', PHASE_D_FILES)

    print("\n=== Done ===")


if __name__ == '__main__':
    main()
