#!/usr/bin/env python3
"""
add-task

Appends a new task to docs/tasks.md and re-synchronizes the task list.
Usage:
    add-task "My Title Goes Here" --project=shared --type=chore
    add-task "My Title Goes Here" -p example-project -t feature -s triage -d "Description text"
"""

import argparse
import importlib.machinery
import importlib.util
import os
import re
import sys
from pathlib import Path


def load_update_tasks_module():
    script_dir = Path(__file__).resolve().parent
    update_script = script_dir / "update-tasks"
    if not update_script.exists():
        raise FileNotFoundError(f"Could not find update-tasks at {update_script}")
    loader = importlib.machinery.SourceFileLoader("update_tasks", str(update_script))
    spec = importlib.util.spec_from_loader("update_tasks", loader)
    if spec is None:
        raise ImportError(f"Could not load spec for {update_script}")
    mod = importlib.util.module_from_spec(spec)
    loader.exec_module(mod)
    return mod


def add_task(title, project="shared", subproject=None, task_type="feature", status="triage", description=None, tasks_file=None, epic=None):
    update_mod = load_update_tasks_module()
    tasks_path = update_mod.find_tasks_file(tasks_file)

    with open(tasks_path, "r", encoding="utf-8") as f:
        text = f.read()

    # 1. Parse frontmatter for valid labels and values
    fm_match = re.match(r"^---\n(.*?)\n---", text, re.DOTALL)
    if not fm_match:
        raise ValueError("YAML frontmatter not found in tasks.md")

    fm_data = update_mod.parse_yaml_frontmatter(fm_match.group(1))
    prefix = update_mod.get_task_id_prefix(fm_data)
    epic_key, epic_label = update_mod.resolve_epic(epic, fm_data)

    project_list = fm_data.get("projects", [])
    subproject_list = fm_data.get("subprojects", [])
    status_list = fm_data.get("task-statuses", [])
    type_list = fm_data.get("task-types", [])

    project_map = {}
    project_value_map = {}
    for p in project_list:
        val = p.get("value", "").strip()
        lbl = p.get("label", "").strip()
        if val:
            project_map[val.lower()] = lbl or val.capitalize()
            project_value_map[val.lower()] = val
        if lbl:
            project_map[lbl.lower()] = lbl
            if val:
                project_value_map[lbl.lower()] = val

    subproject_map = {}
    subproject_value_map = {}
    for sp in subproject_list:
        val = sp.get("value", "").strip()
        lbl = sp.get("label", "").strip()
        if val:
            subproject_map[val.lower()] = lbl or val.replace("-", " ").title()
            subproject_value_map[val.lower()] = val
        if lbl:
            subproject_map[lbl.lower()] = lbl
            if val:
                subproject_value_map[lbl.lower()] = val

    status_map = {s.get("value", "").lower(): s.get("label", "") for s in status_list}
    status_value_map = {s.get("value", "").lower(): s.get("value", "") for s in status_list}
    for s in status_list:
        lbl = s.get("label", "").strip()
        val = s.get("value", "").strip()
        if lbl:
            status_map[lbl.lower()] = lbl
            if val:
                status_value_map[lbl.lower()] = val
    status_map["fixed"] = "Fixed"
    status_map["done"] = "Fixed"
    status_value_map["fixed"] = "done"
    status_value_map["done"] = "done"

    type_map = {t.get("value", "").lower(): t.get("label", "") for t in type_list}
    type_value_map = {t.get("value", "").lower(): t.get("value", "") for t in type_list}
    for t in type_list:
        lbl = t.get("label", "").strip()
        val = t.get("value", "").strip()
        if lbl:
            type_map[lbl.lower()] = lbl
            if val:
                type_value_map[lbl.lower()] = val

    # Normalize values
    proj_input = project.strip().lower()
    type_input = task_type.strip().lower()
    status_input = status.strip().lower()

    proj_key = project_value_map.get(proj_input, proj_input)
    type_key = type_value_map.get(type_input, type_input)
    status_key = status_value_map.get(status_input, status_input)

    proj_label = project_map.get(proj_key, project_map.get(proj_input, project.strip().capitalize()))
    type_label = type_map.get(type_key, type_map.get(type_input, task_type.strip().capitalize()))
    status_label = status_map.get(status_key, status_map.get(status_input, status.strip().capitalize()))

    subproj_key = None
    subproj_label = None
    if subproject and subproject.strip() and subproject.strip().lower() not in ("", "none", "null"):
        sp_in = subproject.strip().lower()
        subproj_key = subproject_value_map.get(sp_in, sp_in)
        subproj_label = subproject_map.get(subproj_key, subproject_map.get(sp_in, subproject.strip().replace("-", " ").replace("_", " ").title()))

    # 2. Find max task ID
    detail_start = text.find("## Detailed Tasks\n")
    if detail_start == -1:
        raise ValueError("## Detailed Tasks heading not found")
    ids = [int(m.group(2)) for m in re.finditer(update_mod.TASK_ID_PATTERN, text[detail_start:], re.IGNORECASE)]
    next_num = (max(ids) + 1) if ids else 1

    # Digit width calculation (minimum 3 digits)
    digit_width = max(3, len(str(next_num)))
    formatted_id = update_mod.format_task_id(next_num, prefix, digit_width)
    formatted_aid = formatted_id.lower()

    # 3. Format description
    if description and description.strip():
        desc_lines = description.strip().splitlines()
        desc_body = "\n".join(desc_lines)
        if not any(line.strip().startswith("- [") for line in desc_lines):
            desc_body += "\n- [ ] "
    else:
        desc_body = "Describe task objectives and implementation requirements here.\n- [ ] "

    if subproj_key:
        anchor_attrs = f'id="{formatted_aid}" class="task" data-project="{proj_key}" data-subproject="{subproj_key}" data-status="{status_key}" data-task-type="{type_key}"'
        subproj_line = f"**Subproject:** {subproj_label}\n"
    else:
        anchor_attrs = f'id="{formatted_aid}" class="task" data-project="{proj_key}" data-status="{status_key}" data-task-type="{type_key}"'
        subproj_line = ""

    if epic_key:
        anchor_attrs += f' data-epic="{epic_key}"'
    epic_line = f"**Epic:** {epic_label}\n" if epic_key else ""

    new_task_chunk = (
        f'<a {anchor_attrs}></a>\n'
        f"### {title.strip()}\n"
        f"**ID:** {formatted_id}\n"
        f"**Project:** {proj_label}\n"
        f"{subproj_line}"
        f"{epic_line}"
        f"**Status:** {status_label}\n"
        f"**Type:** {type_label}\n\n"
        f"**Description:**\n"
        f"{desc_body}\n"
    )

    # 4. Insert before Notes marker
    notes_marker = "\n---\n\n## Notes\n"
    notes_pos = text.find(notes_marker)
    if notes_pos == -1:
        raise ValueError("Could not find ## Notes marker to insert task before")

    updated_text = text[:notes_pos].rstrip() + "\n\n" + new_task_chunk + "\n---\n\n## Notes\n" + text[notes_pos + len(notes_marker):]

    with open(tasks_path, "w", encoding="utf-8") as f:
        f.write(updated_text)

    # 5. Re-run update_tasks_document to sort, standardize, and refresh tables
    update_mod.update_tasks_document(tasks_path)

    return formatted_id, title.strip(), proj_label, subproj_label, status_label, type_label, tasks_path


def main():
    parser = argparse.ArgumentParser(description="Add a new task to tasks.md and update tables.")
    parser.add_argument("title", help="Title of the new task")
    parser.add_argument("--epic", default=None, help="Epic value or label from frontmatter (optional)")
    parser.add_argument(
        "-p", "--project",
        default="shared",
        help="Project name or value (default: shared)"
    )
    parser.add_argument(
        "-S", "--subproject",
        default=None,
        help="Subproject name or category (optional, e.g. actions, expressions, schema)"
    )
    parser.add_argument(
        "-t", "--type",
        default="feature",
        help="Task type (default: feature, e.g. chore, bug, feature, lint, docs)"
    )
    parser.add_argument(
        "-s", "--status",
        default="triage",
        help="Initial task status (default: triage, e.g. planning, triage, pending, fixed)"
    )
    parser.add_argument(
        "-d", "--description",
        default=None,
        help="Task description and requirements"
    )
    parser.add_argument(
        "-f", "--file",
        default=None,
        help="Path to tasks.md (optional)"
    )

    args = parser.parse_args()

    try:
        new_id, title, proj, subproj, status, ttype, tasks_path = add_task(
            title=args.title,
            project=args.project,
            subproject=args.subproject,
            task_type=args.type,
            status=args.status,
            description=args.description,
            tasks_file=args.file,
            epic=args.epic,
        )
        tag_info = f"{proj} | {subproj} | {status} | {ttype}" if subproj else f"{proj} | {status} | {ttype}"
        print(f"Successfully added {new_id}: \"{title}\" [{tag_info}] to {tasks_path}")
    except Exception as e:
        print(f"Error adding task: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()
