"""Task-level scenario cloning (Scenario Builder, plan S2).

Pure / QGIS-free model operations so they unit-test cleanly. The dock widget
drives persistence, execution, and the UI; this module only manipulates the
in-memory Workspace→Study→Analysis→Task model objects.

A "fork" clones the right-clicked task as a **sibling under its parent** (the
analysis or a parent task), optionally cloning its whole child subtree beneath
the clone, with fresh ids and scenario metadata. The baseline is never mutated.
"""

from __future__ import annotations

import copy
import uuid
from typing import Any, List, Optional, Tuple


def find_task_and_container(analysis, task_id: str) -> Tuple[Optional[list], Any]:
    """Return (sibling_list, parent) for task_id within an analysis.

    ``sibling_list`` is the list the task lives in (``analysis.tasks`` or a parent
    task's ``children``); ``parent`` is the analysis or the parent task. Returns
    (None, None) if not found.
    """
    tasks = getattr(analysis, "tasks", None)
    if tasks is None:
        return None, None
    for task in tasks:
        if task.id == task_id:
            return tasks, analysis
        found = _find_in_children(task, task_id)
        if found != (None, None):
            return found
    return None, None


def _find_in_children(parent_task, task_id: str) -> Tuple[Optional[list], Any]:
    children = getattr(parent_task, "children", None) or []
    for child in children:
        if child.id == task_id:
            return parent_task.children, parent_task
        found = _find_in_children(child, task_id)
        if found != (None, None):
            return found
    return None, None


def _reid_subtree(task, *, scenario_id: str, clear_results: bool, include_children: bool) -> None:
    """Give a task (and optionally its subtree) fresh ids + scenario metadata, in place."""
    old_id = task.id
    task.id = str(uuid.uuid4())
    if clear_results:
        task.result = None
    task.metadata = dict(getattr(task, "metadata", {}) or {})
    task.metadata["scenario_baseline_task_id"] = old_id
    task.metadata["scenario_id"] = scenario_id
    task.metadata.pop("model_snapshot_id", None)
    if include_children:
        for child in getattr(task, "children", []) or []:
            _reid_subtree(child, scenario_id=scenario_id, clear_results=clear_results, include_children=True)
    else:
        # Fork-this-node-only: drop the cloned children.
        if hasattr(task, "children"):
            task.children = []


def build_scenario_fork(
    source_task,
    *,
    scenario_name: str,
    include_children: bool,
    scenario_id: Optional[str] = None,
    scenario_of_analysis_id: str = "",
    child_suffix: str = "",
) -> Tuple[Any, str]:
    """Deep-clone ``source_task`` as a scenario sibling. Returns (clone, scenario_id).

    The clone gets fresh ids throughout, cleared results, a user-facing name, and
    scenario metadata (``is_scenario``, ``scenario_id``, ``scenario_of``, and per
    node ``scenario_baseline_task_id``). The original is untouched.

    ``child_suffix`` (e.g. ``"_scenario1"``) is appended to every cloned child
    task's name so the scenario's subtree is visibly independent from the
    baseline (the root clone uses ``scenario_name`` directly).
    """
    scenario_id = scenario_id or str(uuid.uuid4())
    clone = copy.deepcopy(source_task)
    _reid_subtree(clone, scenario_id=scenario_id, clear_results=True, include_children=include_children)
    clone.name = scenario_name or f"{source_task.name} (scenario)"
    if include_children and child_suffix:
        for descendant in iter_subtree(clone)[1:]:
            descendant.name = f"{descendant.name}{child_suffix}"
    clone.metadata = dict(getattr(clone, "metadata", {}) or {})
    clone.metadata.update({
        "is_scenario": True,
        "scenario_id": scenario_id,
        "scenario_of": scenario_of_analysis_id,
        "scenario_baseline_task_id": source_task.id,
    })
    return clone, scenario_id


def iter_subtree(task) -> List[Any]:
    """Depth-first list of a task and all descendants (root first)."""
    out = [task]
    for child in getattr(task, "children", []) or []:
        out.extend(iter_subtree(child))
    return out
