"""
Base workflow functions and utilities.

This module provides common functionality for all workflow commands:
- State validation
- Workflow initiation pattern
- Variable collection from state
- State clearing for fresh workflow starts
"""

import json
import logging
import os
import subprocess
import sys
import uuid
from typing import Any

from ...prompts import TemplateValidationError, load_and_render_prompt
from ...state import (
    get_value,
    load_state,
    save_state,
    set_bootstrap_state,
    set_value,
    set_workflow_state,
)


def clear_state_for_workflow_initiation() -> None:
    """
    Reset workflow tracking state for a fresh workflow initiation.

    Deletes runtime workflow tracking keys (workflow, agdt_run_id) so a new
    workflow can start cleanly.  Context keys (pull_request_id, jira.issue_key,
    versionControl.currentBranch) are intentionally preserved — the worktree
    key change naturally points to a different directory under .agdt/workflows/.

    Both keys are removed in a single load/save cycle to avoid intermediate
    on-disk state and reduce I/O.

    Should be called at the very beginning of each workflow initiation command,
    BEFORE parsing CLI arguments or accessing state.
    """
    state = load_state()
    changed = False
    for key in ("workflow", "agdt_run_id"):
        if key in state:
            del state[key]
            changed = True
    if changed:
        save_state(state)
    print("✓ Reset workflow tracking state")


def validate_required_state(required_keys: list[str]) -> dict[str, Any]:
    """
    Validate that required state keys exist and return their values.

    Args:
        required_keys: List of state keys that must be present

    Returns:
        Dictionary mapping key names to their values

    Raises:
        SystemExit: If any required key is missing
    """
    values = {}
    missing = []

    for key in required_keys:
        value = get_value(key)
        if value is None:
            missing.append(key)
        else:
            values[key] = value

    if missing:
        print(f"ERROR: Missing required state keys: {', '.join(missing)}", file=sys.stderr)
        print("\nPlease set them using:", file=sys.stderr)
        for key in missing:
            print(f"  agdt-set {key} <value>", file=sys.stderr)
        sys.exit(1)

    return values


def collect_variables_from_state(variable_keys: list[str]) -> dict[str, Any]:
    """
    Collect variable values from state.

    This collects values for variables that exist in state, but doesn't
    require them to exist. Use validate_required_state for required keys.

    Args:
        variable_keys: List of state keys to collect

    Returns:
        Dictionary mapping variable names to their values (only includes keys that exist)
    """
    variables = {}
    for key in variable_keys:
        value = get_value(key)
        if value is not None:
            # Convert dotted keys to underscore format for template variables
            # e.g., "jira.issue_key" -> "jira_issue_key"
            var_name = _state_key_to_variable_name(key)
            variables[var_name] = value
    return variables


def _state_key_to_variable_name(state_key: str) -> str:
    """
    Convert a state key to a template variable name.

    State keys may use dot notation (e.g., "jira.issue_key") which is converted
    to underscore format for template variables.

    Examples:
        "pull_request_id" -> "pull_request_id"
        "jira.issue_key" -> "jira_issue_key"
        "jira.comment" -> "jira_comment"

    Args:
        state_key: State key with underscores and/or dots

    Returns:
        Variable name with dots replaced by underscores
    """
    # Simply replace dots with underscores - the templates use underscore format
    return state_key.replace(".", "_")


def initiate_workflow(
    workflow_name: str,
    required_state_keys: list[str] | None = None,
    optional_state_keys: list[str] | None = None,
    additional_variables: dict[str, Any] | None = None,
    step_name: str = "initiate",
    context: dict[str, Any] | None = None,
    skip_bootstrap_init: bool = False,
) -> str:
    """
    Common workflow initiation logic.

    This function:
    1. Validates required state keys exist
    2. Collects variables from state
    3. Updates workflow state
    4. Loads and renders the prompt template
    5. Saves to temp and logs to console

    Args:
        workflow_name: Name of the workflow (e.g., "pull-request-review")
        required_state_keys: State keys that must exist
        optional_state_keys: State keys to include if they exist
        additional_variables: Extra variables to include (not from state)
        step_name: Name of the step (default "initiate")
        context: Workflow context to store in state
        skip_bootstrap_init: Set to True when the caller has already called
            _ensure_bootstrap_identity() to avoid a redundant second call to
            set_bootstrap_state() (extra git subprocess + disk writes).

    Returns:
        The generated prompt content

    Raises:
        SystemExit: If required state keys are missing
        FileNotFoundError: If no template exists
        TemplateValidationError: If override template is invalid
    """
    # Ensure identity.json is populated before any set_value() calls so that
    # _update_bootstrap_worktree_key() (called by set_value()) finds a valid
    # identity and get_state_dir() resolves to the scoped path immediately.
    # When the state directory is overridden via environment variables, scoped
    # bootstrap is not used, so we can skip this initialization to avoid
    # redundant git subprocesses and .agdt/* side effects.
    # skip_bootstrap_init=True when _ensure_bootstrap_identity() was already
    # called before any state I/O in the same command invocation.
    env_state_override = os.getenv("AGENTIC_DEVTOOLS_STATE_DIR")
    if not env_state_override and not skip_bootstrap_init:
        try:
            set_bootstrap_state()
        except (OSError, json.JSONDecodeError, UnicodeError, subprocess.SubprocessError) as exc:
            logging.getLogger(__name__).warning(
                "Failed to initialize bootstrap state; proceeding with unscoped workflow state: %s",
                exc,
            )

    # Validate required state
    required_values = {}
    if required_state_keys:
        required_values = validate_required_state(required_state_keys)

    # Collect all variables
    all_state_keys = (required_state_keys or []) + (optional_state_keys or [])
    variables = collect_variables_from_state(all_state_keys)

    # Add any additional variables
    if additional_variables:
        variables.update(additional_variables)

    # Build context from required values if not provided
    if context is None:
        context = {}
        for key, value in required_values.items():
            context[_state_key_to_variable_name(key)] = value

    # Generate run_id for single-commit-per-run amend strategy
    run_id = uuid.uuid4().hex[:12]
    set_value("agdt_run_id", run_id)

    # Store current branch for persist_if_dirty() resolution
    try:
        from ..git.agdt_branch import _run_plumbing

        branch_result = _run_plumbing("rev-parse", "--abbrev-ref", "HEAD")
        branch_name = branch_result.stdout.strip()
        # When in a detached HEAD state, Git returns the literal string "HEAD".
        # Treat that as "unresolvable" and skip setting the currentBranch key so
        # persist_if_dirty() can fall back or no-op cleanly.
        if branch_result.returncode == 0 and branch_name and branch_name != "HEAD":
            set_value("versionControl.currentBranch", branch_name)
    except Exception:
        pass  # Best-effort; persist_if_dirty has its own fallback

    # Update workflow state
    set_workflow_state(
        name=workflow_name,
        status="initiated",
        step=step_name,
        context=context,
    )

    # Load and render prompt
    try:
        content = load_and_render_prompt(
            workflow_name=workflow_name,
            step_name=step_name,
            variables=variables,
            save_to_temp=True,
            log_output=True,
        )
        return content
    except TemplateValidationError as e:  # pragma: no cover
        print(str(e), file=sys.stderr)
        sys.exit(1)
    except FileNotFoundError as e:  # pragma: no cover
        print(f"ERROR: {e}", file=sys.stderr)
        sys.exit(1)


def advance_workflow_step(
    workflow_name: str,
    step_name: str,
    variables: dict[str, Any] | None = None,
    status: str = "in-progress",
) -> str:
    """
    Advance to a new step in an active workflow.

    Args:
        workflow_name: Name of the workflow
        step_name: Name of the new step
        variables: Variables for the step template
        status: New workflow status

    Returns:
        The generated prompt content
    """
    from ...state import get_workflow_state, is_workflow_active

    # Verify workflow is active
    if not is_workflow_active(workflow_name):
        print(f"ERROR: Workflow '{workflow_name}' is not active.", file=sys.stderr)
        print(f"Start it first with: agdt-initiate-{workflow_name}-workflow", file=sys.stderr)
        sys.exit(1)

    # Get existing context
    workflow = get_workflow_state()
    context = workflow.get("context", {}) if workflow else {}

    # Update workflow state
    set_workflow_state(
        name=workflow_name,
        status=status,
        step=step_name,
        context=context,
    )

    # Load and render prompt
    try:
        content = load_and_render_prompt(
            workflow_name=workflow_name,
            step_name=step_name,
            variables=variables or {},
            save_to_temp=True,
            log_output=True,
        )
        return content
    except (TemplateValidationError, FileNotFoundError) as e:  # pragma: no cover
        print(f"ERROR: {e}", file=sys.stderr)
        sys.exit(1)
