import sys
import time
from importlib.metadata import version

from .config import get_agent_config, save_agent, remove_agent, list_agents
from .emitter import NotiLensEmitter
from .state import delete_state, get_state_file, read_state, update_state, write_state

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


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

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


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

    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 == "--level":
            flags["level"] = 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 helpers ─────────────────────────────────────────────────────────────

EVENT_LEVELS = {
    "task.started":         "info",
    "task.progress":        "info",
    "task.loop":            "warning",
    "task.retrying":        "warning",
    "task.completed":       "info",
    "task.stopped":         "info",
    "task.failed":          "error",
    "task.error":           "error",
    "task.timeout":         "error",
    "task.cancelled":       "warning",
    "task.terminated":      "error",
    "ai.call.start":        "info",
    "ai.call.complete":     "info",
    "ai.response.generated":"info",
    "ai.response.failed":   "error",
    "input.required":       "warning",
    "input.approved":       "info",
    "input.rejected":       "warning",
}


def get_emitter(agent: str) -> NotiLensEmitter:
    conf = get_agent_config(agent)
    if not conf or not conf.get("token") or not conf.get("secret"):
        print(f"❌ Agent '{agent}' not configured. Run: notilens init --agent {agent} --token TOKEN --secret SECRET",
              file=sys.stderr)
        sys.exit(1)
    return NotiLensEmitter(token=conf["token"], secret=conf["secret"], agent=agent)


def send_event(event: str, message: str, flags: dict) -> None:
    emitter = get_emitter(flags["agent"])
    state_file = get_state_file(flags["agent"], flags["task_id"])
    state = read_state(state_file)

    meta = {
        "task_id": flags["task_id"],
        "duration_ms": state.get("duration_ms", 0),
        "retry_count": state.get("retry_count", 0),
        "loop_count": state.get("loop_count", 0),
        "confidence_score": flags["confidence_score"],
        "image_url": flags["image_url"],
        "open_url": flags["open_url"],
        "download_url": flags["download_url"],
        "tags": flags["tags"],
        "is_actionable": flags["is_actionable"],
        **flags["meta"],
    }

    level = flags["level"] or EVENT_LEVELS.get(event, "info")
    emitter.send(event, message, level=level, meta=meta)
    # allow background thread to flush
    time.sleep(0.3)
    emitter.shutdown()


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


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

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

    # ── Setup ─────────────────────────────────────────────────────────────────
    if command == "init":
        agent = token = secret = ""
        i = 0
        while i < len(rest):
            if rest[i] == "--agent" and i + 1 < len(rest):
                agent = rest[i + 1]; i += 2
            elif rest[i] == "--token" and i + 1 < len(rest):
                token = rest[i + 1]; i += 2
            elif rest[i] == "--secret" and i + 1 < len(rest):
                secret = rest[i + 1]; i += 2
            else:
                i += 1
        if not agent or not token or not secret:
            print("Usage: notilens init --agent AGENT --token TOKEN --secret SECRET", file=sys.stderr)
            sys.exit(1)
        save_agent(agent, token, secret)
        print(f"✔ Agent '{agent}' configured")

    elif command == "agents":
        agents = list_agents()
        if agents:
            for a in agents:
                print(f"  • {a}")
        else:
            print("No agents configured.")

    elif command == "remove-agent":
        if not rest:
            print("Usage: notilens remove-agent AGENT", file=sys.stderr)
            sys.exit(1)
        if remove_agent(rest[0]):
            print(f"✔ Agent '{rest[0]}' removed")
        else:
            print(f"❌ Agent '{rest[0]}' not found", file=sys.stderr)

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

    elif command == "task.progress":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        sf = get_state_file(flags["agent"], flags["task_id"])
        update_state(sf, {"duration_ms": calc_duration(sf)})
        send_event("task.progress", msg, flags)
        print(f"⏳ Progress: {flags['agent']} | {flags['task_id']}")

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

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

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

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

    elif command == "task.error":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        sf = get_state_file(flags["agent"], flags["task_id"])
        update_state(sf, {"duration_ms": calc_duration(sf), "last_error": msg})
        send_event("task.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)
        sf = get_state_file(flags["agent"], flags["task_id"])
        update_state(sf, {"duration_ms": calc_duration(sf)})
        send_event("task.failed", msg, flags)
        delete_state(sf)
        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)
        sf = get_state_file(flags["agent"], flags["task_id"])
        update_state(sf, {"duration_ms": calc_duration(sf)})
        send_event("task.timeout", msg, flags)
        delete_state(sf)
        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)
        sf = get_state_file(flags["agent"], flags["task_id"])
        update_state(sf, {"duration_ms": calc_duration(sf)})
        send_event("task.cancelled", msg, flags)
        delete_state(sf)
        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)
        sf = get_state_file(flags["agent"], flags["task_id"])
        update_state(sf, {"duration_ms": calc_duration(sf)})
        send_event("task.terminated", msg, flags)
        delete_state(sf)
        print(f"⚠️  Terminated: {flags['agent']} | {flags['task_id']}")

    # ── Input ─────────────────────────────────────────────────────────────────
    elif command == "input.required":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_event("input.required", msg, flags)

    elif command == "input.approve":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_event("input.approved", msg, flags)

    elif command == "input.reject":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_event("input.rejected", msg, flags)

    # ── AI response ───────────────────────────────────────────────────────────
    elif command == "ai.response.generate":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_event("ai.response.generated", msg, flags)

    elif command == "ai.response.fail":
        pos = positional_args(rest)
        msg = pos[0] if pos else ""
        flags = parse_flags(rest)
        send_event("ai.response.failed", msg, flags)

    # ── Generic emit ──────────────────────────────────────────────────────────
    elif command == "emit":
        pos = positional_args(rest)
        event = pos[0] if len(pos) > 0 else ""
        msg = pos[1] if len(pos) > 1 else ""
        if not event:
            print("Usage: notilens emit EVENT MESSAGE --agent AGENT", file=sys.stderr)
            sys.exit(1)
        flags = parse_flags(rest[2:])
        sf = get_state_file(flags["agent"], flags["task_id"])
        update_state(sf, {"duration_ms": calc_duration(sf)})
        send_event(event, msg, flags)
        print(f"📡 Emitted: {event}")

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

    else:
        print_usage()


def print_usage():
    print("""Usage: notilens <command> [options]

Setup:
  notilens init --agent AGENT --token TOKEN --secret SECRET
  notilens agents
  notilens remove-agent AGENT

Task Lifecycle:
  notilens task.start --agent AGENT [--task ID]
  notilens task.progress "msg" --agent AGENT --task ID
  notilens task.loop "msg" --agent AGENT --task ID
  notilens task.retry --agent AGENT --task ID
  notilens task.stop --agent AGENT --task ID
  notilens task.complete "msg" --agent AGENT --task ID
  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

Input:
  notilens input.required "msg" --agent AGENT --task ID
  notilens input.approve "msg" --agent AGENT --task ID
  notilens input.reject "msg" --agent AGENT --task ID

AI Response:
  notilens ai.response.generate "msg" --agent AGENT --task ID
  notilens ai.response.fail "msg" --agent AGENT --task ID

Generic:
  notilens emit EVENT "msg" --agent AGENT [--task ID]

Options:
  --agent AGENT
  --task ID
  --level debug|info|warning|error
  --meta key=value          (repeatable)
  --image_url URL
  --open_url URL
  --download_url URL
  --tags "tag1,tag2"
  --is_actionable true|false
  --confidence 0.0-1.0

Other:
  notilens version""")
