"""Non-TUI logic: parsing, task operations, worktree commands."""

import re
import shutil
import subprocess
from pathlib import Path

from project_manager_tui.config import Config


def parse_section_projects(projects_file, section_name):
  """Parse Projects.md and return list of slugs for a given section."""
  if not projects_file.exists():
    return []

  content = projects_file.read_text()
  section_match = re.search(
      rf"## {section_name}\n(.*?)(?=\n## |\Z)", content, re.DOTALL
  )
  if not section_match:
    return []

  section_content = section_match.group(1)
  projects = []
  for line in section_content.strip().split("\n"):
    slug = line.strip()
    if slug:
      projects.append(slug)
  return projects


def get_all_projects_by_section(projects_file):
  """Get all projects grouped by section."""
  sections = ["Active", "Future", "Archived"]
  result = {}
  for section in sections:
    result[section] = parse_section_projects(projects_file, section)
  return result


def get_worktree_list_output(config):
  """Run agent-worktree.sh list once and return the output."""
  agent_worktree = config.agent_worktree_script
  if not agent_worktree.exists():
    return ""

  try:
    result = subprocess.run(
        [str(agent_worktree), "list"],
        capture_output=True,
        text=True,
    )
    if result.returncode != 0:
      return ""
  except Exception:
    return ""

  return result.stdout


def parse_worktree_statuses(worktree_output):
  """Parse worktree list output to get status per worktree."""
  statuses = {}
  for line in worktree_output.strip().split("\n"):
    if not line:
      continue
    if line.startswith("   "):
      rest = line.lstrip()
      name_part = rest.split(":")[0].split()[0] if rest else ""
      if name_part:
        statuses[name_part] = ""
    else:
      parts = line.split()
      if len(parts) >= 2:
        status = parts[0]
        name_part = parts[1].rstrip(":")
        statuses[name_part] = status
  return statuses


def get_active_task_nums(config, project_slug, worktree_output=None):
  """Get set of active task numbers for a project from agent-worktree.sh list."""
  if worktree_output is None:
    worktree_output = get_worktree_list_output(config)

  active_tasks = set()
  for match in re.finditer(rf"\b{re.escape(project_slug)}-(\d+)\b", worktree_output):
    active_tasks.add(int(match.group(1)))

  return active_tasks


def parse_tasks(config, project_md_path, project_slug, worktree_output=None):
  """Parse Project.md and return dict of number -> task info."""
  if not project_md_path.exists():
    return {}

  content = project_md_path.read_text()
  active_tasks = get_active_task_nums(config, project_slug, worktree_output)
  worktree_statuses = parse_worktree_statuses(worktree_output or "")
  tasks = {}

  for match in re.finditer(r"^(\d+)\.\s+\[([^\]])\]\s*(.*)$", content, re.MULTILINE):
    num = int(match.group(1))
    state_char = match.group(2).lower()
    done = state_char == "x"
    snoozed = state_char in ("s", "z")
    verified = state_char == "v"
    desc = match.group(3).strip()
    in_worktree = num in active_tasks

    worktree_name = f"{project_slug}-{num}"
    worktree_status = worktree_statuses.get(worktree_name, "")

    depends_on = []
    dep_match = re.search(r"\s*>(\d+(?:,\d+)*)\s*$", desc)
    if dep_match:
      depends_on = [int(d) for d in dep_match.group(1).split(",")]
      desc = desc[:dep_match.start()].strip()

    line_num = content[:match.start()].count('\n') + 1
    tasks[num] = {"desc": desc, "done": done, "snoozed": snoozed, "verified": verified,
                  "in_worktree": in_worktree, "worktree_status": worktree_status,
                  "depends_on": depends_on, "line_num": line_num, "state_char": state_char}

  for num, task in tasks.items():
    deps_met = all(tasks.get(d, {}).get("state_char", "") in ("v")
                   for d in task["depends_on"])
    task["deps_met"] = deps_met
    task["available"] = not task["done"] and not task["in_worktree"] and deps_met

  return tasks


def add_task_to_project(project_md_path):
  """Add a new empty task to the end of the task list in Project.md."""
  if not project_md_path.exists():
    return None, None

  content = project_md_path.read_text()

  existing_nums = []
  for match in re.finditer(r"^(\d+)\.\s+\[", content, re.MULTILINE):
    existing_nums.append(int(match.group(1)))

  new_num = max(existing_nums) + 1 if existing_nums else 1

  last_task_end = 0
  for match in re.finditer(r"^(\d+)\.\s+\[[^\]]\].*$", content, re.MULTILINE):
    last_task_end = match.end()

  if last_task_end == 0:
    tasks_match = re.search(r"^## Tasks\s*$", content, re.MULTILINE)
    if tasks_match:
      last_task_end = tasks_match.end()
    else:
      content = content.rstrip() + "\n\n## Tasks\n"
      last_task_end = len(content)

  new_task_line = f"\n{new_num}. [ ] "
  new_content = content[:last_task_end] + new_task_line + content[last_task_end:]

  project_md_path.write_text(new_content)

  line_num = new_content[:last_task_end + 1].count('\n') + 1

  return new_num, line_num


def move_project_to_section(projects_file, project_slug, target_section):
  """Move a project from one section to another in Projects.md."""
  content = projects_file.read_text()

  content = re.sub(rf"^{re.escape(project_slug)}\s*\n?",
                   "", content, flags=re.MULTILINE)

  section_match = re.search(rf"(## {target_section})(\n|$)", content)
  if section_match:
    insert_pos = section_match.end()
    prefix = "" if section_match.group(2) == "\n" else "\n"
    content = content[:insert_pos] + prefix + f"{project_slug}\n" + content[insert_pos:]
  else:
    content = content.rstrip() + f"\n\n## {target_section}\n{project_slug}\n"

  projects_file.write_text(content)


def snooze_task(project_md_path, task_num):
  """Snooze a task by changing [ ] to [z] in Project.md."""
  if not project_md_path.exists():
    return False

  content = project_md_path.read_text()
  new_content = re.sub(
      rf"^({task_num}\.\s+)\[ \]",
      r"\1[z]",
      content,
      flags=re.MULTILINE
  )
  if new_content != content:
    project_md_path.write_text(new_content)
    return True
  return False


def unsnooze_task(project_md_path, task_num):
  """Unsnooze a task by changing [s] or [z] to [ ] in Project.md."""
  if not project_md_path.exists():
    return False

  content = project_md_path.read_text()
  new_content = re.sub(
      rf"^({task_num}\.\s+)\[[sSzZ]\]",
      r"\1[ ]",
      content,
      flags=re.MULTILINE
  )
  if new_content != content:
    project_md_path.write_text(new_content)
    return True
  return False


def mark_task_done(project_md_path, task_num):
  """Mark a task as done in Project.md by changing [ ] to [x]."""
  if not project_md_path.exists():
    return False

  content = project_md_path.read_text()
  pattern = rf"^({task_num}\.\s+)\[ \]"
  new_content, count = re.subn(pattern, r"\1[x]", content, flags=re.MULTILINE)

  if count > 0:
    project_md_path.write_text(new_content)
    return True
  return False


def mark_task_verified(project_md_path, task_num):
  """Mark a task as verified in Project.md by changing any state to [v]."""
  if not project_md_path.exists():
    return False

  content = project_md_path.read_text()
  pattern = rf"^({task_num}\.\s+)\[[^\]]\]"
  new_content, count = re.subn(pattern, r"\1[v]", content, flags=re.MULTILINE)

  if count > 0:
    project_md_path.write_text(new_content)
    return True
  return False


def mark_task_released(project_md_path, task_num):
  """Mark a task as released in Project.md by changing state to [r]."""
  if not project_md_path.exists():
    return False

  content = project_md_path.read_text()
  pattern = rf"^({task_num}\.\s+)\[[^\]]\]"
  new_content, count = re.subn(pattern, r"\1[r]", content, flags=re.MULTILINE)

  if count > 0:
    project_md_path.write_text(new_content)
    return True
  return False


def delete_task(config, project_slug, task_num):
  """Delete a task - remove worktree, Task.md directory, and task line from Project.md."""
  project_dir = config.projects_dir / project_slug
  project_md = project_dir / "Project.md"

  worktree_name = f"{project_slug}-{task_num}"
  agent_worktree = config.agent_worktree_script

  if agent_worktree.exists():
    worktree_output = get_worktree_list_output(config)
    if worktree_name in worktree_output:
      result = subprocess.run([str(agent_worktree), "rm", worktree_name])
      if result.returncode != 0:
        return False

  task_dir = project_dir / "tasks" / str(task_num)
  if task_dir.exists():
    shutil.rmtree(task_dir)

  if project_md.exists():
    content = project_md.read_text()
    new_content = re.sub(
        rf"^{task_num}\.\s+\[[^\]]\].*\n?",
        "",
        content,
        flags=re.MULTILINE
    )
    project_md.write_text(new_content)

  return True


def ensure_task_md(config, project_md_path, project_slug, task_num, task_desc):
  """Ensure Task.md exists for a task, creating it if needed. Returns the path."""
  task_md = config.projects_dir / project_slug / "tasks" / str(task_num) / "Task.md"
  if not task_md.exists():
    task_md.parent.mkdir(parents=True, exist_ok=True)
    task_md.write_text(
        f"# {task_desc}\n\n<!-- Read {project_md_path} for project context -->\n")
  return task_md


def activate_task(config, project_slug, task_num, task_desc):
  """Activate a task - create directory and worktree."""
  project_dir = config.projects_dir / project_slug
  project_md = project_dir / "Project.md"

  task_md = ensure_task_md(config, project_md, project_slug, task_num, task_desc)
  print(f"Created: {task_md.parent}")

  worktree_name = f"{project_slug}-{task_num}"
  agent_worktree = config.agent_worktree_script

  if not agent_worktree.exists():
    print(f"Warning: {agent_worktree} not found, skipping worktree creation")
    return

  print(f"Running: agent-worktree.sh add {worktree_name}")
  subprocess.run([str(agent_worktree), "add", worktree_name])


def get_current_branch(config):
  """Get the current git branch name."""
  try:
    result = subprocess.run(
        ["git", "branch", "--show-current"],
        capture_output=True,
        text=True,
        cwd=str(config.git_repo_dir)
    )
    if result.returncode == 0:
      return result.stdout.strip()
  except Exception:
    pass
  return None


def get_all_active_tasks(config):
  """Get all tasks from active projects.

  Returns list of dicts with: project_slug, task_num, desc, state, task_md_path
  """
  active_projects = parse_section_projects(config.projects_file, "Active")
  if not active_projects:
    return []

  worktree_output = get_worktree_list_output(config)

  all_tasks = []
  for project_slug in active_projects:
    project_dir = config.projects_dir / project_slug
    project_md = project_dir / "Project.md"
    if not project_md.exists():
      continue

    tasks = parse_tasks(config, project_md, project_slug, worktree_output)
    for task_num, task_info in sorted(tasks.items()):
      state = task_info.get("state_char", " ")
      worktree_status = task_info.get("worktree_status", "")
      task_md_path = project_dir / "tasks" / str(task_num) / "Task.md"

      all_tasks.append({
          "project_slug": project_slug,
          "task_num": task_num,
          "desc": task_info["desc"],
          "state": state,
          "worktree_status": worktree_status,
          "task_md_path": task_md_path,
          "project_md_path": project_md,
          "line_num": task_info.get("line_num", 1),
          "done": task_info["done"],
          "snoozed": task_info.get("snoozed", False),
          "verified": task_info.get("verified", False),
          "in_worktree": task_info["in_worktree"],
          "depends_on": task_info.get("depends_on", []),
          "deps_met": task_info.get("deps_met", True),
      })

  return all_tasks


def get_project_line_numbers(projects_file):
  """Get line numbers for each project in Projects.md."""
  if not projects_file.exists():
    return {}

  content = projects_file.read_text()
  line_nums = {}
  for i, line in enumerate(content.split("\n"), start=1):
    slug = line.strip()
    if slug and not slug.startswith("#"):
      line_nums[slug] = i
  return line_nums


def get_project_list(config, current_project=None):
  """Get flat list of projects with section info for display."""
  projects_by_section = get_all_projects_by_section(config.projects_file)
  line_nums = get_project_line_numbers(config.projects_file)

  result = []
  for section in ["Active", "Future", "Archived"]:
    for slug in projects_by_section.get(section, []):
      result.append({
          "slug": slug,
          "section": section,
          "is_current": slug == current_project,
          "line_num": line_nums.get(slug, 1),
      })
  return result
