#!/usr/bin/env python3
from __future__ import annotations

import os
from pathlib import Path
from typing import Any, Dict, List, Tuple

try:
    from .fleet_manifest import (
        DEFAULT_RUNTIME_ROOT,
        _bot_seed_is_placeholder,
        _dump_json,
        _load_json,
        _normalize_context_settings,
        _normalize_model_name,
        _write_minimal_llm_config,
    )
except ImportError:
    from fleet_manifest import (
        DEFAULT_RUNTIME_ROOT,
        _bot_seed_is_placeholder,
        _dump_json,
        _load_json,
        _normalize_context_settings,
        _normalize_model_name,
        _write_minimal_llm_config,
    )


def _prompt_text(label: str, default: str = "", *, required: bool = False) -> str:
    while True:
        hint = f" [{default}]" if default else ""
        raw = input(f"{label}{hint}: ").strip()
        if raw:
            return raw
        if default:
            return default
        if not required:
            return ""
        print("[INPUT] this field is required")


def _prompt_bool(label: str, default: bool) -> bool:
    suffix = "Y/n" if default else "y/N"
    while True:
        raw = input(f"{label} [{suffix}]: ").strip().lower()
        if not raw:
            return default
        if raw in {"y", "yes"}:
            return True
        if raw in {"n", "no"}:
            return False
        print("[INPUT] please answer y or n")


def _prompt_int(label: str, default: int, *, min_value: int = 0) -> int:
    while True:
        raw = input(f"{label} [{default}]: ").strip()
        if not raw:
            return max(min_value, default)
        try:
            value = int(raw)
        except ValueError:
            print("[INPUT] please enter a valid integer")
            continue
        if value < min_value:
            print(f"[INPUT] value must be >= {min_value}")
            continue
        return value


def _prompt_channel(default: str = "localweb") -> str:
    default_norm = default if default in {"telegram", "feishu", "whatsapp", "discord", "qq", "wechat", "localweb"} else "localweb"
    mapping = {
        "1": "telegram",
        "2": "feishu",
        "3": "whatsapp",
        "4": "discord",
        "5": "qq",
        "6": "wechat",
        "7": "localweb",
        "telegram": "telegram",
        "feishu": "feishu",
        "whatsapp": "whatsapp",
        "discord": "discord",
        "qq": "qq",
        "wechat": "wechat",
        "localweb": "localweb",
    }
    while True:
        raw = input(
            f"channel [1=telegram, 2=feishu, 3=whatsapp, 4=discord, 5=qq, 6=wechat, 7=localweb] [{default_norm}]: "
        ).strip().lower()
        if not raw:
            return default_norm
        value = mapping.get(raw)
        if value:
            return value
        print("[INPUT] channel must be one of: 1/2/3/4/5/6/7 or telegram/feishu/whatsapp/discord/qq/wechat/localweb")


def _prompt_bot_payload(
    *,
    index: int,
    seed_bot: Dict[str, Any] | None = None,
    existing_bot_ids: set[str] | None = None,
) -> Dict[str, Any]:
    seed = seed_bot if isinstance(seed_bot, dict) else {}
    existing = existing_bot_ids if existing_bot_ids is not None else set()
    seed_channel = str(seed.get("channel") or "localweb").strip().lower()
    print(f"\n[SETUP] bot {index}")
    channel = _prompt_channel(seed_channel)

    while True:
        bot_id_default = str(seed.get("bot_id") or f"{channel}_bot_{index}")
        bot_id = _prompt_text("bot_id", bot_id_default, required=True)
        if bot_id in existing:
            print(f"[INPUT] bot_id already exists: {bot_id}")
            continue
        break

    display_name = _prompt_text("display_name", str(seed.get("display_name") or bot_id), required=True)
    enabled = _prompt_bool("enabled", bool(seed.get("enabled", True)))
    default_webhooks = bool(seed.get("serve_webhooks", channel in {"localweb", "qq", "wechat"}))
    serve_webhooks = _prompt_bool("serve_webhooks", default_webhooks)

    bot_payload: Dict[str, Any] = {
        "bot_id": bot_id,
        "display_name": display_name,
        "channel": channel,
        "enabled": enabled,
        "serve_webhooks": serve_webhooks,
    }
    if serve_webhooks:
        bot_payload["host"] = _prompt_text("host", str(seed.get("host") or "127.0.0.1"), required=True)
        bot_payload["port"] = _prompt_int("port", int(seed.get("port") or (8764 + index)), min_value=1)

    if channel == "telegram":
        seed_telegram = seed.get("telegram") if isinstance(seed.get("telegram"), dict) else {}
        token = _prompt_text("telegram.bot_token", str(seed_telegram.get("bot_token") or ""), required=True)
        chats_default = ",".join(str(x) for x in seed_telegram.get("allowed_chats", []) if str(x).strip())
        chats_raw = _prompt_text("telegram.allowed_chats (comma-separated)", chats_default)
        bot_payload["telegram"] = {"bot_token": token, "allowed_chats": [x.strip() for x in chats_raw.split(",") if x.strip()]}
    elif channel == "feishu":
        seed_feishu = seed.get("feishu") if isinstance(seed.get("feishu"), dict) else {}
        bot_payload["feishu"] = {
            "mode": _prompt_text("feishu.mode", str(seed_feishu.get("mode") or "long_connection"), required=True),
            "app_id": _prompt_text("feishu.app_id", str(seed_feishu.get("app_id") or ""), required=True),
            "app_secret": _prompt_text("feishu.app_secret", str(seed_feishu.get("app_secret") or ""), required=True),
            "verify_token": _prompt_text("feishu.verify_token", str(seed_feishu.get("verify_token") or "")),
            "encrypt_key": _prompt_text("feishu.encrypt_key", str(seed_feishu.get("encrypt_key") or "")),
        }
    elif channel == "whatsapp":
        seed_wa = seed.get("whatsapp") if isinstance(seed.get("whatsapp"), dict) else {}
        bot_payload["whatsapp"] = {
            "access_token": _prompt_text("whatsapp.access_token", str(seed_wa.get("access_token") or ""), required=True),
            "phone_number_id": _prompt_text("whatsapp.phone_number_id", str(seed_wa.get("phone_number_id") or ""), required=True),
            "verify_token": _prompt_text("whatsapp.verify_token", str(seed_wa.get("verify_token") or ""), required=True),
            "api_version": _prompt_text("whatsapp.api_version", str(seed_wa.get("api_version") or "v21.0"), required=True),
        }
    elif channel == "discord":
        seed_dc = seed.get("discord") if isinstance(seed.get("discord"), dict) else {}
        bot_payload["discord"] = {
            "bot_token": _prompt_text("discord.bot_token", str(seed_dc.get("bot_token") or ""), required=True),
            "intents": _prompt_int("discord.intents", int(seed_dc.get("intents") or 37377), min_value=1),
            "require_mention_in_guild": _prompt_bool("discord.require_mention_in_guild", bool(seed_dc.get("require_mention_in_guild", True))),
        }
    elif channel in {"qq", "wechat"}:
        seed_bridge = seed.get(channel) if isinstance(seed.get(channel), dict) else {}
        bot_payload[channel] = {
            "onebot_api_base": _prompt_text(
                f"{channel}.onebot_api_base",
                str(seed_bridge.get("onebot_api_base") or f"http://127.0.0.1:{5700 if channel == 'qq' else 5701}"),
                required=True,
            ),
            "onebot_access_token": _prompt_text(f"{channel}.onebot_access_token", str(seed_bridge.get("onebot_access_token") or "")),
            "onebot_post_secret": _prompt_text(f"{channel}.onebot_post_secret", str(seed_bridge.get("onebot_post_secret") or "")),
            "onebot_self_id": _prompt_text(f"{channel}.onebot_self_id", str(seed_bridge.get("onebot_self_id") or "")),
            "require_mention_in_group": _prompt_bool(f"{channel}.require_mention_in_group", bool(seed_bridge.get("require_mention_in_group", True))),
        }
    else:
        seed_local = seed.get("localweb") if isinstance(seed.get("localweb"), dict) else {}
        bot_payload["localweb"] = {"require_mention_in_group": _prompt_bool("localweb.require_mention_in_group", bool(seed_local.get("require_mention_in_group", True)))}
    return bot_payload


def interactive_manifest_setup(default_manifest_path: Path, *, seed_manifest: Dict[str, Any] | None = None) -> Tuple[Path, Dict[str, Any]]:
    seed = seed_manifest if isinstance(seed_manifest, dict) else {}
    seed_bots = seed.get("bots")
    bots_seed: List[Dict[str, Any]] = [b for b in seed_bots if isinstance(b, dict)] if isinstance(seed_bots, list) else []
    if bots_seed and all(_bot_seed_is_placeholder(item) for item in bots_seed):
        bots_seed = []
    print("[SETUP] Interactive manifest setup")
    manifest_input = _prompt_text("manifest path", str(default_manifest_path))
    manifest_path = Path(manifest_input).expanduser().resolve()

    runtime_seed = str(seed.get("runtime_root") or "")
    runtime_default = str(DEFAULT_RUNTIME_ROOT) if _is_placeholder_text(runtime_seed) else (runtime_seed or str(DEFAULT_RUNTIME_ROOT))
    fleet_seed = str(seed.get("fleet_config_path") or "")
    fleet_default_seed = "" if _is_placeholder_text(fleet_seed) else fleet_seed
    fleet_default = fleet_default_seed or str((Path(runtime_default).expanduser().resolve() / "fleet.generated.json"))
    runtime_root = _prompt_text("runtime_root", runtime_default, required=True)
    fleet_config_path = _prompt_text("fleet_config_path", fleet_default or str((Path(runtime_root).expanduser().resolve() / "fleet.generated.json")), required=True)

    seed_proxy = seed.get("proxy_env") if isinstance(seed.get("proxy_env"), dict) else {}
    print("[SETUP] proxy_env (leave empty if not needed)")
    http_proxy = _prompt_text("proxy_env.http_proxy", str(seed_proxy.get("http_proxy") or ""))
    https_proxy = _prompt_text("proxy_env.https_proxy", str(seed_proxy.get("https_proxy") or ""))
    all_proxy = _prompt_text("proxy_env.all_proxy", str(seed_proxy.get("all_proxy") or ""))

    bot_count_default = len(bots_seed) if bots_seed else 1
    bot_count = _prompt_int("bot count", bot_count_default, min_value=1)
    bots: List[Dict[str, Any]] = []

    existing_bot_ids: set[str] = set()
    for idx in range(bot_count):
        seed_bot = bots_seed[idx] if idx < len(bots_seed) else {}
        bot_payload = _prompt_bot_payload(index=idx + 1, seed_bot=seed_bot, existing_bot_ids=existing_bot_ids)
        existing_bot_ids.add(str(bot_payload.get("bot_id") or ""))
        bots.append(bot_payload)

    print("\n[SETUP] LLM provider config")
    print("[SETUP] You can add more models manually in llm_config later.")
    llm_seed = str(seed.get("llm_config_path") or "")
    llm_default_path = (
        str(Path(runtime_root).expanduser().resolve() / "config" / "llm_config.yaml")
        if _is_placeholder_text(llm_seed)
        else (llm_seed or str(Path(runtime_root).expanduser().resolve() / "config" / "llm_config.yaml"))
    )
    llm_config_path_text = _prompt_text("llm_config_path (will be created/updated)", llm_default_path, required=True)
    llm_config_path = Path(llm_config_path_text).expanduser().resolve()
    llm_base_url = _prompt_text("llm.base_url", "https://openrouter.ai/api/v1", required=True)
    llm_api_key_default = os.environ.get("OPENAI_API_KEY", "")
    llm_api_key = _prompt_text("llm.api_key", llm_api_key_default, required=False)
    llm_model_raw = _prompt_text(
        "llm.model (recommended: full id like 'openai/gpt-4o'; bare names auto-add 'openai/')",
        "openai/google/gemini-3-flash-preview",
        required=True,
    )
    llm_model = _normalize_model_name(llm_model_raw)
    llm_multimodal = _prompt_bool("llm model supports multimodal (image input)", True)
    seed_context = _normalize_context_settings(seed.get("context"))
    print("\n[SETUP] Context compression config")
    context_user_history_tokens = _prompt_int("context.user_history_compress_threshold_tokens", int(seed_context.get("user_history_compress_threshold_tokens", 1500)), min_value=0)
    context_structured_agents = _prompt_int("context.structured_call_info_compress_threshold_agents", int(seed_context.get("structured_call_info_compress_threshold_agents", 10)), min_value=1)
    context_structured_tokens = _prompt_int("context.structured_call_info_compress_threshold_tokens", int(seed_context.get("structured_call_info_compress_threshold_tokens", 2200)), min_value=0)
    if llm_config_path.exists():
        overwrite = _prompt_bool(f"llm config exists, overwrite? {llm_config_path}", True)
        if not overwrite:
            print(f"[SETUP] keep existing llm config: {llm_config_path}")
        else:
            _write_minimal_llm_config(llm_config_path, base_url=llm_base_url, api_key=llm_api_key, model_name=llm_model, multimodal=llm_multimodal)
            print(f"[OK] llm config written: {llm_config_path}")
    else:
        _write_minimal_llm_config(llm_config_path, base_url=llm_base_url, api_key=llm_api_key, model_name=llm_model, multimodal=llm_multimodal)
        print(f"[OK] llm config written: {llm_config_path}")

    manifest: Dict[str, Any] = {
        "llm_config_path": str(llm_config_path),
        "runtime_root": runtime_root,
        "fleet_config_path": fleet_config_path,
        "proxy_env": {"http_proxy": http_proxy, "https_proxy": https_proxy, "all_proxy": all_proxy},
        "context": {
            "user_history_compress_threshold_tokens": int(context_user_history_tokens),
            "structured_call_info_compress_threshold_agents": int(context_structured_agents),
            "structured_call_info_compress_threshold_tokens": int(context_structured_tokens),
        },
        "bots": bots,
    }
    return manifest_path, manifest


def interactive_add_bot(manifest_path: Path) -> str:
    manifest = _load_json(manifest_path)
    bots = manifest.get("bots")
    if not isinstance(bots, list):
        bots = []
        manifest["bots"] = bots

    existing_ids = {
        str(item.get("bot_id") or "").strip()
        for item in bots
        if isinstance(item, dict) and str(item.get("bot_id") or "").strip()
    }
    new_payload = _prompt_bot_payload(index=len(existing_ids) + 1, existing_bot_ids=existing_ids)
    new_bot_id = str(new_payload.get("bot_id") or "").strip()
    bots.append(new_payload)
    _dump_json(manifest_path, manifest)
    return new_bot_id
