#
# This file is licensed under the Affero General Public License (AGPL) version 3.
#
# Copyright (C) 2026 Element Creations Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# See the GNU Affero General Public License for more details:
# <https://www.gnu.org/licenses/agpl-3.0.html>.
#

from typing import TypedDict

from typing_extensions import NotRequired, Unpack

from synapse.api.room_versions import (
    RoomVersion,
    RoomVersions,
)
from synapse.events import EventBase, make_event_from_dict
from synapse.federation.federation_base import event_from_pdu_json
from synapse.types import JsonDict


def default_event_fields(room_version: RoomVersion) -> JsonDict:
    """Return default values for every field required by `room_version`."""

    # We need to include entries for every required field for the room version.
    # Note that they don't necessarily have to be valid values, just enough to
    # allow us to construct the event class. (Ideally we'd build a fully valid
    # event, but this is fine for now.)
    defaults: JsonDict = {
        "type": "m.test",
        "sender": "@test:test",
        "content": {},
        "depth": 1,
        "origin_server_ts": 1,
        "hashes": {},
        "prev_events": [],
        "room_id": "!test:test",
    }

    # MSC4242 versions require prev_state_events but not auth_events.
    if room_version.msc4242_state_dags:
        defaults["prev_state_events"] = []
    else:
        defaults["auth_events"] = []

    if room_version == RoomVersions.V1:
        # V1 requires an event_id field, but later versions don't.
        defaults["event_id"] = "$test_event_id:matrix.org"

    return defaults


def make_test_event(
    event_dict: JsonDict | None = None,
    room_version: RoomVersion = RoomVersions.V1,
    internal_metadata_dict: JsonDict | None = None,
    rejected_reason: str | None = None,
    **fields: Unpack["_EventFields"],
) -> EventBase:
    """Build an `EventBase` with defaults for the strict-required fields.

    Pass an `event_dict` and/or `**fields` keyword arguments — both are
    merged on top of the format-version defaults from
    `default_event_fields`. Explicit values win over defaults, and
    `**fields` wins over `event_dict` so call sites can override a
    shared base dict with one-off tweaks.
    """
    merged: JsonDict = {
        **default_event_fields(room_version),
        **(event_dict or {}),
        **fields,
    }

    # For room versions where the create event's room_id is derived from its
    # event ID (v11+ format), omit the default room_id on create events so each
    # create event ends up with a distinct room_id.
    #
    # We can't do this in the `default_event_fields` as we don't know the event
    # type at that point.
    if (
        room_version.msc4291_room_ids_as_hashes
        and merged["type"] == "m.room.create"
        and merged["state_key"] == ""
    ):
        merged.pop("room_id", None)

    return make_event_from_dict(
        merged,
        room_version=room_version,
        internal_metadata_dict=internal_metadata_dict,
        rejected_reason=rejected_reason,
    )


def make_test_pdu_event(
    pdu: JsonDict,
    room_version: RoomVersion,
    received_time: int | None = None,
) -> EventBase:
    """Wrapper around `event_from_pdu_json` for test PDU dicts.

    Federation-side test fixtures often omit fields the strict Rust ctor
    requires (e.g. `hashes`, `auth_events`, `prev_events`, `depth`)
    because those tests focus on transport/auth flow rather than event
    well-formedness. This helper layers in the same format-version
    defaults as `make_test_event` before delegating.
    """
    pdu = {**default_event_fields(room_version), **pdu}
    return event_from_pdu_json(pdu, room_version, received_time=received_time)


class _EventFields(TypedDict):
    """Type for `kwargs` in `make_test_event`."""

    event_id: NotRequired[str]
    type: NotRequired[str]
    sender: NotRequired[str]
    content: NotRequired[JsonDict]
    depth: NotRequired[int]
    origin_server_ts: NotRequired[int]
    hashes: NotRequired[dict[str, str]]
    auth_events: NotRequired[list[str]]
    prev_events: NotRequired[list[str]]
    prev_state_events: NotRequired[list[str]]
    room_id: NotRequired[str]
