import sys
import time
from importlib.metadata import version

from .config import add_agent, get_agent_config
from .notify import send_notification
from .state import delete_state, get_state_file, read_state, update_state, write_state

try:
    VERSION = version("notilens")
except Exception:
    VERSION = "0.0.0"


# ── Arg helpers ──────────────────────────────────────────────────────────────

def positional_args(args: list[str]) -> list[str]:
    result = []
    for a in args:
        if a.startswith("--"):
            break
        result.append(a)
    return result


def parse_flags(args: list[str]) -> dict:
    flags = {
        "agent": "",
        "task_id": "",
        "type": "",
        "meta": {},
        "image_url": "",
        "open_url": "",
        "download_url": "",
        "tags": "",
        "is_actionable": "",
        "confidence_score": 0.0,
    }

    i = 0
    while i < len(args):
        a = args[i]
        if a == "--agent":
            flags["agent"] = args[i + 1] if i + 1 < len(args) else ""
            i += 2
        elif a == "--task":
            flags["task_id"] = args[i + 1] if i + 1 < len(args) else ""
            i += 2
        elif a == "--type":
            flags["type"] = args[i + 1] if i + 1 < len(args) else ""
            i += 2
        elif a == "--image_url":
            flags["image_url"] = args[i + 1] if i + 1 < len(args) else ""
            i += 2
        elif a == "--open_url":
            flags["open_url"] = args[i + 1] if i + 1 < len(args) else ""
            i += 2
        elif a == "--download_url":
            flags["download_url"] = args[i + 1] if i + 1 < len(args) else ""
            i += 2
        elif a == "--tags":
            flags["tags"] = args[i + 1] if i + 1 < len(args) else ""
            i += 2
        elif a == "--is_actionable":
            flags["is_actionable"] = args[i + 1] if i + 1 < len(args) else ""
            i += 2
        elif a == "--confidence":
            try:
                flags["confidence_score"] = float(args[i + 1]) if i + 1 < len(args) else 0.0
            except ValueError:
                flags["confidence_score"] = 0.0
            i += 2
        elif a == "--meta":
            kv = args[i + 1] if i + 1 < len(args) else ""
            eq = kv.find("=")
            if eq != -1:
                flags["meta"][kv[:eq]] = kv[eq + 1:]
            i += 2
        else:
            i += 1

    if not flags["agent"]:
        print("❌ --agent is required", file=sys.stderr)
        sys.exit(1)

    if not flags["task_id"]:
        flags["task_id"] = f"task_{int(time.time() * 1000)}"

    return flags


# ── Event type / actionable mapping ──────────────────────────────────────────

def get_event_type(event: str) -> str:
    mapping = {
        "task.completed": "success",
        "response.generated": "success",
        "input.approved": "success",
        "task.failed": "urgent",
        "task.timeout": "urgent",
        "response.failed": "urgent",
        "validation.failed": "urgent",
        "task.error": "important",
        "task.terminated": "important",
        "guardrail.triggered": "important",
        "task.retrying": "warning",
        "task.cancelled": "warning",
        "input.required": "warning",
        "input.rejected": "warning",
    }
    return mapping.get(event, "info")


def get_actionable_default(event: str) -> bool:
    return event in {
        "task.error", "task.failed", "task.timeout", "task.retrying",
        "ai.response.failed", "ai.validation.failed", "ai.guardrail.triggered",
        "input.required", "input.rejected",
    }


def validate_type(t: str) -> str:
    return t if t in {"info", "success", "warning", "urgent", "important"} else ""


# ── Core send ─────────────────────────────────────────────────────────────────

def send_notify(event: str, title: str, message: str, flags: dict) -> None:
    conf = get_agent_config(flags["agent"])
    if not conf or not conf.get("endpoint") or not conf.get("secret"):
        print("❌ Agent not configured", file=sys.stderr)
        return

    state_file = get_state_file(flags["agent"], flags["task_id"])
    state = read_state(state_file)

    prompt_tokens = state.get("prompt_tokens", 0)
    completion_tokens = state.get("completion_tokens", 0)

    meta = {
        "duration_ms": state.get("duration_ms", 0),
        "prompt_tokens": prompt_tokens,
        "completion_tokens": completion_tokens,
        "total_tokens": prompt_tokens + completion_tokens,
        "retry_count": state.get("retry_count", 0),
        "confidence_score": flags["confidence_score"],
        "loop_count": state.get("loop_count", 0),
        **flags["meta"],
    }

    final_type = validate_type(flags["type"]) or get_event_type(event)
    if flags["is_actionable"] != "":
        final_actionable = flags["is_actionable"].lower() == "true"
    else:
        final_actionable = get_actionable_default(event)

    payload = {
        "event": event,
        "title": title,
        "message": message,
        "type": final_type,
        "agent": flags["agent"],
        "task_id": flags["task_id"],
        "meta": meta,
        "image_url": flags["image_url"],
        "open_url": flags["open_url"],
        "download_url": flags["download_url"],
        "tags": flags["tags"],
        "is_actionable": final_actionable,
    }

    try:
        send_notification(conf["endpoint"], conf["secret"], payload)
        time.sleep(0.3)
    except Exception:
        pass  # silent fail — mirrors JS behaviour


# ── Duration helper ───────────────────────────────────────────────────────────

def calc_duration(state_file) -> int:
    state = read_state(state_file)
    return int(time.time() * 1000) - state.get("start_time", 0)


# ── Commands ──────────────────────────────────────────────────────────────────

def main():
    args = sys.argv[1:]
    command = args[0] if args else ""
    rest = args[1:]

    if command == "add-agent":
        if len(rest) < 4:
            print("Usage: notilens add-agent <agent> <transport> <endpoint> <secret>", file=sys.stderr)
            sys.exit(1)
        agent, transport, endpoint, secret = rest[0], rest[1], rest[2], rest[3]
        add_agent(agent, transport, endpoint, secret)
        print(f"✔ Agent '{agent}' added")

    elif command == "task.start":
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        write_state(state_file, {
            "agent": flags["agent"],
            "task": flags["task_id"],
            "start_time": int(time.time() * 1000),
            "retry_count": 0,
        })
        send_notify("task.started", f"{flags['agent']} | {flags['task_id']} started", "Task started", flags)
        print(f"▶️  Started: {flags['agent']} | {flags['task_id']}")

    elif command == "task.in_progress":
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"duration_ms": calc_duration(state_file)})
        send_notify("task.in_progress", f"{flags['agent']} | {flags['task_id']} running", "Task in progress", flags)
        print(f"⏳ In Progress: {flags['agent']} | {flags['task_id']}")

    elif command == "task.stop":
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        dur = calc_duration(state_file)
        update_state(state_file, {"duration_ms": dur})
        send_notify("task.stopped", f"{flags['agent']} | {flags['task_id']} stopped", "Task stopped", flags)
        print(f"⏹  Stopped: {flags['agent']} | {flags['task_id']} ({dur} ms)")

    elif command == "task.retry":
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        state = read_state(state_file)
        update_state(state_file, {
            "duration_ms": calc_duration(state_file),
            "retry_count": state.get("retry_count", 0) + 1,
        })
        send_notify("task.retrying", f"{flags['agent']} | {flags['task_id']} retry", "Retrying task", flags)
        print(f"🔁 Retry: {flags['agent']} | {flags['task_id']}")

    elif command == "task.loop":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        state = read_state(state_file)
        loop_count = state.get("loop_count", 0) + 1
        update_state(state_file, {"duration_ms": calc_duration(state_file), "loop_count": loop_count})
        send_notify("task.loop", f"{flags['agent']} | {flags['task_id']} loop #{loop_count}", msg, flags)
        print(f"🔄 Loop ({loop_count}): {flags['agent']} | {flags['task_id']}")

    elif command == "task.error":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"duration_ms": calc_duration(state_file), "last_error": msg})
        send_notify("task.error", f"{flags['agent']} | {flags['task_id']} error", msg, flags)
        print(f"❌ Error: {msg}", file=sys.stderr)

    elif command == "task.fail":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"duration_ms": calc_duration(state_file), "failed": True})
        send_notify("task.failed", f"{flags['agent']} | {flags['task_id']} failed", msg, flags)
        delete_state(state_file)
        print(f"💥 Failed: {flags['agent']} | {flags['task_id']}")

    elif command == "task.timeout":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"duration_ms": calc_duration(state_file), "timeout": True})
        send_notify("task.timeout", f"{flags['agent']} | {flags['task_id']} timeout", msg, flags)
        delete_state(state_file)
        print(f"⏰ Timeout: {flags['agent']} | {flags['task_id']}")

    elif command == "task.cancel":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"duration_ms": calc_duration(state_file), "cancelled": True})
        send_notify("task.cancelled", f"{flags['agent']} | {flags['task_id']} cancelled", msg, flags)
        delete_state(state_file)
        print(f"🚫 Cancelled: {flags['agent']} | {flags['task_id']}")

    elif command == "task.terminate":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"duration_ms": calc_duration(state_file), "terminated": True})
        send_notify("task.terminated", f"{flags['agent']} | {flags['task_id']} terminated", msg, flags)
        delete_state(state_file)
        print(f"⚠️  Terminated: {flags['agent']} | {flags['task_id']}")

    elif command == "task.complete":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"duration_ms": calc_duration(state_file)})
        send_notify("task.completed", f"{flags['agent']} | {flags['task_id']} workflow", msg, flags)
        delete_state(state_file)
        print(f"✅ Completed: {flags['agent']} | {flags['task_id']}")

    elif command == "set.metrics":
        pos = positional_args(rest)
        flags = parse_flags(rest)
        prompt = int(pos[0]) if len(pos) > 0 and pos[0].isdigit() else 0
        completion = int(pos[1]) if len(pos) > 1 and pos[1].isdigit() else 0
        try:
            conf = float(pos[2]) if len(pos) > 2 else 0.0
        except ValueError:
            conf = 0.0
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"prompt_tokens": prompt, "completion_tokens": completion})
        if conf:
            flags["confidence_score"] = conf
        print(f"📊 Metrics set: tokens({prompt}/{completion}) confidence({conf})")

    elif command == "ai.response.generate":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_notify("ai.response.generated", f"{flags['agent']} | {flags['task_id']} response", msg, flags)

    elif command == "ai.response.fail":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_notify("ai.response.failed", f"{flags['agent']} | {flags['task_id']} response failed", msg, flags)

    elif command == "input.required":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_notify("input.required", f"{flags['agent']} | {flags['task_id']} input required", msg, flags)

    elif command == "input.approve":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_notify("input.approved", f"{flags['agent']} | {flags['task_id']} input approved", msg, flags)

    elif command == "input.reject":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_notify("input.rejected", f"{flags['agent']} | {flags['task_id']} input rejected", msg, flags)

    elif command == "emit":
        pos = positional_args(rest)
        event = pos[0] if len(pos) > 0 else ""
        msg = pos[1] if len(pos) > 1 else ""
        flags = parse_flags(rest[2:])
        state_file = get_state_file(flags["agent"], flags["task_id"])
        update_state(state_file, {"duration_ms": calc_duration(state_file)})
        send_notify(event, f"{flags['agent']} | {flags['task_id']} {event}", msg, flags)
        print(f"📡 Event emitted: {event}")

    elif command == "version":
        print(f"NotiLens v{VERSION}")

    else:
        print_usage()


def print_usage():
    print("""Usage:
  notilens add-agent <agent> <transport> <endpoint> <secret>

Core Commands:
  notilens task.start --agent <agent> [--task <id>]
  notilens task.in_progress --agent <agent> [--task <id>]
  notilens task.stop --agent <agent> [--task <id>]
  notilens task.retry --agent <agent> [--task <id>]
  notilens task.loop "msg" --agent <agent>
  notilens task.error "msg" --agent <agent> [--task <id>]
  notilens task.fail "msg" --agent <agent> [--task <id>]
  notilens task.timeout "msg" --agent <agent> [--task <id>]
  notilens task.cancel "msg" --agent <agent> [--task <id>]
  notilens task.terminate "msg" --agent <agent> [--task <id>]
  notilens task.complete "msg" --agent <agent> [--task <id>]
  notilens ai.response.generate "msg" --agent <agent>
  notilens ai.response.fail "msg" --agent <agent>
  notilens input.required "msg" --agent <agent>
  notilens input.approve "msg" --agent <agent>
  notilens input.reject "msg" --agent <agent>

Generic Event:
  notilens emit <event> "msg" --agent <agent>

Metrics:
  notilens set.metrics <prompt_tokens> <completion_tokens> [confidence] --agent <agent> [--task <id>]

Options:
  --agent <agent>
  --task <id>
  --type success|warning|urgent|important|info
  --meta key=value            (repeatable)
  --image_url <url>
  --open_url <url>
  --download_url <url>
  --tags "tag1,tag2"
  --is_actionable true|false
  --confidence <0-1>

Other:
  notilens version""")
