import os
import subprocess
from multiprocessing.pool import ThreadPool
from datetime import datetime
import shutil
from ..utils.utils import execute_command, process_logger, log_message


def gen_ifgs(paths, mst, filter_wavelength, rng, az, ncores, console_text=None, log_file_path=None):
    """
    Generate interferograms with option to skip existing pairs.
    """
    # Create detailed log file for command outputs and troubleshooting
    detailed_log_path = None
    if log_file_path:
        log_dir = os.path.dirname(log_file_path)
        log_name = os.path.basename(log_file_path)
        detailed_log_name = f"detailed_{log_name}"
        detailed_log_path = os.path.join(log_dir, detailed_log_name)
        
        # Initialize detailed log file
        with open(detailed_log_path, 'a') as f:
            f.write(f"\n{'='*80}\n")
            f.write(f"IFG Generation Session Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"{'='*80}\n\n")
    
    # Get existing pairs from paths if provided
    existing_pairs = paths.get('existing_pairs', set())
    if existing_pairs is None:
        existing_pairs = set()
        
    for key in ["pF1", "pF2", "pF3"]:
        dir_path = paths.get(key)
        # Skip if this subswath is disabled (set to None for single subswath processing)
        if dir_path is None:
            continue
            
        subswath_map = {"pF1": "2.2.1", "pF2": "2.2.2", "pF3": "2.2.3"}
        process_num = subswath_map.get(key, "2.2.x")
        process_logger(process_num=process_num, log_file=paths.get("log_file_path"), message=f"Starting IFG generation for subswath {key} (process {process_num})...", mode="start")
        
        if dir_path and os.path.exists(dir_path):

            ind = os.path.join(dir_path, "intf.in")
            fmst = 'S1_' + mst.replace("-", "") + f'_ALL_F{key[-1]}'
            con = os.path.join(dir_path, "batch_tops.config")            
            os.chdir(dir_path)
            with open(con, 'r') as f:
                lines = f.readlines()
            with open(con, 'w') as f:
                for line in lines:
                    if 'master_image' in line:
                        line = 'master_image = ' + fmst + '\n'
                    if 'proc_stage' in line:
                        line = 'proc_stage = 1\n'
                    if 'topo_phase' in line:
                        line = 'topo_phase = 1\n'
                    if 'shift_topo' in line:
                        line = 'shift_topo = 0\n'
                    if 'filter_wavelength' in line:
                        line = f'filter_wavelength = {filter_wavelength}\n'
                    if 'range_dec' in line:
                        line = f'range_dec = {rng}\n'
                    if 'azimuth_dec' in line:
                        line = f'azimuth_dec = {az}\n'
                    if 'threshold_snaphu' in line:
                        line = 'threshold_snaphu = 0\n'
                    if 'threshold_geocode' in line:
                        line = 'threshold_geocode = 0\n'
                    f.write(line)
            print(f"Generating interferograms for {key} ...")
            # Checking if first IFG is generated using topo_ra.grd as indicator
            topo_dir = os.path.join(dir_path, 'topo')
            topo_ra_file = os.path.join(topo_dir, 'topo_ra.grd')

            print('Checking first interferogram generation status...')
            if os.path.exists(topo_ra_file):
                print('First IFG for {} already generated (topo_ra.grd found)'.format(os.path.basename(dir_path)))
            else:
                # Generate first IFG
                print('Generating first interferogram for {} ...'.format(os.path.basename(dir_path)))
                subprocess.call('head -1 intf.in>one.in', shell=True)
                process_logger(process_num=f"{process_num}.1", log_file=paths.get("log_file_path"), message=f"Starting first IFG generation for subswath {key} (process {process_num}.1)...", mode="start")
                subprocess.call('intf_tops.csh one.in batch_tops.config', shell=True)
                
                # Verify first interferogram was generated by checking topo_ra.grd
                if os.path.exists(topo_ra_file):
                    print("First interferogram for {} generated successfully (topo_ra.grd created)".format(os.path.basename(dir_path)))
                    process_logger(process_num=f"{process_num}.1", log_file=paths.get("log_file_path"), message=f"First IFG generation for subswath {key} (process {process_num}.1) generated successfully.", mode="end")
                else:
                    raise RuntimeError('First interferogram generation failed - topo_ra.grd not found. Please check the log file for more details.')

            # Get completed interferograms from both intf and intf_all directories
            def get_completed_interferograms():
                """Get set of completed interferogram pairs by checking for corr.grd files."""
                completed_pairs = set()
                
                # Check intf directory
                intf_dir = os.path.join(dir_path, 'intf')
                if os.path.exists(intf_dir):
                    for item in os.listdir(intf_dir):
                        item_path = os.path.join(intf_dir, item)
                        if os.path.isdir(item_path):
                            corr_file = os.path.join(item_path, 'corr.grd')
                            if os.path.exists(corr_file):
                                completed_pairs.add(item)
                
                # Check intf_all directory
                intf_all_dir = os.path.join(dir_path, 'intf_all')
                if os.path.exists(intf_all_dir):
                    for item in os.listdir(intf_all_dir):
                        item_path = os.path.join(intf_all_dir, item)
                        if os.path.isdir(item_path):
                            corr_file = os.path.join(item_path, 'corr.grd')
                            if os.path.exists(corr_file):
                                completed_pairs.add(item)
                
                return completed_pairs
            
            def backup_existing_interferograms():
                """Backup existing interferogram directories with timestamp."""
                timestamp = datetime.now().strftime('%Y%m%d_%H%M')
                backup_paths = []
                
                # Backup intf directory if it exists
                intf_dir = os.path.join(dir_path, 'intf')
                if os.path.exists(intf_dir):
                    intf_backup = f"{intf_dir}_{timestamp}"
                    print(f"Backing up existing intf directory: {intf_dir} -> {intf_backup}")
                    if detailed_log_path:
                        log_message(detailed_log_path, f"Backing up existing intf directory: {intf_dir} -> {intf_backup}")
                    shutil.move(intf_dir, intf_backup)
                    backup_paths.append(('intf', intf_backup))
                
                # Backup intf_all directory if it exists
                intf_all_dir = os.path.join(dir_path, 'intf_all')
                if os.path.exists(intf_all_dir):
                    intf_all_backup = f"{intf_all_dir}_{timestamp}"
                    print(f"Backing up existing intf_all directory: {intf_all_dir} -> {intf_all_backup}")
                    if detailed_log_path:
                        log_message(detailed_log_path, f"Backing up existing intf_all directory: {intf_all_dir} -> {intf_all_backup}")
                    shutil.move(intf_all_dir, intf_all_backup)
                    backup_paths.append(('intf_all', intf_all_backup))
                
                return backup_paths
            
            def restore_existing_interferograms(backup_paths):
                """Restore interferograms from backup directories to current intf_all."""
                target_intf_all = os.path.join(dir_path, 'intf_all')
                
                # Ensure target directory exists
                if not os.path.exists(target_intf_all):
                    os.makedirs(target_intf_all)
                
                for dir_type, backup_path in backup_paths:
                    if os.path.exists(backup_path):
                        print(f"Restoring interferograms from {backup_path} to {target_intf_all}")
                        if detailed_log_path:
                            log_message(detailed_log_path, f"Restoring interferograms from {backup_path} to {target_intf_all}")
                        
                        # Move all interferogram directories from backup to intf_all
                        for item in os.listdir(backup_path):
                            item_path = os.path.join(backup_path, item)
                            target_path = os.path.join(target_intf_all, item)
                            
                            if os.path.isdir(item_path):
                                # Check if corr.grd exists (valid interferogram)
                                corr_file = os.path.join(item_path, 'corr.grd')
                                if os.path.exists(corr_file):
                                    if os.path.exists(target_path):
                                        print(f"  Skipping {item} - already exists in target")
                                        if detailed_log_path:
                                            log_message(detailed_log_path, f"  Skipping {item} - already exists in target")
                                    else:
                                        print(f"  Moving {item} to intf_all")
                                        if detailed_log_path:
                                            log_message(detailed_log_path, f"  Moving {item} to intf_all")
                                        shutil.move(item_path, target_path)
                                else:
                                    print(f"  Skipping {item} - no corr.grd found (incomplete)")
                                    if detailed_log_path:
                                        log_message(detailed_log_path, f"  Skipping {item} - no corr.grd found (incomplete)")
                        
                        # Remove empty backup directory
                        try:
                            if not os.listdir(backup_path):
                                os.rmdir(backup_path)
                                print(f"Removed empty backup directory: {backup_path}")
                                if detailed_log_path:
                                    log_message(detailed_log_path, f"Removed empty backup directory: {backup_path}")
                        except OSError:
                            print(f"Could not remove backup directory: {backup_path} (not empty)")
                            if detailed_log_path:
                                log_message(detailed_log_path, f"Could not remove backup directory: {backup_path} (not empty)")
            
            def cleanup_invalid_directories():
                """Remove directories that don't have corr.grd files (incomplete/invalid)."""
                cleaned_count = 0
                
                # Clean intf directory
                intf_dir = os.path.join(dir_path, 'intf')
                if os.path.exists(intf_dir):
                    for item in os.listdir(intf_dir):
                        item_path = os.path.join(intf_dir, item)
                        if os.path.isdir(item_path):
                            corr_file = os.path.join(item_path, 'corr.grd')
                            if not os.path.exists(corr_file):
                                print(f"Removing incomplete directory: {item_path}")
                                if detailed_log_path:
                                    log_message(detailed_log_path, f"Removing incomplete directory: {item_path}")
                                subprocess.call(f"rm -rf {item_path}", shell=True)
                                cleaned_count += 1
                
                # Clean intf_all directory  
                intf_all_dir = os.path.join(dir_path, 'intf_all')
                if os.path.exists(intf_all_dir):
                    for item in os.listdir(intf_all_dir):
                        item_path = os.path.join(intf_all_dir, item)
                        if os.path.isdir(item_path):
                            corr_file = os.path.join(item_path, 'corr.grd')
                            if not os.path.exists(corr_file):
                                print(f"Removing incomplete directory: {item_path}")
                                if detailed_log_path:
                                    log_message(detailed_log_path, f"Removing incomplete directory: {item_path}")
                                subprocess.call(f"rm -rf {item_path}", shell=True)
                                cleaned_count += 1
                
                if cleaned_count > 0:
                    print(f"Cleaned up {cleaned_count} incomplete interferogram directories")
                    if detailed_log_path:
                        log_message(detailed_log_path, f"Cleaned up {cleaned_count} incomplete interferogram directories")
                
                return cleaned_count
            
            # Cleanup incomplete directories first
            cleanup_invalid_directories()
            
            # Get completed interferograms after cleanup
            completed_ifgs = get_completed_interferograms()
            
            # Count expected interferograms
            expected_count = 0
            if os.path.exists(ind):
                with open(ind, "r") as f:
                    expected_count = sum(1 for _ in f)
            
            print(f"Found {len(completed_ifgs)} completed interferograms out of {expected_count} total for {key}")
            if len(completed_ifgs) > 0:
                print(f"Sample completed interferograms: {list(completed_ifgs)[:5]}")
            
            if len(completed_ifgs) >= expected_count and expected_count > 0:
                print('All IFGs for {} are already completed'.format(os.path.basename(dir_path)))

            else:
                print(f'Generating remaining IFGs for {dir_path} ...')
                
                # Backup existing interferogram directories if they exist
                backup_paths = []
                if len(completed_ifgs) > 0:
                    backup_paths = backup_existing_interferograms()
                
                # Change proc_stage = 2 in batch_tops.config file
                with open(con, 'r') as f:
                    lines = f.readlines()
                with open(con, 'w') as f:
                    for line in lines:
                        if 'proc_stage' in line:
                            line = 'proc_stage = 2\n'
                        f.write(line)
                
                ain1 = []
                skipped_pairs = []
                total_pairs_checked = 0

                def convert_yyyymmdd_to_yyyyddd(date_str):
                    """Convert YYYYMMDD to YYYYDDD format with GMTSAR's 1-day offset.
                    GMTSAR uses 1-day offset: 20180112 -> 2018011 (not 2018012)"""
                    import datetime
                    try:
                        date_obj = datetime.datetime.strptime(date_str, '%Y%m%d')
                        day_of_year = date_obj.timetuple().tm_yday
                        # Apply GMTSAR's 1-day offset
                        gmtsar_day = day_of_year - 1
                        return f"{date_obj.year}{gmtsar_day:03d}"
                    except:
                        return date_str

                with open(ind, "r") as intf_file:
                    for intf in intf_file:
                        intf = intf.strip()
                        date1 = intf.split(":")[0][3:11]
                        date2 = intf.split(":")[1][3:11]
                        
                        # Convert to day-of-year format to match completed_ifgs format
                        date1_ddd = convert_yyyymmdd_to_yyyyddd(date1)
                        date2_ddd = convert_yyyymmdd_to_yyyyddd(date2)
                        pair_name_ddd = f"{date1_ddd}_{date2_ddd}"
                        
                        # Keep original format for file naming
                        pair_name = f"{date1}_{date2}"
                        total_pairs_checked += 1
                        
                        # Debug: Show first few pair names and check
                        if total_pairs_checked <= 3:
                            print(f"Checking pair {total_pairs_checked}: {pair_name} -> {pair_name_ddd} (from line: {intf})")
                            print(f"  In completed_ifgs: {pair_name_ddd in completed_ifgs}")
                            print(f"  In existing_pairs: {pair_name in existing_pairs}")
                        
                        # Skip if this pair is already completed (has corr.grd) - check using DDD format
                        if pair_name_ddd in completed_ifgs:
                            skipped_pairs.append(pair_name)
                            continue
                        
                        # Also check against existing_pairs from paths if provided
                        if pair_name in existing_pairs:
                            skipped_pairs.append(pair_name)
                            continue
                            
                        infile = f"intf_{pair_name}.in"
                        with open(infile, "w") as in_file:
                            in_file.write(intf)
                            ain1.append(in_file.name)

                if skipped_pairs:
                    print(f"Skipped {len(skipped_pairs)} existing interferogram pairs for {key}")
                
                if not ain1:
                    print(f"All interferograms already exist for {key}, skipping generation")
                    process_logger(process_num=f"{process_num}.2", log_file=paths.get("log_file_path"), message=f"All IFGs already exist for subswath {key} (process {process_num}.2), skipping generation.", mode="end")
                else:
                    remaining_count = len(ain1)
                    total_count = expected_count
                    completed_count = len(completed_ifgs)
                    
                    print(f"Processing {remaining_count} remaining interferograms for {key} (skipping {len(skipped_pairs)} existing)")
                    
                    # Create a logging function for this subprocess
                    def ifg_logger(message, process_num=None):
                        if message.strip():  # Only log non-empty messages
                            # Log to time-tracking log file
                            process_logger(
                                message=message.strip(),
                                process_num=process_num,
                                log_file=paths.get("log_file_path")
                            )
                            # Also log to detailed log file for troubleshooting
                            if detailed_log_path:
                                timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
                                detailed_message = f"[{timestamp}] Process-{process_num}: {message.strip()}"
                                log_message(detailed_log_path, detailed_message)
                    
                    # Prepare commands for IFGs generation (only for non-existing pairs)
                    bash_commands1 = [
                        "intf_tops.csh {} batch_tops.config".format(i) for i in ain1
                    ]
                    process_logger(process_num=f"{process_num}.2", log_file=paths.get("log_file_path"), message=f"Starting {remaining_count} remaining IFGs generation for subswath {key} (process {process_num}.2) - {completed_count}/{total_count} already completed...", mode="start")
                    
                    if detailed_log_path:
                        log_message(detailed_log_path, f"\n--- Starting IFG Generation for {key} ---")
                        log_message(detailed_log_path, f"Commands to execute: {len(bash_commands1)}")
                        log_message(detailed_log_path, f"Process cores: {ncores}")
                        log_message(detailed_log_path, f"Working directory: {dir_path}")
                        for i, cmd in enumerate(bash_commands1[:5]):  # Log first 5 commands as sample
                            log_message(detailed_log_path, f"  Sample command {i+1}: {cmd}")
                        if len(bash_commands1) > 5:
                            log_message(detailed_log_path, f"  ... and {len(bash_commands1)-5} more commands")
                        log_message(detailed_log_path, "")
                    
                    # Create wrapper function to pass logging info to execute_command
                    def execute_with_logging(command):
                        if detailed_log_path:
                            log_message(detailed_log_path, f"Executing: {command}")
                        return execute_command(
                            command, 
                            log_func=ifg_logger, 
                            process_num=f"{process_num}.2"
                        )
                    
                    # Create a thread pool with a maximum of n threads
                    with ThreadPool(processes=ncores) as pool:
                        # Execute bash commands in parallel with logging
                        pool.map(execute_with_logging, bash_commands1)

                    process_logger(process_num=f"{process_num}.2", log_file=paths.get("log_file_path"), message=f"Remaining {remaining_count} IFGs generation for subswath {key} (process {process_num}.2) completed successfully.", mode="end")
                    
                    if detailed_log_path:
                        log_message(detailed_log_path, f"--- Completed IFG Generation for {key} ---\n")
                    
                    # Restore existing interferograms from backup
                    if backup_paths:
                        restore_existing_interferograms(backup_paths)
            
            # Log completion for this subswath
            process_logger(process_num=process_num, log_file=paths.get("log_file_path"), message=f"IFG generation for subswath {key} (process {process_num}) completed successfully.", mode="end")