import sys
import os
import argparse
import configparser
from pathlib import Path

from src.scheduler import Config
from src.log import init_logger
from src.utils.data_utils import parse_json_param

project_root = Path(__file__).parent.resolve()


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("--testcase_dir", required=True)
    parser.add_argument("--log_dir", required=True)
    # `--pytest_testkit_whl_path` 是面向 testkit 包开发/测试场景的内部参数,
    # 用户正式使用一律走 `pip install pytest-testkit` 拉取 pypi 上的发布版本。
    # 同步在 --help 中隐藏, 避免用户参考帮助时误以为有 whl 安装的正式选项。
    parser.add_argument(
        "--pytest_testkit_whl_path",
        required=False,
        help=argparse.SUPPRESS,
    )
    # `--pytest_testkit_path` 是已废弃的兼容参数, 仅做 deprecation 告警,
    # 不再驱动 whl 安装 (避免老脚本未同步改名时仍走 whl 路径, 强制切到 pip 链路)。
    parser.add_argument(
        "--pytest_testkit_path",
        required=False,
        help=argparse.SUPPRESS,
    )
    parser.add_argument("--testcase_collector_path", required=False)
    parser.add_argument("--obs", required=True)
    parser.add_argument("--task_id", required=True)
    parser.add_argument("--archive_log_dir", required=False)
    parser.add_argument("--env_device_resource", required=False)
    parser.add_argument("--pipeline_id", required=True)
    parser.add_argument("--pipeline_run_id", required=True)
    parser.add_argument("--job_run_id", required=True)
    parser.add_argument("--log_conf", required=False)
    parser.add_argument(
        "--scheduler_secret",
        required=False,
        default=None,
        help=(
            "Fallback when SCHEDULER_SECRET env var is absent. "
            "Prefer env var to avoid exposing the secret in argv."
        ),
    )
    parser.add_argument("--testcase_config_file", required=False)
    parser.add_argument(
        "--collect_config_file",
        required=False,
        default=None,
        help=(
            "Optional pytest config file (relative to testcase_dir) used for "
            "test case metadata collection; defaults to the original flow when omitted"
        ),
    )
    parser.add_argument("--max_workers", required=False)
    parser.add_argument("--image_label", required=False)
    parser.add_argument(
        "--env_deploy_model",
        required=False,
        default="Dislocated",
        help="Environment deployment model: Co-located or Dislocated",
    )
    parser.add_argument(
        "--platform",
        required=False,
        default="codearts",
        help="Platform name prefix for environment naming, default: codearts",
    )
    parser.add_argument(
        "--fail_case",
        required=False,
        type=str,
        help="Path to JSON file containing failed test cases result",
    )
    parser.add_argument(
        "--ci_env_info",
        required=False,
        type=str,
        help=(
            "Grouped JSON of CI runtime metadata under the ci_env_info key, "
            "injected into test cases as the ci_env_info environment variable"
        ),
    )
    parser.add_argument(
        "--debug_keep_env",
        required=False,
        action="store_true",
        help=argparse.SUPPRESS,
    )
    return parser.parse_args()


def _init_logger(log_dir: str):
    log_dir = Path(log_dir)
    log_dir.mkdir(parents=True, exist_ok=True)
    return init_logger(log_dir)


_SENSITIVE_ENV_KEYWORDS = {
    "password",
    "pwd",
    "secret",
    "token",
    "access_token",
    "ak",
    "sk",
    "api_key",
    "api_secret",
    "private_key",
    "ssh_key",
    "rsa_key",
    "pem",
    "credential",
    "cred",
    "auth",
    "username",
    "user",
    "gitcode",
    "obs",
}


def _sanitize_env_vars():
    for key in list(os.environ.keys()):
        # SCHEDULER_SECRET is consumed and cleared in _init_config; skip here
        # so the real value survives sanitization and can be popped explicitly.
        if key == "SCHEDULER_SECRET":
            continue
        key_lower = key.lower()
        if any(keyword in key_lower for keyword in _SENSITIVE_ENV_KEYWORDS):
            os.environ[key] = "***"


def _parse_ci_env_info(raw):
    """解析并校验 --ci_env_info,输出规范的分组结构。

    Args:
        raw: 插件传入的 JSON 字符串,形如 {"ci_env_info": {...}},可能为空

    Returns:
        dict: {"ci_env_info": {字段名: 字符串值}},白名单字段恒在
    """
    return Config.validate_ci_env_info(parse_json_param(raw, "ci_env_info"))


def _init_config(args):
    Config.set("obs_info", args.obs)
    Config.set("task_id", args.task_id)
    Config.set(
        "env_device_resource",
        parse_json_param(args.env_device_resource, "env_device_resource"),
    )
    Config.set("log_conf", parse_json_param(args.log_conf, "log_conf"))
    # `--pytest_testkit_path` 已被改名为 `--pytest_testkit_whl_path`。
    # 保留 Config.pytest_testkit_path 字段名以避免连片重命名;
    # 兼容策略: 老参数传值时仅打 deprecation 警告, 不再驱动 whl 安装 (强制走 pip);
    # 只有新参数 `--pytest_testkit_whl_path` 才真正生效。
    Config.set(
        "pytest_testkit_path",
        Path(args.pytest_testkit_whl_path) if args.pytest_testkit_whl_path else None,
    )
    Config.set(
        "testcase_collector_path",
        Path(args.testcase_collector_path) if args.testcase_collector_path else None,
    )
    Config.set("test_dir", Path(args.testcase_dir))
    Config.set("max_workers", int(args.max_workers))
    Config.set("output_json_path", Path(args.log_dir) / "output.json")

    job_info = {
        "pipeline_id": args.pipeline_id,
        "pipeline_run_id": args.pipeline_run_id,
        "job_id": args.job_run_id,
    }
    Config.set("job_info", job_info)
    job_unique_id = f"{args.pipeline_id}-{args.pipeline_run_id}-{args.job_run_id}"
    Config.set("job_unique_id", job_unique_id)
    Config.set("ci_env_info", _parse_ci_env_info(args.ci_env_info))

    scheduler_secret_raw = (
        os.environ.pop("SCHEDULER_SECRET", None) or args.scheduler_secret
    )
    if not scheduler_secret_raw:
        raise ValueError(
            "scheduler_secret must be provided via SCHEDULER_SECRET env var "
            "or --scheduler_secret argument"
        )
    scheduler_secret = parse_json_param(scheduler_secret_raw, "scheduler_secret")
    Config.set("scheduler_secret", scheduler_secret)
    Config.set("testcase_config_file", args.testcase_config_file)
    Config.set("collect_config_file", args.collect_config_file)

    # Read only_fail from testcase_config_file
    only_fail = False
    if args.testcase_config_file:
        test_dir = Path(args.testcase_dir)
        testcase_config_path = test_dir / args.testcase_config_file

        if testcase_config_path.exists():
            config_parser = configparser.ConfigParser()
            config_parser.read(testcase_config_path, encoding="utf-8")
            if (
                "only-fail" in config_parser
                and "run_only_fail_case" in config_parser["only-fail"]
            ):
                only_fail_value = config_parser["only-fail"]["run_only_fail_case"]
                only_fail = only_fail_value.lower() in ("true", "1", "yes")

    image_label = args.image_label or ""
    Config.set("image_label", image_label)

    archive_log_dir = (
        Path(args.archive_log_dir)
        if args.archive_log_dir
        else project_root / "case-logs"
    )
    Config.set("archive_log_dir", archive_log_dir)
    archive_log_dir.mkdir(parents=True, exist_ok=True)

    Config.set("env_deploy_model", args.env_deploy_model)
    Config.set("platform", args.platform)
    Config.set("only_fail", only_fail)
    Config.set("fail_case", args.fail_case)
    # 调试模式开关 + 透传 health_ttl:
    #   debug_keep_env=True  -> health_ttl=-1 (不启动心跳, 不释放环境)
    #   debug_keep_env=False -> health_ttl=600 (默认 10 分钟, 每 200s 上报一次心跳)
    Config.set("debug_keep_env", args.debug_keep_env)
    Config.set(
        "lease_health_ttl_seconds",
        -1 if args.debug_keep_env else 600,
    )


def main():
    args = parse_args()
    _sanitize_env_vars()
    _init_logger(args.log_dir)
    _init_config(args)

    from src.run_scheduler import SchedulerRunner

    scheduler_runner = SchedulerRunner()
    return scheduler_runner.run()


if __name__ == "__main__":
    success = main()
    sys.exit(0 if success else 1)