# pyright: reportCallIssue=false

from __future__ import annotations

import json
from typing import Any

import allure  # pyright: ignore[reportMissingImports]
from allure_commons.types import AttachmentType  # pyright: ignore[reportMissingImports]
from behave import then, when

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,
    )


def _get_change_ids(context) -> dict[str, str]:
    ids = getattr(context, "ticket_category_change_ids", None)
    assert isinstance(ids, dict), f"Нет ticket_category_change_ids в контексте: {ids}"
    for k in ("old", "in_group", "out_group"):
        assert ids.get(k), f"ticket_category_change_ids[{k!r}] пустой: {ids}"
    return ids


def _get_change_titles(context) -> dict[str, str]:
    titles = getattr(context, "ticket_category_change_titles", None)
    assert isinstance(titles, dict), f"Нет ticket_category_change_titles в контексте: {titles}"
    for k in ("old", "in_group", "out_group"):
        assert titles.get(k), f"ticket_category_change_titles[{k!r}] пустой: {titles}"
    return titles


@when("change ticket category to in_group category")  # pyright: ignore[reportGeneralTypeIssues]
def step_change_category_to_in_group(context) -> None:
    td = TicketTestData.from_behave_context(context)
    token = td.ensure_token()
    ids = _get_change_ids(context)
    ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
    assert ticket_id, "Нет ticket_id (ticket ещё не создан)."

    mutation = """
mutation updatecategory($ticket_id: String!, $category_id: String!) {
  changeTicketCategory(dto: {ticket_id: $ticket_id, category_id: $category_id})
}
""".strip()
    variables = {"ticket_id": ticket_id, "category_id": ids["in_group"]}
    with allure.step("GraphQL: changeTicketCategory (to in_group)"):
        resp = execute_graphql(query=mutation, variables=variables, company_id=td.company_id, access_token=token)
        _attach_json("changeTicketCategory response", resp)
        context.change_category_response = resp


@when("change ticket category to out_group category")  # pyright: ignore[reportGeneralTypeIssues]
def step_change_category_to_out_group(context) -> None:
    td = TicketTestData.from_behave_context(context)
    token = td.ensure_token()
    ids = _get_change_ids(context)
    ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
    assert ticket_id, "Нет ticket_id (ticket ещё не создан)."

    mutation = """
mutation updatecategory($ticket_id: String!, $category_id: String!) {
  changeTicketCategory(dto: {ticket_id: $ticket_id, category_id: $category_id})
}
""".strip()
    variables = {"ticket_id": ticket_id, "category_id": ids["out_group"]}
    with allure.step("GraphQL: changeTicketCategory (to out_group)"):
        resp = execute_graphql(query=mutation, variables=variables, company_id=td.company_id, access_token=token)
        _attach_json("changeTicketCategory response", resp)
        context.change_category_response = resp


@when("query tickets by created place id")  # pyright: ignore[reportGeneralTypeIssues]
def step_query_tickets_by_place(context) -> None:
    td = TicketTestData.from_behave_context(context)
    token = td.ensure_token()
    place_id = td.place_id
    assert place_id, "Нет place_id (place ещё не создан)."

    query = """
query tickets($place_id: String!) {
  ticket(
    filter: { place_id: $place_id },
    pagination: { limit: 100, skip: 0 }
  ) {
    results {
      number
      id
      category { id title }
      assignee { id user { id username data { first_name last_name } } }
    }
  }
}
""".strip()
    variables = {"place_id": place_id}
    with allure.step("GraphQL: ticket(filter: place_id)"):
        resp = execute_graphql(query=query, variables=variables, company_id=td.company_id, access_token=token)
        _attach_json("ticket response", resp)
        context.ticket_query_response = resp


def _find_ticket_in_results(context, *, ticket_id: str) -> dict[str, Any]:
    resp = getattr(context, "ticket_query_response", None)
    assert isinstance(resp, dict), f"Нет ticket_query_response или не dict: {resp}"
    results = resp.get("data", {}).get("ticket", {}).get("results", [])
    assert isinstance(results, list), f"ticket.results не list. Ответ: {resp}"
    for item in results:
        if isinstance(item, dict) and item.get("id") == ticket_id:
            return item
    _attach_json("ticket.results (extracted)", results)
    raise AssertionError(f"Не нашли ticket id={ticket_id} в ticket.results")


@then("ticket category changed from old to in_group")  # pyright: ignore[reportGeneralTypeIssues]
def step_assert_category_changed_to_in_group(context) -> None:
    td = TicketTestData.from_behave_context(context)
    ids = _get_change_ids(context)
    titles = _get_change_titles(context)
    ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
    assert ticket_id, "Нет ticket_id."

    item = _find_ticket_in_results(context, ticket_id=ticket_id)
    category = item.get("category")
    assert isinstance(category, dict), f"ticket.category не объект: {category}"
    assert category.get("id"), "ticket.category.id пустой"
    assert category.get("title"), "ticket.category.title пустой"
    assert category["id"] != ids["old"], f"Категория не поменялась, всё ещё old={ids['old']}"
    assert category["id"] == ids["in_group"], f"Ожидали in_group={ids['in_group']}, получили {category['id']}"
    assert category["title"] != titles["old"], f"title не поменялся, всё ещё old title={titles['old']!r}"
    assert category["title"] == titles["in_group"], f"Ожидали title={titles['in_group']!r}, получили {category['title']!r}"


@then("employee is authorized for ticket")  # pyright: ignore[reportGeneralTypeIssues]
def step_assert_employee_authorized(context) -> None:
    td = TicketTestData.from_behave_context(context)
    ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
    assert ticket_id, "Нет ticket_id."

    item = _find_ticket_in_results(context, ticket_id=ticket_id)
    assignee = item.get("assignee")
    assert isinstance(assignee, dict), f"assignee должен быть объектом (уполномочен), получено: {assignee!r}"
    assert assignee.get("id"), "assignee.id пустой"

    user = assignee.get("user")
    assert isinstance(user, dict), f"assignee.user не объект: {user!r}"
    if td.account_id:
        assert user.get("id") == td.account_id, f"user.id не совпал с созданным user_id: {user.get('id')!r} != {td.account_id!r}"
    data = user.get("data")
    assert isinstance(data, dict), f"assignee.user.data не объект: {data!r}"
    assert data.get("first_name") == td.default_user_first_name, f"first_name не совпал: {data.get('first_name')!r}"
    assert data.get("last_name") == td.default_user_last_name, f"last_name не совпал: {data.get('last_name')!r}"


@then("employee is NOT authorized for ticket")  # pyright: ignore[reportGeneralTypeIssues]
def step_assert_employee_not_authorized(context) -> None:
    td = TicketTestData.from_behave_context(context)
    ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
    assert ticket_id, "Нет ticket_id."

    item = _find_ticket_in_results(context, ticket_id=ticket_id)
    assignee = item.get("assignee")
    if assignee is None:
        return
    # если assignee есть, то он должен быть не нашим user
    if isinstance(assignee, dict) and isinstance(assignee.get("user"), dict) and isinstance(assignee["user"].get("data"), dict):
        data = assignee["user"]["data"]
        assert not (
            data.get("first_name") == td.default_user_first_name and data.get("last_name") == td.default_user_last_name
        ), f"Employee всё ещё уполномочен (assignee совпал с созданным user): {data}"
        return
    raise AssertionError(f"Неожиданная структура assignee при проверке NOT authorized: {assignee!r}")

