import pytest
from flexoentity import FlexOID, FlexoEntity, EntityState


def test_initial_state(sample_domain):
    assert sample_domain.state == EntityState.DRAFT
    assert sample_domain.flexo_id.version == 1
    assert FlexoEntity.verify_integrity(sample_domain)

def test_approval_does_not_bump_version(sample_domain):
    q = sample_domain
    q.approve()
    assert q.state == EntityState.APPROVED
    assert q.flexo_id.version == 1

def test_signing_does_not_bump_version(sample_domain):
    q = sample_domain
    q.approve()
    before = q.flexo_id
    q.sign()
    after = q.flexo_id

    # state changed
    assert q.state == EntityState.APPROVED_AND_SIGNED

    # version unchanged
    assert before.version == after.version

    # only suffix letter differs
    assert before.prefix == after.prefix
    assert before.state_code == "A"
    assert after.state_code == "S"


def test_publish_does_not_bump_version(sample_domain):
    q = sample_domain
    q.approve()
    q.sign()
    v_before = q.flexo_id.version
    q.publish()
    assert q.state == EntityState.PUBLISHED
    assert q.flexo_id.version == v_before


def test_modify_content_changes_fingerprint(sample_signature):
    sample_signature.comment += "Rephrased content"  # simulate text change
    changed = sample_signature._update_fingerprint()
    assert changed


def test_no_version_bump_on_draft_edits(sample_signature):
    sample_signature.comment = "Minor draft edit"
    sample_signature._update_fingerprint()
    assert sample_signature.flexo_id.version == 1


def test_version_bump_after_edit_and_sign(sample_signature):
    sample_signature.approve()
    v1 = str(sample_signature.flexo_id)
    sample_signature.comment = "Changed comment"
    sample_signature.sign()
    assert str(sample_signature.flexo_id) != v1


def test_integrity_check_passes_and_fails(sample_signature):
    sample_signature.approve()
    assert FlexoEntity.verify_integrity(sample_signature)

    # simulate tampering
    sample_signature.comment = "Tampered text"
    assert not FlexoEntity.verify_integrity(sample_signature)


def test_obsolete_state(sample_domain):
    q = sample_domain
    q.approve()
    q.sign()
    q.publish()
    q.obsolete()
    assert q.state == EntityState.OBSOLETE


def test_clone_new_base_resets_lineage(sample_domain):
    q = sample_domain
    q.approve()
    q.sign()
    q.publish()
    q.obsolete()
    old_id = str(q.flexo_id)
    q.clone_new_base()
    assert str(q.flexo_id) != old_id
    assert q.state == EntityState.DRAFT
    assert q.flexo_id.version == 1

def test_clone_new_base_sets_origin(sample_domain):
    q = sample_domain
    q.approve()
    q.sign()
    q.publish()
    q.obsolete()
    old_id = str(q.flexo_id)
    q.clone_new_base()
    assert q.origin == old_id
    assert q.state == EntityState.DRAFT
    assert q.flexo_id.version == 1
    assert q.flexo_id != old_id

def test_mass_version_increments_until_obsolete(sample_domain):
    q = sample_domain
    q.approve()
    for _ in range(FlexOID.MAX_VERSION - 1):
        q.bump_version()

    # Next one must raise
    with pytest.raises(RuntimeError, match="mark obsolete"):
        q.bump_version()

def test_fork_creates_independent_domain(sample_domain):
    original = sample_domain

    forked = original.fork(domain_id="DST")

    # ─── identity ───────────────────────────────────────────────────────
    assert forked is not original
    assert isinstance(forked.flexo_id, FlexOID)
    assert forked.flexo_id != original.flexo_id

    # ─── lifecycle reset ────────────────────────────────────────────────
    assert forked.state == EntityState.DRAFT
    assert forked.version == 1

    # ─── provenance ─────────────────────────────────────────────────────
    assert forked.origin == str(original.flexo_id)

    # ─── domain reassigned ──────────────────────────────────────────────
    assert forked.domain_id == "DST"

def test_fork_does_not_mutate_original(sample_domain):
    original = sample_domain
    original_id = original.flexo_id
    original_fp = original.fingerprint

    _ = original.fork(domain_id="DST")

    assert original.flexo_id == original_id
    assert original.fingerprint == original_fp
    assert original.origin is None

def test_fork_does_not_shadow_state(sample_domain):
    forked = sample_domain.fork(domain_id="DST")

    # No instance attribute allowed
    assert "state" not in forked.__dict__

    # Property must still work
    assert forked.state == EntityState.DRAFT

def test_fork_without_origin(sample_domain):
    forked = sample_domain.fork(domain_id="DST", keep_origin=False)

    assert forked.origin is None
