#!/usr/bin/env python3
"""
Create a new glider mission directory from the most recent mission.

This script:
1. Finds the most recent mission for a specified glider
2. Copies the directory structure to a new mission with a new date
3. Updates deploymentRealtime.yml with new deployment info
4. Cleans out data files (equivalent to 'make clean')
"""

import os
import shutil
import yaml
from datetime import datetime, timedelta
from pathlib import Path
import argparse
import readline


# ============================================================================
# Interactive prompts with pre-filled editable values
# ============================================================================

def prompt_with_prefill(prompt_text, prefill=''):
    """Prompt with a pre-filled editable value using readline."""
    def hook():
        readline.insert_text(prefill)
        readline.redisplay()
    readline.set_pre_input_hook(hook)
    try:
        return input(prompt_text)
    finally:
        readline.set_pre_input_hook()


def prompt_for_metadata(previous_yaml_path):
    """Prompt for key metadata fields with previous values pre-filled.

    Returns dict with updated values, or None if user skips.
    """
    # Load previous YAML
    with open(previous_yaml_path, 'r') as f:
        prev_config = yaml.safe_load(f)
    prev_meta = prev_config.get('metadata', {})

    print("\n" + "="*70)
    print("Update deployment metadata (press Enter to keep current value)")
    print("="*70)

    updated = {}

    # Comment
    prev_comment = prev_meta.get('comment', '')
    comment = prompt_with_prefill(f"Comment: ", prev_comment)
    if comment != prev_comment:
        updated['comment'] = comment

    # Project
    prev_project = prev_meta.get('project', '')
    project = prompt_with_prefill(f"Project: ", prev_project)
    if project != prev_project:
        updated['project'] = project

    # Summary (can be multiline, just get first line for editing)
    prev_summary = prev_meta.get('summary', '')
    if isinstance(prev_summary, str) and prev_summary:
        # For multiline, just show first part
        summary_preview = prev_summary[:100] + '...' if len(prev_summary) > 100 else prev_summary
        summary = prompt_with_prefill(f"Summary: ", prev_summary)
        if summary != prev_summary:
            updated['summary'] = summary

    # Deployment-specific fields
    print()  # Blank line for readability

    # Deployment vessel
    prev_vessel = prev_meta.get('deployment_vessel', '')
    vessel = prompt_with_prefill(f"Deployment vessel: ", prev_vessel)
    if vessel != prev_vessel:
        updated['deployment_vessel'] = vessel

    # Deployment station
    prev_station = prev_meta.get('deployment_station', '')
    station = prompt_with_prefill(f"Deployment station: ", prev_station)
    if station != prev_station:
        updated['deployment_station'] = station

    # Deployment latitude
    prev_lat = prev_meta.get('deployment_latitude', '')
    lat = prompt_with_prefill(f"Deployment latitude: ", prev_lat)
    if lat != prev_lat:
        updated['deployment_latitude'] = lat

    # Deployment longitude
    prev_lon = prev_meta.get('deployment_longitude', '')
    lon = prompt_with_prefill(f"Deployment longitude: ", prev_lon)
    if lon != prev_lon:
        updated['deployment_longitude'] = lon

    print("="*70)

    return updated if updated else None


# ============================================================================
# Core functions
# ============================================================================

def find_latest_mission(glider_name, deployments_dir):
    """Find the most recent mission directory for a glider."""
    glider_dir = Path(deployments_dir) / glider_name

    if not glider_dir.exists():
        raise ValueError(f"Glider directory not found: {glider_dir}")

    # Find all mission directories for this glider
    missions = []
    for item in glider_dir.iterdir():
        if item.is_dir() and item.name.startswith(f"{glider_name}-"):
            try:
                # Extract date from directory name
                date_str = item.name.split('-')[-1]
                date = datetime.strptime(date_str, '%Y%m%d')
                missions.append((date, item))
            except (ValueError, IndexError):
                continue

    if not missions:
        raise ValueError(f"No mission directories found for {glider_name}")

    # Sort by date and return the most recent
    missions.sort(reverse=True)
    return missions[0][1]


def clean_data_files(mission_dir):
    """Remove data files from the new mission directory (equivalent to 'make clean')."""
    dirs_to_clean = [
        'realtime_raw',
        'L0-timeseries',
        'L0-profiles',
        'L0-profiles-IOOS',
        'L0-gridfiles',
        'figs',
        'logs',
        'delayed_raw',
        'Nav-timeseries',
    ]

    files_to_remove = [
        '*.kml',
        'index.html',
        '.processMap.log',
        '.process.log',
        'stdout.log',
        'stderr.log',
        'logsummary.nc',
    ]

    print(f"  Cleaning data files from {mission_dir.name}...")

    # Clean directories
    for dir_name in dirs_to_clean:
        dir_path = mission_dir / dir_name
        if dir_path.exists():
            for file in dir_path.iterdir():
                if file.is_file():
                    file.unlink()
            print(f"    Cleaned {dir_name}/")

    # Remove specific files
    for pattern in files_to_remove:
        if '*' in pattern:
            # Handle wildcards
            for file in mission_dir.glob(pattern):
                if file.is_file():
                    file.unlink()
                    print(f"    Removed {file.name}")
        else:
            file_path = mission_dir / pattern
            if file_path.exists():
                file_path.unlink()
                print(f"    Removed {pattern}")


def get_previous_deployment_id(mission_dir):
    """Get the deployment_id from the previous mission's deploymentRealtime.yml."""
    yaml_path = mission_dir / 'deploymentRealtime.yml'
    if not yaml_path.exists():
        return None

    try:
        with open(yaml_path, 'r') as f:
            config = yaml.safe_load(f)

        deployment_id = config.get('metadata', {}).get('deployment_id')
        if deployment_id:
            return int(deployment_id)
    except (ValueError, KeyError):
        pass

    return None


def update_deployment_yaml(yaml_path, deployment_name, start_date, end_date, deployment_id, metadata_updates=None):
    """Update deploymentRealtime.yml with new deployment information using text replacement to preserve formatting."""

    # Read the file as text
    with open(yaml_path, 'r') as f:
        content = f.read()

    # Use regex/text replacement for specific fields to preserve formatting
    import re

    # Update deployment_name
    content = re.sub(
        r"(deployment_name:\s*['\"]?)([^'\"'\n]+)(['\"]?)",
        rf"\g<1>{deployment_name}\g<3>",
        content
    )

    # Update deployment_start
    content = re.sub(
        r"(deployment_start:\s*['\"]?)([^'\"'\n]+)(['\"]?)",
        rf"\g<1>{start_date.strftime('%Y-%m-%d')}\g<3>",
        content
    )

    # Update deployment_end
    content = re.sub(
        r"(deployment_end:\s*['\"]?)([^'\"'\n]+)(['\"]?)",
        rf"\g<1>{end_date.strftime('%Y-%m-%d')}\g<3>",
        content
    )

    # Update deployment_id
    content = re.sub(
        r"(deployment_id:\s*['\"]?)([^'\"'\n]+)(['\"]?)",
        rf"\g<1>{deployment_id}\g<3>",
        content
    )

    # Apply any additional metadata updates
    if metadata_updates:
        for key, value in metadata_updates.items():
            # Escape special regex characters in the value
            escaped_value = re.escape(str(value))
            # This pattern handles various YAML formats
            pattern = rf"({key}:\s*['\"]?)([^'\"'\n]*)(['\"]?)"
            replacement = rf"\g<1>{value}\g<3>"
            content = re.sub(pattern, replacement, content)

    # Write back
    with open(yaml_path, 'w') as f:
        f.write(content)

    print(f"  Updated {yaml_path.name}")
    print(f"    deployment_id: {deployment_id}")
    if metadata_updates:
        for key in metadata_updates:
            print(f"    {key}: updated")


def update_process_deployment_script(script_path, new_mission_name):
    """Update process_deploymentRealtime.py with new ioosdeployment string."""
    import re

    if not script_path.exists():
        return

    # Read the file
    with open(script_path, 'r') as f:
        content = f.read()

    # Pattern to find ioosdeployment lines
    # Look for lines like: ioosdeployment =  'dfo-hal1002-20251201T0000'
    pattern = r"(ioosdeployment\s*=\s*['\"])([^'\"]+)(['\"])"

    # Find the old value to comment out
    matches = list(re.finditer(pattern, content))

    if matches:
        # Get the last match (the active one)
        last_match = matches[-1]
        old_deployment = last_match.group(2)

        # Create new deployment string (mission name + T0000)
        new_deployment = f"{new_mission_name}T0000"

        # Replace with commented out new deployment (needs manual activation)
        new_line = f"# {last_match.group(1)}{new_deployment}{last_match.group(3)}  # Uncomment after manual deployment registration"

        content = content[:last_match.start()] + new_line + content[last_match.end():]

        # Write back
        with open(script_path, 'w') as f:
            f.write(content)

        print(f"  Updated {script_path.name}")
        print(f"    ioosdeployment: {old_deployment} → {new_deployment} (commented out)")
        print(f"    Note: Uncomment after manual deployment registration")
    else:
        print(f"  Warning: Could not find ioosdeployment line in {script_path.name}")


def create_new_mission(glider_name, start_date, deployments_dir, duration_months=3,
                       deployment_id=None, no_clean=False, edit_metadata=False):
    """Create a new mission directory."""
    deployments_path = Path(deployments_dir)

    # Find the latest mission
    print(f"Finding latest mission for {glider_name}...")
    latest_mission = find_latest_mission(glider_name, deployments_dir)
    print(f"  Found: {latest_mission.name}")

    # Get previous deployment_id and auto-increment if not specified
    if deployment_id is None:
        prev_id = get_previous_deployment_id(latest_mission)
        if prev_id is not None:
            deployment_id = prev_id + 1
            print(f"  Previous deployment_id: {prev_id}, using {deployment_id}")
        else:
            deployment_id = 1
            print(f"  No previous deployment_id found, using {deployment_id}")

    # Create new mission name
    start_date_str = start_date.strftime('%Y%m%d')
    new_mission_name = f"{glider_name}-{start_date_str}"
    new_mission_dir = deployments_path / glider_name / new_mission_name

    if new_mission_dir.exists():
        raise ValueError(f"Mission directory already exists: {new_mission_dir}")

    # Define directories to skip during copy (will be created empty)
    skip_dirs = {
        'realtime_raw',
        'delayed_raw',
        'L0-timeseries',
        'L0-gridfiles',
        'L0-profiles',
        'L0-profiles-IOOS',
        'figs',
        'logs',
        'Nav-timeseries',
    }

    def ignore_large_data_dirs(directory, contents):
        """Ignore function for copytree - skip contents of large data directories."""
        dir_name = os.path.basename(directory)
        if dir_name in skip_dirs:
            # Return all contents to skip everything in this directory
            return contents
        return []

    # Copy the directory, skipping large data directories
    print(f"\nCopying {latest_mission.name} -> {new_mission_name}...")
    print(f"  Skipping contents of: {', '.join(sorted(skip_dirs))}")
    shutil.copytree(latest_mission, new_mission_dir, ignore=ignore_large_data_dirs)
    print(f"  ✓ Directory copied")

    # Clean data files (unless --no-clean specified)
    if not no_clean:
        print()
        clean_data_files(new_mission_dir)

    # Update deploymentRealtime.yml
    print()
    yaml_path = new_mission_dir / 'deploymentRealtime.yml'
    end_date = start_date + timedelta(days=duration_months * 30)

    # Prompt for metadata updates if requested
    metadata_updates = None
    if edit_metadata:
        prev_yaml_path = latest_mission / 'deploymentRealtime.yml'
        metadata_updates = prompt_for_metadata(prev_yaml_path)

    update_deployment_yaml(yaml_path, new_mission_name, start_date, end_date, deployment_id, metadata_updates)

    # Update process_deploymentRealtime.py with new ioosdeployment
    print()
    script_path = new_mission_dir / 'process_deploymentRealtime.py'
    update_process_deployment_script(script_path, new_mission_name)

    # Remove .geojson file - process_deploymentRealtime will create it
    old_geojson = new_mission_dir / f"{latest_mission.name}.geojson"
    if old_geojson.exists():
        old_geojson.unlink()
        print(f"  Removed {old_geojson.name} (will be regenerated)")

    print(f"\n✓ New mission created: {new_mission_dir}")
    print(f"  Deployment: {new_mission_name}")
    print(f"  Start: {start_date.strftime('%Y-%m-%d')}")
    print(f"  End: {end_date.strftime('%Y-%m-%d')}")

    return new_mission_dir


def main():
    parser = argparse.ArgumentParser(
        description='Create a new glider mission from the most recent mission.',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Create mission with prompts
  python create_new_mission.py

  # Create mission with arguments
  python create_new_mission.py --glider dfo-hal1002 --date 2026-05-01

  # Specify duration and deployment ID
  python create_new_mission.py --glider dfo-eva035 --date 2026-06-15 --duration 4 --id 12

  # Keep data files (don't run clean)
  python create_new_mission.py --glider dfo-hal1002 --date 2026-05-01 --no-clean
        """
    )

    parser.add_argument(
        '--glider', '-g',
        help='Glider name (e.g., dfo-hal1002)',
    )
    parser.add_argument(
        '--date', '-d',
        help='Mission start date (YYYY-MM-DD or YYYYMMDD)',
    )
    parser.add_argument(
        '--duration', '-m',
        type=int,
        default=3,
        help='Mission duration in months (default: 3)',
    )
    parser.add_argument(
        '--id',
        help='Deployment ID (optional)',
    )
    parser.add_argument(
        '--deployments-dir',
        default=os.path.dirname(os.path.abspath(__file__)),
        help='Path to deployments directory (default: current script directory)',
    )
    parser.add_argument(
        '--no-clean',
        action='store_true',
        help='Do not clean data files (skip "make clean" equivalent)',
    )
    parser.add_argument(
        '--edit-metadata', '-e',
        action='store_true',
        help='Prompt to edit metadata fields (comment, project, summary) with previous values pre-filled',
    )

    args = parser.parse_args()

    # Interactive mode if arguments not provided
    glider_name = args.glider
    if not glider_name:
        glider_name = input("Glider name (e.g., dfo-hal1002): ").strip()

    start_date_str = args.date
    if not start_date_str:
        start_date_str = input("Mission start date (YYYY-MM-DD or YYYYMMDD): ").strip()

    # Parse date
    try:
        if len(start_date_str) == 8 and start_date_str.isdigit():
            start_date = datetime.strptime(start_date_str, '%Y%m%d')
        else:
            start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
    except ValueError:
        print(f"Error: Invalid date format: {start_date_str}")
        print("Use YYYY-MM-DD or YYYYMMDD")
        return 1

    duration_months = args.duration
    if not args.date and not args.glider:
        # Only prompt if in interactive mode
        duration_input = input(f"Mission duration in months (default: {duration_months}): ").strip()
        if duration_input:
            try:
                duration_months = int(duration_input)
            except ValueError:
                print(f"Warning: Invalid duration, using default: {duration_months}")

    deployment_id = args.id
    if not deployment_id and not args.date and not args.glider:
        deployment_id = input("Deployment ID (optional, press Enter to skip): ").strip() or None

    try:
        create_new_mission(
            glider_name=glider_name,
            start_date=start_date,
            deployments_dir=args.deployments_dir,
            duration_months=duration_months,
            deployment_id=deployment_id,
            no_clean=args.no_clean,
            edit_metadata=args.edit_metadata
        )
        return 0
    except Exception as e:
        print(f"\nError: {e}")
        return 1


if __name__ == '__main__':
    exit(main())
