#!/usr/bin/env python3
"""Fictional Freshdesk merge observations and independent ERP return state.

No HTTP, merge request, ticket mutation, message, ERP operation or persistent
event store. Supplied observations are a local format, not native API payloads.
A documented 204 is treated as a successful controlled merge response, without
inventing a response body or treating it as permission to resolve an ERP return.
"""
from dataclasses import dataclass, replace
from itertools import combinations, permutations
import json
import sys


KINDS = ("intent", "response", "primary_read", "secondary_read")
DEFAULT_STATUSES = (2, 3, 4, 5)


@dataclass(frozen=True)
class Scope:
    account: str
    operation: str


@dataclass(frozen=True)
class Event:
    event_id: str
    scope: Scope
    kind: str
    values: tuple


@dataclass(frozen=True)
class ReturnState:
    reference: str
    state: str


def text(value):
    return isinstance(value, str) and bool(value.strip())


def positive_id(value):
    return type(value) is int and value > 0


def typed_values(values):
    # Python considers True == 1; observation identity must preserve the type.
    return tuple((type(value).__name__, value) for value in values)


def held(reason, business):
    return {"support_merge": "held", "reason": reason,
            "erp_return": business.reference, "erp_state": business.state,
            "erp_resolution_authorized_by_support": False}


class Journal:
    """One explicitly scoped observation window, entirely in Python memory."""

    def __init__(self, scope, business):
        if (not text(scope.account) or not text(scope.operation)
                or not text(business.reference) or not text(business.state)):
            raise ValueError("invalid_local_scope")
        self.scope = scope
        self.business = business
        self.events = {}
        self.conflicts = set()

    def receive(self, event):
        if event.scope != self.scope:
            return "held_scope"
        if (not text(event.event_id) or event.kind not in KINDS
                or not isinstance(event.values, tuple)):
            return "held_shape"
        # Only immutable primitive tuples enter this deliberately small model.
        if any(type(v) not in (str, int, bool, type(None)) for v in event.values):
            return "held_shape"
        previous = self.events.get(event.event_id)
        if previous is not None:
            if (previous.kind != event.kind
                    or typed_values(previous.values) != typed_values(event.values)):
                self.conflicts.add(event.event_id)
                return "held_conflict"
            return "duplicate"
        self.events[event.event_id] = event
        return "recorded"

    def project(self):
        business = self.business
        if self.conflicts:
            return held("conflicting_event_identity", business)
        selected = {}
        for kind in KINDS:
            values = {typed_values(e.values): e.values
                      for e in self.events.values() if e.kind == kind}
            if not values:
                return held("missing_" + kind, business)
            if len(values) != 1:
                return held("multiple_" + kind + "_observations", business)
            selected[kind] = next(iter(values.values()))
        intent = selected["intent"]
        response = selected["response"]
        primary = selected["primary_read"]
        secondary = selected["secondary_read"]
        if not (len(intent) == 5 and len(response) == 4
                and len(primary) == len(secondary) == 3):
            return held("observation_shape", business)
        p_id, s_id, p_return, s_return, qualified_mapping = intent
        if not (positive_id(p_id) and positive_id(s_id) and p_id != s_id):
            return held("ticket_identity", business)
        if (qualified_mapping is not True or p_return != business.reference
                or s_return != business.reference):
            return held("return_mapping_requires_review", business)
        rp, rs, status, qualified_request = response
        if qualified_request is not True or (rp, rs) != (p_id, s_id):
            return held("response_not_bound_to_known_request", business)
        if type(status) is not int or status != 204:
            return held("merge_outcome_not_confirmed_by_local_profile", business)
        if (primary[0] != p_id or secondary[0] != s_id
                or not positive_id(primary[0]) or not positive_id(secondary[0])):
            return held("readback_ticket_identity", business)
        if primary[2] is not True or secondary[2] is not True:
            return held("readback_unqualified", business)
        if (type(primary[1]) is not int or type(secondary[1]) is not int
                or primary[1] not in DEFAULT_STATUSES
                or secondary[1] not in DEFAULT_STATUSES):
            return held("status_outside_local_default_profile", business)
        if secondary[1] != 5:
            return held("secondary_closure_not_observed", business)
        return {"support_merge": "observed_controlled_merge",
                "primary_ticket": p_id, "secondary_ticket": s_id,
                "primary_status": primary[1], "secondary_status": secondary[1],
                "erp_return": business.reference, "erp_state": business.state,
                "erp_resolution_authorized_by_support": False,
                "transcript_completeness_certified": False,
                "customer_notification_absence_certified": False}


def naive_closed_projection(erp_record, event):
    """Intentionally wrong mutation of a fictional in-memory ERP dictionary."""
    if event.kind == "secondary_read" and len(event.values) == 3 and event.values[1] == 5:
        erp_record["state"] = "closed"


def run_checks():
    names = []

    def check(name, condition):
        if name in names or not condition:
            raise AssertionError(name)
        names.append(name)

    scope = Scope("SUPPORT-A", "LOCAL-MERGE-01")
    business = ReturnState("RMA-A", "awaiting_physical_return")
    events = (
        Event("intent-1", scope, "intent", (100, 101, "RMA-A", "RMA-A", True)),
        Event("response-1", scope, "response", (100, 101, 204, True)),
        Event("primary-1", scope, "primary_read", (100, 2, True)),
        Event("secondary-1", scope, "secondary_read", (101, 5, True)),
    )

    def loaded(supplied=events, target=scope, return_state=business):
        journal = Journal(target, return_state)
        for event in supplied:
            journal.receive(event)
        return journal

    good = loaded()
    expected = good.project()
    check("controlled_merge_observed", expected["support_merge"] == "observed_controlled_merge")
    check("return_remains_unresolved", expected["erp_state"] == "awaiting_physical_return")
    check("support_does_not_authorize_return_resolution", expected["erp_resolution_authorized_by_support"] is False)
    bad = {"reference": "RMA-A", "state": business.state}
    naive_closed_projection(bad, events[-1])
    check("actual_bad_dictionary_mutation_closes_unresolved_return", bad["state"] == "closed")
    check("no_transcript_certification_in_output", expected["transcript_completeness_certified"] is False)
    check("no_notification_absence_certification_in_output", expected["customer_notification_absence_certified"] is False)

    order_count = 0
    for number, ordering in enumerate(permutations(events), 1):
        journal = Journal(scope, business)
        prefixes = []
        for event in ordering:
            journal.receive(event)
            prefixes.append(journal.project())
        check("arrival_order_"+str(number),
              journal.project() == expected
              and all(p["erp_state"] == business.state for p in prefixes)
              and all(p["support_merge"] == "held" for p in prefixes[:-1]))
        order_count += 1

    subset_count = 0
    for size in range(5):
        for subset in combinations(events, size):
            result = loaded(subset).project()
            check("observation_subset_"+str(subset_count),
                  result["erp_state"] == business.state
                  and (result["support_merge"] == "observed_controlled_merge") == (size == 4))
            subset_count += 1

    before = dict(good.events)
    check("same_event_is_duplicate", good.receive(events[-1]) == "duplicate")
    check("replay_keeps_observations_and_projection", good.events == before and good.project() == expected)
    modified = replace(events[-1], values=(101, 4, True))
    check("same_event_id_different_observation_is_conflict", good.receive(modified) == "held_conflict")
    check("conflict_does_not_overwrite_prior_observation", good.events == before)
    check("conflict_holds_projection", good.project()["reason"] == "conflicting_event_identity")
    check("conflict_never_resolves_return", good.project()["erp_state"] == business.state)
    typed = loaded()
    numeric_qualification = replace(events[1], values=(100, 101, 204, 1))
    check("boolean_to_integer_is_not_identical_replay", typed.receive(numeric_qualification) == "held_conflict")
    check("typed_conflict_keeps_original_boolean", typed.events["response-1"].values[-1] is True)
    numeric_other_id = replace(numeric_qualification, event_id="response-2")
    for number, response_order in enumerate(permutations((events[1], numeric_other_id)), 1):
        typed_journal = loaded((events[0], events[2], events[3]) + response_order)
        check("typed_values_conflict_in_both_orders_"+str(number),
              typed_journal.project()["reason"] == "multiple_response_observations"
              and typed_journal.project()["erp_state"] == business.state)

    def variant(index, values, reason):
        supplied = list(events)
        supplied[index] = replace(supplied[index], values=values)
        result = loaded(supplied).project()
        return result["reason"] == reason and result["erp_state"] == business.state

    check("different_business_returns_held", variant(0, (100, 101, "RMA-A", "RMA-B", True), "return_mapping_requires_review"))
    check("unqualified_mapping_held", variant(0, (100, 101, "RMA-A", "RMA-A", False), "return_mapping_requires_review"))
    check("same_ticket_twice_held", variant(0, (100, 100, "RMA-A", "RMA-A", True), "ticket_identity"))
    check("boolean_ticket_id_held", variant(0, (True, 101, "RMA-A", "RMA-A", True), "ticket_identity"))
    check("reversed_request_binding_held", variant(1, (101, 100, 204, True), "response_not_bound_to_known_request"))
    check("unqualified_request_held", variant(1, (100, 101, 204, False), "response_not_bound_to_known_request"))
    check("timeout_outcome_held_without_retry_command", variant(1, (100, 101, None, True), "merge_outcome_not_confirmed_by_local_profile"))
    check("different_success_profile_not_assumed_equivalent", variant(1, (100, 101, 200, True), "merge_outcome_not_confirmed_by_local_profile"))
    check("wrong_secondary_readback_held", variant(3, (999, 5, True), "readback_ticket_identity"))
    check("unqualified_primary_readback_held", variant(2, (100, 2, False), "readback_unqualified"))
    check("custom_status_requires_separate_profile", variant(2, (100, 6, True), "status_outside_local_default_profile"))
    check("secondary_still_open_held", variant(3, (101, 2, True), "secondary_closure_not_observed"))
    check("short_intent_held", variant(0, (100, 101), "observation_shape"))
    closed_primary = list(events)
    closed_primary[2] = replace(events[2], values=(100, 5, True))
    check("closed_primary_still_does_not_resolve_return", loaded(closed_primary).project()["erp_state"] == business.state)

    isolated = loaded()
    prior = dict(isolated.events)
    check("other_account_event_held", isolated.receive(replace(events[-1], scope=replace(scope, account="SUPPORT-B"))) == "held_scope")
    check("other_operation_event_held", isolated.receive(replace(events[-1], scope=replace(scope, operation="OTHER"))) == "held_scope")
    check("other_scope_cannot_change_journal", isolated.events == prior and isolated.project() == expected)
    check("mutable_payload_held", isolated.receive(replace(events[-1], event_id="mutable", values=[101, 5, True])) == "held_shape")
    check("unknown_event_kind_held", isolated.receive(replace(events[-1], event_id="unknown", kind="merged_to_id")) == "held_shape")
    another = replace(events[-1], event_id="secondary-2", values=(101, 4, True))
    multiple = loaded(events + (another,))
    check("different_reads_need_qualification_not_latest_guess", multiple.project()["reason"] == "multiple_secondary_read_observations")
    known_closed = ReturnState("RMA-A", "closed_by_separate_business_decision")
    check("independent_business_state_is_preserved", loaded(return_state=known_closed).project()["erp_state"] == known_closed.state)
    only_closed = loaded((events[-1],)).project()
    check("closed_without_known_merge_does_not_invent_relation", only_closed["support_merge"] == "held" and only_closed["reason"] == "missing_intent")
    response_only = loaded((events[0], events[1])).project()
    check("204_success_has_no_invented_ticket_response_body", response_only["reason"] == "missing_primary_read")
    check("all_input_events_are_unchanged", events[-1].values == (101, 5, True) and events[0].values == (100, 101, "RMA-A", "RMA-A", True))

    return {"checks_passed": len(names), "check_names": names,
            "python_version": sys.version,
            "scope": {"http_calls": 0, "freshdesk_mutations": 0,
                      "erp_operations": 0, "customer_messages": 0,
                      "persistent_writes": 0, "native_payload_parser": False,
                      "native_merge_or_permission_certification": False,
                      "durable_distributed_journal_certification": False},
            "arrival_orders_executed": order_count,
            "observation_subsets_executed": subset_count,
            "examples": {"qualified_observations": expected,
                         "bad_local_projection": bad,
                         "closed_without_known_merge": only_closed,
                         "response_without_readbacks": response_only,
                         "event_conflict": good.project(),
                         "same_event_replay_observation_count": len(before)}}


if __name__ == "__main__":
    print(json.dumps(run_checks(), ensure_ascii=False, indent=2))
