# pyright: reportCallIssue=false

from __future__ import annotations
import allure  # pyright: ignore[reportMissingImports]
from allure_commons.types import AttachmentType  # pyright: ignore[reportMissingImports]
from behave import then, when
import json
from typing import Any
from Ticket.testdata.ticket_test_data import TicketTestData
from worklib.graphql_client import execute_graphql

def _attach_json(name: str, payload: Any) -> None:
    allure.attach(
        json.dumps(payload, ensure_ascii=False, indent=2),
        name=name,
        attachment_type=AttachmentType.JSON,
    )
DEFAULT_TICKETINFO_PLACE_ID = "682733c16773cfa73dc8d0a7"


@when("create user for ticket")  # pyright: ignore[reportGeneralTypeIssues]
def step_create_user_for_ticket(context) -> None:
    td = TicketTestData.from_behave_context(context)
    td.create_user()
    context.ticket_account_id = td.account_id
    context.ticket_username = td.username


@when("create employee for created user")  # pyright: ignore[reportGeneralTypeIssues]
def step_create_employee(context) -> None:
    td = TicketTestData.from_behave_context(context)
    td.create_employee()
    context.ticket_employee_id = td.employee_id


@when("create category group for created category")  # pyright: ignore[reportGeneralTypeIssues]
def step_create_category_group(context) -> None:
    td = TicketTestData.from_behave_context(context)
    td.create_category_group()
    context.ticket_category_group_id = td.category_group_id


@when("connect employee to category group")  # pyright: ignore[reportGeneralTypeIssues]
def step_connect_employee(context) -> None:
    td = TicketTestData.from_behave_context(context)
    td.connect_employee_to_category_group()


@when("prepare ticket and employees for assign employee test")  # pyright: ignore[reportGeneralTypeIssues]
def step_prepare_ticket_and_employees_for_assign(context) -> None:
    """
    Подготовка:
    - 1 place
    - 1 category
    - 1 category group (для этой категории)
    - 3 employee:
      - fixed employee (в группе)
      - новый in_group employee (в группе)
      - новый out_group employee (вне группы)
    - 1 ticket в этой категории
    """
    td = TicketTestData.from_behave_context(context)
    token = td.ensure_token()

    # Берём существующую заявку и её категорию (createTicket часто запрещён)
    query = """
query ticketinfo($place_id: String!) {
  ticket(pagination:{skip:0,limit:25} ,filter:{place_id:$place_id}){
    results {
      id
      category { id title }
      assignee { id user { id username } }
    }
  }
}
""".strip()
    td.place_id = DEFAULT_TICKETINFO_PLACE_ID
    resp = execute_graphql(query=query, variables={"place_id": td.place_id}, company_id=td.company_id, access_token=token)
    results = resp.get("data", {}).get("ticket", {}).get("results", [])
    if isinstance(results, list) and results:
        ticket0 = results[0]
        assert isinstance(ticket0, dict) and ticket0.get("id"), f"Некорректный ticket: {ticket0!r}"
        category = ticket0.get("category")
        assert isinstance(category, dict) and category.get("id"), f"У ticket нет category: {ticket0!r}"
        context.ticket_id = ticket0["id"]
        td.ticket_id = ticket0["id"]
        category_id = category["id"]
    else:
        # fallback: создаём свою заявку, если это разрешено
        td.place_id = None
        td.ensure_place()
        category_id = td.ensure_ticket_category()
        try:
            td.create_ticket_with_category(category_id=category_id, place_id=td.place_id)
        except AssertionError as e:
            raise AssertionError(
                "Нет доступных tickets для проверки assignTicketEmployee (по умолчанию берём place_id "
                f"{DEFAULT_TICKETINFO_PLACE_ID}) и createTicket запрещён на стенде. "
                "Укажите place_id с существующими заявками (поменяйте DEFAULT_TICKETINFO_PLACE_ID в шаге) "
                "или дайте права на createTicket. "
                f"Детали: {e}"
            )
        context.ticket_id = td.ticket_id
        with allure.step("GraphQL: ticket(pagination:skip:0,limit:25,filter:place_id)"):
            resp = execute_graphql(query=query, variables={"place_id": td.place_id}, company_id=td.company_id, access_token=token)
            _attach_json("ticket response", resp)
            context.ticket_response = resp
    # employee #2 (new in-group)
    td.create_employee()
    assert td.account_id and td.employee_id, "Не создался in-group employee"
    in_group_user_id = td.account_id
    in_group_employee_id = td.employee_id

    fixed_member = {"user_id": td.fixed_user_id, "employee_id": td.fixed_employee_id}
    new_in_group_member = {"user_id": in_group_user_id, "employee_id": in_group_employee_id}

    td.create_category_group_for_categories([category_id], members=[fixed_member, new_in_group_member], cache=False)

    # employee #3 (out-group): создаём ещё одного нового employee, но НЕ добавляем в group
    td_out = TicketTestData(company_id=td.company_id)
    td_out.access_token = td.access_token
    td_out._cleanup_fns = getattr(context, "_cleanup_fns", None)
    td_out.create_employee()
    assert td_out.account_id and td_out.employee_id, "Не создался out-group employee"
    out_group_user_id = td_out.account_id

    # сохраняем ids для проверок
    context.assign_fixed_user_id = td.fixed_user_id
    context.assign_in_group_user_id = in_group_user_id
    context.assign_out_group_user_id = out_group_user_id


def _assign_ticket_employee(context, *, employee_user_id: str, expect_error: bool) -> None:
    td = TicketTestData.from_behave_context(context)
    token = td.ensure_token()
    ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
    assert ticket_id, "Нет ticket_id."

    mutation = """
mutation assignTicketEmployee($ticket_id: String!, $employee_user_id: String!) {
  assignTicketEmployee(dto: {ticket_id: $ticket_id, employee_user_id: $employee_user_id})
}
""".strip()
    variables = {"ticket_id": ticket_id, "employee_user_id": employee_user_id}
    try:
        resp = execute_graphql(query=mutation, variables=variables, company_id=td.company_id, access_token=token)
        context.assign_ticket_employee_response = resp
        if expect_error:
            raise AssertionError(f"Ожидали ошибку при assignTicketEmployee, но получили успех: {resp}")
    except Exception as e:
        context.assign_ticket_employee_error = str(e)
        if not expect_error:
            raise


@when("assign ticket to fixed in_group employee")  # pyright: ignore[reportGeneralTypeIssues]
def step_assign_ticket_fixed_employee(context) -> None:
    _assign_ticket_employee(context, employee_user_id=getattr(context, "assign_fixed_user_id"), expect_error=False)


@when("assign ticket to new in_group employee")  # pyright: ignore[reportGeneralTypeIssues]
def step_assign_ticket_new_in_group_employee(context) -> None:
    _assign_ticket_employee(context, employee_user_id=getattr(context, "assign_in_group_user_id"), expect_error=False)


@when("assign ticket to out_group employee (should fail)")  # pyright: ignore[reportGeneralTypeIssues]
def step_assign_ticket_out_group_employee(context) -> None:
    _assign_ticket_employee(context, employee_user_id=getattr(context, "assign_out_group_user_id"), expect_error=True)


@when("prepare ticket and employees for unassign employee test")  # pyright: ignore[reportGeneralTypeIssues]
def step_prepare_ticket_and_employees_for_unassign(context) -> None:
    """
    Подготовка для unassign:
    - 1 категория + 1 группа категорий (для этой категории)
    - 2 работника:
      - fixed employee (в группе): user_id/employee_id заданы константами
      - новый employee (в группе) — его будем assign/unassign
    - 1 ticket (существующий по place_id либо созданный, если разрешено)
    """
    td = TicketTestData.from_behave_context(context)
    token = td.ensure_token()

    query = """
query ticketinfo($place_id: String!) {
  ticket(pagination:{skip:0,limit:25} ,filter:{place_id:$place_id}){
    results {
      id
      category { id title }
      assignee { id user { id username } }
    }
  }
}
""".strip()
    td.place_id = DEFAULT_TICKETINFO_PLACE_ID
    resp = execute_graphql(query=query, variables={"place_id": td.place_id}, company_id=td.company_id, access_token=token)
    results = resp.get("data", {}).get("ticket", {}).get("results", [])

    if isinstance(results, list) and results:
        ticket0 = results[0]
        assert isinstance(ticket0, dict) and ticket0.get("id"), f"Некорректный ticket: {ticket0!r}"
        category = ticket0.get("category")
        assert isinstance(category, dict) and category.get("id"), f"У ticket нет category: {ticket0!r}"
        context.ticket_id = ticket0["id"]
        td.ticket_id = ticket0["id"]
        category_id = category["id"]
    else:
        # fallback: создаём свою заявку, если это разрешено
        td.place_id = None
        td.ensure_place()
        category_id = td.ensure_ticket_category()
        try:
            td.create_ticket_with_category(category_id=category_id, place_id=td.place_id)
        except AssertionError as e:
            raise AssertionError(
                "Нет доступных tickets для проверки unassignTicketEmployee (по умолчанию берём place_id "
                f"{DEFAULT_TICKETINFO_PLACE_ID}) и createTicket запрещён на стенде. "
                "Укажите place_id с существующими заявками (поменяйте DEFAULT_TICKETINFO_PLACE_ID в шаге) "
                "или дайте права на createTicket. "
                f"Детали: {e}"
            )
        context.ticket_id = td.ticket_id

    # новый employee (его будем assign/unassign)
    td_new = TicketTestData(company_id=td.company_id)
    td_new.access_token = td.access_token
    td_new._cleanup_fns = getattr(context, "_cleanup_fns", None)
    td_new.create_employee()
    assert td_new.account_id and td_new.employee_id, "Не создался новый employee для unassign"

    fixed_member = {"user_id": td.fixed_user_id, "employee_id": td.fixed_employee_id}
    new_member = {"user_id": td_new.account_id, "employee_id": td_new.employee_id}
    td.create_category_group_for_categories([category_id], members=[fixed_member, new_member], cache=False)

    context.unassign_new_user_id = td_new.account_id


@when("assign ticket to new grouped employee")  # pyright: ignore[reportGeneralTypeIssues]
def step_assign_ticket_to_new_grouped_employee(context) -> None:
    _assign_ticket_employee(context, employee_user_id=getattr(context, "unassign_new_user_id"), expect_error=False)


@when("unassign ticket from new grouped employee")  # pyright: ignore[reportGeneralTypeIssues]
def step_unassign_ticket_from_new_grouped_employee(context) -> None:
    td = TicketTestData.from_behave_context(context)
    token = td.ensure_token()
    ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
    assert ticket_id, "Нет ticket_id."
    employee_user_id = getattr(context, "unassign_new_user_id", None)
    assert isinstance(employee_user_id, str) and employee_user_id, "Нет unassign_new_user_id."

    mutation = """
mutation unassignTicketEmployee($ticket_id: String!, $employee_user_id: String!) {
  unassignTicketEmployee(dto: {ticket_id: $ticket_id, employee_user_id: $employee_user_id})
}
""".strip()
    variables = {"ticket_id": ticket_id, "employee_user_id": employee_user_id}
    resp = execute_graphql(query=mutation, variables=variables, company_id=td.company_id, access_token=token)
    context.unassign_ticket_employee_response = resp


def _get_ticket_assignee_user_id(context) -> str | None:
    td = TicketTestData.from_behave_context(context)
    ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
    assert ticket_id, "Нет ticket_id."
    resp = getattr(context, "ticket_query_response", None)
    assert isinstance(resp, dict), f"Нет ticket_query_response: {resp!r}"
    results = resp.get("data", {}).get("ticket", {}).get("results", [])
    assert isinstance(results, list), f"ticket.results не list: {results!r}"
    for item in results:
        if isinstance(item, dict) and item.get("id") == ticket_id:
            assignee = item.get("assignee")
            if assignee is None:
                return None
            if isinstance(assignee, dict) and isinstance(assignee.get("user"), dict):
                return assignee["user"].get("id")
            raise AssertionError(f"Неожиданная структура assignee: {assignee!r}")
    raise AssertionError(f"Не нашли ticket id={ticket_id} в results")


@then("ticket assignee is fixed employee")  # pyright: ignore[reportGeneralTypeIssues]
def step_assert_assignee_fixed(context) -> None:
    assert _get_ticket_assignee_user_id(context) == getattr(context, "assign_fixed_user_id")


@then("ticket assignee is new in_group employee")  # pyright: ignore[reportGeneralTypeIssues]
def step_assert_assignee_new_in_group(context) -> None:
    assert _get_ticket_assignee_user_id(context) == getattr(context, "assign_in_group_user_id")


@then("ticket assignee is still new in_group employee")  # pyright: ignore[reportGeneralTypeIssues]
def step_assert_assignee_still_new_in_group(context) -> None:
    assert _get_ticket_assignee_user_id(context) == getattr(context, "assign_in_group_user_id")


@then("ticket assignee is new grouped employee")  # pyright: ignore[reportGeneralTypeIssues]
def step_assert_assignee_new_grouped_employee(context) -> None:
    assert _get_ticket_assignee_user_id(context) == getattr(context, "unassign_new_user_id")


@then("ticket assignee is empty")  # pyright: ignore[reportGeneralTypeIssues]
def step_assert_assignee_empty(context) -> None:
    assignee_user_id = _get_ticket_assignee_user_id(context)
    assert assignee_user_id is None, f"Ожидали пустой assignee после unassign, получили user_id={assignee_user_id!r}"
