#!/usr/bin/env python3
# encoding: utf-8
import argparse
import requests
import yaml
import os
import sys
import time
from urllib.parse import quote

# Маппинг queue_name → файл
QUEUE_NAME_MAPPING = {
    # Добавьте ваши маппинги здесь
}


def parse_args():
    parser = argparse.ArgumentParser(
        description="Generate Kubernetes manifests for RabbitMQ queues."
    )
    parser.add_argument("pattern", help="Partial queue name to search")
    parser.add_argument("--host", default="localhost", help="RabbitMQ host")
    parser.add_argument("--port", default=15672, type=int, help="RabbitMQ API port")
    parser.add_argument("--vhost", default="ev-test-yandex2", help="RabbitMQ vhost")
    parser.add_argument("--cluster", help="Cluster name")
    parser.add_argument("--env", help="Environment")
    parser.add_argument("--namespace", default="rabbitmq", help="Namespace")
    parser.add_argument("--output-dir", "-o", default=".", help="Output directory")
    parser.add_argument("--rmq-user", help="RabbitMQ username")
    parser.add_argument("--rmq-pass", help="RabbitMQ password")
    parser.add_argument(
        "--include-delayed",
        action="store_true",
        help="Include delayed exchanges and bindings",
    )
    return parser.parse_args()


def api_get(url, auth):
    response = requests.get(url, auth=auth)
    if response.status_code != 200:
        return None
    return response.json()


def check_delayed_queue(base_url, vhost, queue_name, auth, retries=3, cooldown=1):
    """Проверяет наличие отложенной очереди queue_name_delayed с ретраем"""
    delayed_name = f"{queue_name}_delayed"
    url = f"{base_url}/api/queues/{quote(vhost)}/{quote(delayed_name)}"
    for attempt in range(1, retries + 1):
        resp = requests.get(url, auth=auth)
        if resp.status_code == 200:
            print(f"[INFO] Found delayed queue: {delayed_name}", file=sys.stderr)
            return True
        time.sleep(cooldown)
    print(
        f"[WARN] Delayed queue not found after {retries} attempts: {delayed_name}",
        file=sys.stderr,
    )
    return False


def get_queue_resources(base_url, vhost, queue_name, auth, include_delayed=False):
    """Получаем очередь, обмены и привязки для указанной очереди"""
    # Сначала пытаемся проверить наличие отложенной очереди
    check_delayed_queue(base_url, vhost, queue_name, auth)

    # Получаем информацию о основной очереди
    queue_url = f"{base_url}/api/queues/{quote(vhost)}/{quote(queue_name)}"
    queue_info = api_get(queue_url, auth)
    if not queue_info:
        print(f"[ERROR] Queue not found: {queue_name}", file=sys.stderr)
        return None

    # Получаем все bindings
    bindings_url = f"{base_url}/api/queues/{quote(vhost)}/{quote(queue_name)}/bindings"
    bindings = api_get(bindings_url, auth) or []

    # Определяем все источники обменов из привязок
    exchange_names = {b.get("source") for b in bindings if b.get("source")}
    exchanges = []
    delayed_exchanges = set()

    # Получаем информацию по каждому обмену
    for ex_name in exchange_names:
        ex_url = f"{base_url}/api/exchanges/{quote(vhost)}/{quote(ex_name)}"
        ex_info = api_get(ex_url, auth)
        if not ex_info:
            continue

        is_delayed = ex_info.get(
            "type"
        ) == "x-delayed-message" or "x-delayed-type" in ex_info.get("arguments", {})
        if is_delayed:
            delayed_exchanges.add(ex_name)

        # Если включаем delayed-обмены или он не delayed
        if include_delayed or not is_delayed:
            exchange_data = {
                "name": ex_info["name"],
                "type": ex_info["type"],
                "durable": ex_info["durable"],
                "autoDelete": ex_info["auto_delete"],
                "internal": ex_info["internal"],
            }
            if ex_info.get("arguments"):
                exchange_data["arguments"] = ex_info["arguments"]
            exchanges.append(exchange_data)

    # Фильтруем привязки: исключаем delayed-привязки, если include_delayed=False
    filtered_bindings = []
    for b in bindings:
        # Определяем задержанные биндинги по аргументу x-delay
        is_binding_delayed = b.get("arguments", {}).get("x-delay") is not None
        if not include_delayed and is_binding_delayed:
            continue
        filtered_bindings.append(
            {
                "source": b.get("source", ""),
                "destination": b["destination"],
                "destinationType": b["destination_type"],
                "routingKey": b["routing_key"],
                "propertiesKey": b["properties_key"],
                "arguments": b.get("arguments", {}),
            }
        )

    return {
        "queue": {
            "name": queue_info["name"],
            "durable": queue_info["durable"],
            "autoDelete": queue_info["auto_delete"],
            "exclusive": queue_info["exclusive"],
            "arguments": queue_info.get("arguments", {}),
        },
        "exchanges": exchanges,
        "bindings": filtered_bindings,
    }


def generate_resource_name(queue_name, resource_type, index=None):
    """Генерирует имя ресурса на основе имени очереди"""
    # Разбираем имя очереди по точкам
    parts = queue_name.split(".")

    # Ищем ключевые компоненты в имени очереди
    service_part = ""
    consumer_part = ""

    # Ищем Service и Consumer части
    for i, part in enumerate(parts):
        if part.lower() == "service" and i > 0:
            service_part = parts[i - 1].lower()  # Берем часть перед Service
        elif part.lower() == "consumers" and i + 2 < len(parts):
            # Берем Consumer класс (обычно последняя часть)
            consumer_part = parts[-1].lower()

    # Если не нашли стандартную структуру, используем упрощенную логику
    if not service_part or not consumer_part:
        # Пробуем извлечь значимые части из имени
        meaningful_parts = []
        for part in parts:
            part_lower = part.lower()
            if part_lower not in [
                "q",
                "eventicious",
                "host",
                "consumers",
                "masstransit",
            ]:
                if part_lower == "service":
                    continue
                meaningful_parts.append(part_lower)

        if len(meaningful_parts) >= 2:
            service_part = meaningful_parts[0]
            consumer_part = meaningful_parts[-1]
        else:
            # Fallback: используем первую и последнюю части
            service_part = parts[1].lower() if len(parts) > 1 else parts[0].lower()
            consumer_part = parts[-1].lower()

    # Сокращаем части
    service_short = service_part[:5] if len(service_part) > 5 else service_part

    # Обрабатываем consumer часть - убираем "consumer" в конце и берем ключевые слова
    consumer_clean = consumer_part.replace("consumer", "").replace("requested", "req")

    # Разбиваем CamelCase на части
    import re

    consumer_words = re.findall(r"[A-Z][a-z]*|[a-z]+", consumer_clean)
    consumer_short = "".join(
        word.lower() for word in consumer_words[:3]
    )  # Берем первые 3 слова

    base_name = f"{service_short}-serv-{consumer_short}"

    # Обрезаем до разумной длины
    base_name = base_name[:50]

    if resource_type == "binding" and index is not None:
        return f"{base_name}-bind-{index}"
    else:
        return base_name


def generate_manifests(resources, namespace):
    """Генерирует манифесты Kubernetes для RabbitMQ ресурсов"""
    manifests = []
    queue_name = resources["queue"]["name"]
    resource_base_name = generate_resource_name(queue_name, "queue")

    # Создаем Binding манифесты
    for i, binding in enumerate(resources["bindings"], 1):
        binding_name = generate_resource_name(queue_name, "binding", i)

        binding_manifest = {
            "apiVersion": "rabbitmq.com/v1beta1",
            "kind": "Binding",
            "metadata": {"name": binding_name, "namespace": namespace},
            "spec": {
                "rabbitmqClusterReference": {"name": "CLUSTER_NAME_PLACEHOLDER"},
                "vhost": "VHOST_PLACEHOLDER",
                "source": binding["source"],
                "destination": binding["destination"],
                "destinationType": binding["destinationType"],
                "routingKey": binding["routingKey"],
            },
        }

        # Добавляем arguments если они есть
        if binding["arguments"]:
            binding_manifest["spec"]["arguments"] = binding["arguments"]

        manifests.append(binding_manifest)

    # Создаем Exchange манифесты
    for exchange in resources["exchanges"]:
        exchange_manifest = {
            "apiVersion": "rabbitmq.com/v1beta1",
            "kind": "Exchange",
            "metadata": {"name": resource_base_name, "namespace": namespace},
            "spec": {
                "name": exchange["name"],
                "rabbitmqClusterReference": {"name": "CLUSTER_NAME_PLACEHOLDER"},
                "vhost": "VHOST_PLACEHOLDER",
                "type": exchange["type"],
                "durable": exchange["durable"],
                "autoDelete": exchange["autoDelete"],
            },
        }

        # Добавляем arguments если они есть (важно для delayed exchanges)
        if exchange.get("arguments"):
            exchange_manifest["spec"]["arguments"] = exchange["arguments"]

        manifests.append(exchange_manifest)

    # Создаем Queue манифест
    queue_manifest = {
        "apiVersion": "rabbitmq.com/v1beta1",
        "kind": "Queue",
        "metadata": {"name": resource_base_name, "namespace": namespace},
        "spec": {
            "name": resources["queue"]["name"],
            "rabbitmqClusterReference": {"name": "CLUSTER_NAME_PLACEHOLDER"},
            "vhost": "VHOST_PLACEHOLDER",
            "durable": resources["queue"]["durable"],
            "autoDelete": resources["queue"]["autoDelete"],
        },
    }

    # Добавляем arguments если они есть
    if resources["queue"]["arguments"]:
        queue_manifest["spec"]["arguments"] = resources["queue"]["arguments"]
    else:
        queue_manifest["spec"]["arguments"] = {}

    manifests.append(queue_manifest)

    return manifests


def manifests_to_yaml(manifests):
    """Конвертирует список манифестов в YAML строку"""
    yaml_parts = []
    for manifest in manifests:
        yaml_str = yaml.dump(
            manifest, default_flow_style=False, allow_unicode=True, sort_keys=False
        )
        yaml_parts.append(yaml_str.strip())

    return "---\n" + "\n---\n".join(yaml_parts)


def main():
    args = parse_args()

    # Аутентификация
    if args.rmq_user and args.rmq_pass:
        auth = (args.rmq_user, args.rmq_pass)
    elif "RMQ_CREDS" in os.environ:
        auth = tuple(os.environ["RMQ_CREDS"].split(":"))
    else:
        print("Error: no credentials", file=sys.stderr)
        sys.exit(1)

    base_url = f"http://{args.host}:{args.port}"
    queues_url = f"{base_url}/api/queues/{quote(args.vhost)}"
    all_queues = api_get(queues_url, auth)

    if not all_queues:
        sys.exit("Error: could not get queues")

    matching_queues = [q["name"] for q in all_queues if args.pattern in q["name"]]

    if not matching_queues:
        print(f"No queues matched: {args.pattern}", file=sys.stderr)
        sys.exit(0)

    os.makedirs(args.output_dir, exist_ok=True)

    for queue_name in matching_queues:
        print(f"Processing: {queue_name}", file=sys.stderr)

        resources = get_queue_resources(
            base_url, args.vhost, queue_name, auth, args.include_delayed
        )

        if not resources:
            print(f"Skip queue {queue_name}", file=sys.stderr)
            continue

        # Генерируем манифесты
        manifests = generate_manifests(resources, args.namespace)
        yaml_output = manifests_to_yaml(manifests)

        # Определяем имя файла
        file_basename = QUEUE_NAME_MAPPING.get(
            queue_name, generate_resource_name(queue_name, "queue")
        )

        output_file = os.path.join(args.output_dir, f"{file_basename}.yaml")

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

        print(f"Written: {output_file}", file=sys.stderr)


if __name__ == "__main__":
    main()

перепиши скрипт, чтобы он корректно собирал данные с rmq api о всех exchange, биндингах и самих очередях. мне нужноч тобы он ничего не упустил в реббите и каррктно отразил это в виде манифестов.
сейчас, например, я вижу что он собирает не все биндинги и не собирает _delayed ресурсы, которые тоже нужно отражать