"""Workflow Migration System

Environment-aware workflow migration with config-driven preferences.
Helps users transition from deprecated workflows to consolidated versions.

Behavior:
- CI/Non-interactive: Silent aliasing with log message
- First interactive use: Show dialog with "remember" option
- Subsequent uses: Follow stored preference
- Config override: Users can set migration mode globally

Usage:
    from attune.workflows.migration import resolve_workflow_migration

    # Returns (canonical_name, kwargs, was_migrated)
    name, kwargs, migrated = resolve_workflow_migration("test-gen-behavioral")

Copyright 2026 Smart-AI-Memory
Licensed under Fair Source License 0.9
"""

import json
import logging
import os
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)

# Migration mode options
MIGRATION_MODE_PROMPT = "prompt"  # Ask user on first use
MIGRATION_MODE_AUTO = "auto"  # Silently use new syntax
MIGRATION_MODE_LEGACY = "legacy"  # Keep using old syntax (deprecated)

# Workflow aliases: old_name -> (new_name, default_kwargs)
WORKFLOW_ALIASES: dict[str, tuple[str, dict[str, Any]]] = {
    # Code Review consolidation
    "pro-review": ("code-review", {"mode": "premium"}),
    # Test Generation consolidation
    "test-gen-behavioral": ("test-gen", {"style": "behavioral"}),
    "test-coverage-boost": ("test-gen", {"target": "coverage"}),
    "autonomous-test-gen": ("test-gen-parallel", {"autonomous": True}),
    "progressive-test-gen": ("test-gen-parallel", {"progressive": True}),
    # Release consolidation
    "secure-release": ("release-prep", {"mode": "secure"}),
    "orchestrated-release-prep": ("release-prep", {"mode": "full"}),
    # Deprecated (show warning, map to replacement)
    "release-prep-legacy": ("release-prep", {"mode": "standard", "_deprecated": True}),
    "document-manager": ("doc-gen", {"_deprecated": True}),
    # Removed (show error with migration path)
    "test5": (
        None,
        {"_removed": True, "_message": "test5 was a test artifact and has been removed"},
    ),
    "manage-docs": (
        None,
        {
            "_removed": True,
            "_message": "manage-docs was incomplete. Use doc-gen or doc-orchestrator",
        },
    ),
}

# Human-readable descriptions for the dialog
WORKFLOW_DESCRIPTIONS: dict[str, str] = {
    "code-review": "Comprehensive code analysis with optional premium mode",
    "test-gen": "Generate tests with style (standard/behavioral) and target (gaps/coverage) options",
    "test-gen-parallel": "Batch test generation with autonomous and progressive modes",
    "release-prep": "Release readiness with modes: standard, secure, or full",
    "doc-gen": "Generate documentation for code",
}


@dataclass
class MigrationConfig:
    """Configuration for workflow migration behavior.

    Attributes:
        mode: How to handle migrations (prompt, auto, legacy)
        remembered_choices: User's choices for specific workflows
        show_tips: Whether to show migration tips after workflow runs
        last_prompted: Track when workflows were last prompted
    """

    mode: str = MIGRATION_MODE_PROMPT
    remembered_choices: dict[str, str] = field(default_factory=dict)
    show_tips: bool = True
    last_prompted: dict[str, str] = field(default_factory=dict)

    @classmethod
    def load(cls) -> "MigrationConfig":
        """Load migration config from .attune/migration.json"""
        config_path = Path.cwd() / ".attune" / "migration.json"

        if config_path.exists():
            try:
                with config_path.open() as f:
                    data = json.load(f)
                return cls(
                    mode=data.get("mode", MIGRATION_MODE_PROMPT),
                    remembered_choices=data.get("remembered_choices", {}),
                    show_tips=data.get("show_tips", True),
                    last_prompted=data.get("last_prompted", {}),
                )
            except (json.JSONDecodeError, OSError) as e:
                logger.warning(f"Failed to load migration config: {e}")

        return cls()

    def save(self) -> None:
        """Save migration config to .attune/migration.json"""
        config_dir = Path.cwd() / ".attune"
        config_dir.mkdir(exist_ok=True)
        config_path = config_dir / "migration.json"

        try:
            with config_path.open("w") as f:
                json.dump(
                    {
                        "mode": self.mode,
                        "remembered_choices": self.remembered_choices,
                        "show_tips": self.show_tips,
                        "last_prompted": self.last_prompted,
                    },
                    f,
                    indent=2,
                )
        except OSError as e:
            logger.warning(f"Failed to save migration config: {e}")

    def remember_choice(self, old_name: str, choice: str) -> None:
        """Remember user's migration choice for a workflow."""
        self.remembered_choices[old_name] = choice
        self.save()


def is_interactive() -> bool:
    """Check if we're running in an interactive terminal.

    Returns False in CI environments or when stdin is not a TTY.
    """
    # Check common CI environment variables
    ci_vars = ["CI", "GITHUB_ACTIONS", "GITLAB_CI", "JENKINS_URL", "CIRCLECI", "TRAVIS"]
    if any(os.getenv(var) for var in ci_vars):
        return False

    # Check for explicit non-interactive flag
    if os.getenv("ATTUNE_NO_INTERACTIVE"):
        return False

    # Check if stdin is a TTY
    return sys.stdin.isatty()


def show_migration_dialog(
    old_name: str, new_name: str, kwargs: dict[str, Any]
) -> tuple[str, dict[str, Any], bool]:
    """Show interactive migration dialog to user.

    Args:
        old_name: The deprecated workflow name
        new_name: The new canonical workflow name
        kwargs: Default kwargs for the new workflow

    Returns:
        Tuple of (chosen_name, chosen_kwargs, should_remember)
    """
    # Build the new command string
    flag_parts = []
    for key, value in kwargs.items():
        if key.startswith("_"):
            continue
        if isinstance(value, bool) and value:
            flag_parts.append(f"--{key.replace('_', '-')}")
        else:
            flag_parts.append(f"--{key.replace('_', '-')} {value}")

    new_command = f"attune workflow run {new_name}"
    if flag_parts:
        new_command += " " + " ".join(flag_parts)

    description = WORKFLOW_DESCRIPTIONS.get(new_name, "")

    # Print dialog
    print()
    print("┌" + "─" * 68 + "┐")
    print("│" + " Workflow Migration ".center(68) + "│")
    print("├" + "─" * 68 + "┤")
    print(f"│  '{old_name}' has been consolidated into '{new_name}'".ljust(69) + "│")
    if description:
        print(f"│  {description[:64]}".ljust(69) + "│")
    print("│" + " " * 68 + "│")
    print("│  New syntax:".ljust(69) + "│")
    print(f"│    {new_command[:62]}".ljust(69) + "│")
    print("│" + " " * 68 + "│")
    print("│  How would you like to proceed?".ljust(69) + "│")
    print("│" + " " * 68 + "│")
    print("│  1. Use new syntax (recommended)".ljust(69) + "│")
    print("│  2. Continue with legacy behavior (deprecated)".ljust(69) + "│")
    print("│  3. Always use new syntax (don't ask again)".ljust(69) + "│")
    print("│  4. Always use legacy (don't ask again)".ljust(69) + "│")
    print("└" + "─" * 68 + "┘")
    print()

    while True:
        try:
            choice = input("Enter choice [1-4]: ").strip()

            if choice == "1":
                return new_name, kwargs, False
            elif choice == "2":
                return old_name, {}, False
            elif choice == "3":
                return new_name, kwargs, True  # Remember: use new
            elif choice == "4":
                return old_name, {}, True  # Remember: use legacy
            else:
                print("Please enter 1, 2, 3, or 4")
        except (EOFError, KeyboardInterrupt):
            # User cancelled - default to new syntax
            print("\nDefaulting to new syntax...")
            return new_name, kwargs, False


def show_removed_workflow_error(old_name: str, message: str) -> None:
    """Show error for removed workflows with migration guidance."""
    print()
    print("┌" + "─" * 68 + "┐")
    print("│" + " Workflow Removed ".center(68) + "│")
    print("├" + "─" * 68 + "┤")
    print(f"│  '{old_name}' is no longer available.".ljust(69) + "│")
    print("│" + " " * 68 + "│")
    # Word wrap the message
    words = message.split()
    line = "│  "
    for word in words:
        if len(line) + len(word) + 1 > 67:
            print(line.ljust(69) + "│")
            line = "│  " + word + " "
        else:
            line += word + " "
    if line.strip() != "│":
        print(line.ljust(69) + "│")
    print("│" + " " * 68 + "│")
    print("│  Run 'attune workflow list' to see available workflows.".ljust(69) + "│")
    print("└" + "─" * 68 + "┘")
    print()


def show_deprecation_warning(old_name: str, new_name: str, kwargs: dict[str, Any]) -> None:
    """Show deprecation warning (non-blocking)."""
    flag_parts = []
    for key, value in kwargs.items():
        if key.startswith("_"):
            continue
        if isinstance(value, bool) and value:
            flag_parts.append(f"--{key.replace('_', '-')}")
        else:
            flag_parts.append(f"--{key.replace('_', '-')} {value}")

    new_command = f"attune workflow run {new_name}"
    if flag_parts:
        new_command += " " + " ".join(flag_parts)

    logger.warning(
        f"'{old_name}' is deprecated. Use '{new_command}' instead. "
        f"Set ATTUNE_NO_INTERACTIVE=1 to suppress this warning in CI."
    )


def resolve_workflow_migration(
    workflow_name: str,
    config: MigrationConfig | None = None,
) -> tuple[str, dict[str, Any], bool]:
    """Resolve a workflow name, handling migrations as needed.

    This is the main entry point for the migration system.

    Args:
        workflow_name: The workflow name from user input
        config: Optional migration config (loads from file if not provided)

    Returns:
        Tuple of:
            - resolved_name: The canonical workflow name to use
            - kwargs: Additional kwargs to pass to the workflow
            - was_migrated: True if the workflow was migrated

    Raises:
        SystemExit: If workflow has been removed
    """
    # Not an aliased workflow - return as-is
    if workflow_name not in WORKFLOW_ALIASES:
        return workflow_name, {}, False

    new_name, kwargs = WORKFLOW_ALIASES[workflow_name]

    # Handle removed workflows
    if kwargs.get("_removed"):
        message = kwargs.get("_message", "This workflow has been removed.")
        if is_interactive():
            show_removed_workflow_error(workflow_name, message)
        else:
            logger.error(f"Workflow '{workflow_name}' has been removed: {message}")
        raise SystemExit(1)

    # Load config if not provided
    if config is None:
        config = MigrationConfig.load()

    # Check if user has a remembered choice
    if workflow_name in config.remembered_choices:
        remembered = config.remembered_choices[workflow_name]
        if remembered == "new":
            logger.debug(f"Using remembered choice: {new_name}")
            return new_name, kwargs, True
        elif remembered == "legacy":
            logger.debug(f"Using remembered choice: {workflow_name} (legacy)")
            return workflow_name, {}, True

    # Handle based on mode and environment
    is_deprecated = kwargs.get("_deprecated", False)

    if not is_interactive():
        # CI/non-interactive: silent aliasing with log
        if config.mode == MIGRATION_MODE_LEGACY:
            logger.info(f"Using legacy workflow '{workflow_name}' (migration mode: legacy)")
            return workflow_name, {}, False
        else:
            show_deprecation_warning(workflow_name, new_name, kwargs)
            return new_name, kwargs, True

    # Interactive mode
    if config.mode == MIGRATION_MODE_AUTO:
        # Auto-migrate without prompting
        show_deprecation_warning(workflow_name, new_name, kwargs)
        return new_name, kwargs, True

    elif config.mode == MIGRATION_MODE_LEGACY:
        # Use legacy behavior
        if is_deprecated:
            show_deprecation_warning(workflow_name, new_name, kwargs)
        return workflow_name, {}, False

    else:  # MIGRATION_MODE_PROMPT
        # Show interactive dialog
        chosen_name, chosen_kwargs, remember = show_migration_dialog(
            workflow_name, new_name, kwargs
        )

        if remember:
            if chosen_name == new_name:
                config.remember_choice(workflow_name, "new")
            else:
                config.remember_choice(workflow_name, "legacy")

        was_migrated = chosen_name == new_name
        return chosen_name, chosen_kwargs, was_migrated


def show_migration_tip(old_name: str, new_name: str, kwargs: dict[str, Any]) -> None:
    """Show a migration tip after workflow completion.

    Called at the end of a workflow run to educate users.
    """
    config = MigrationConfig.load()
    if not config.show_tips:
        return

    if not is_interactive():
        return

    # Build command
    flag_parts = []
    for key, value in kwargs.items():
        if key.startswith("_"):
            continue
        if isinstance(value, bool) and value:
            flag_parts.append(f"--{key.replace('_', '-')}")
        else:
            flag_parts.append(f"--{key.replace('_', '-')} {value}")

    new_command = f"attune workflow run {new_name}"
    if flag_parts:
        new_command += " " + " ".join(flag_parts)

    print()
    print(f"💡 Tip: '{old_name}' is now '{new_command}'")
    print("   Run 'attune config set workflow.migration.mode auto' to auto-migrate")
    print("   Run 'attune config set workflow.migration.show_tips false' to hide tips")
    print()


def get_canonical_workflow_name(workflow_name: str) -> str:
    """Get the canonical (new) name for a workflow.

    Useful for documentation and help text.
    """
    if workflow_name in WORKFLOW_ALIASES:
        new_name, _ = WORKFLOW_ALIASES[workflow_name]
        return new_name if new_name else workflow_name
    return workflow_name


def list_migrations() -> list[dict[str, Any]]:
    """List all workflow migrations for documentation.

    Returns:
        List of migration info dicts
    """
    migrations = []
    for old_name, (new_name, kwargs) in WORKFLOW_ALIASES.items():
        if new_name is None:
            status = "removed"
        elif kwargs.get("_deprecated"):
            status = "deprecated"
        else:
            status = "consolidated"

        migrations.append(
            {
                "old_name": old_name,
                "new_name": new_name,
                "status": status,
                "kwargs": {k: v for k, v in kwargs.items() if not k.startswith("_")},
                "message": kwargs.get("_message", ""),
            }
        )

    return migrations
