# tests/stubs/single_choice_question.py
from dataclasses import dataclass, field
import pytest
import platform
from pathlib import Path
from datetime import datetime
from typing import List
from flexoentity import FlexOID, FlexoEntity, EntityType, EntityState, Domain, get_signing_backend, CertificateReference


@pytest.fixture
def fixed_datetime(monkeypatch):
    class FixedDate(datetime):
        @classmethod
        def now(cls, tz=None):
            return datetime(2025, 11, 1, tzinfo=tz)
    monkeypatch.setattr("flexoentity.id_factory.datetime", FixedDate)
    return FixedDate

@dataclass
class AnswerOption:
    id: str
    text: str
    points: float = 0.0

    def to_dict(self):
        return {"id": self.id, "text": self.text, "points": self.points}

    @classmethod
    def from_dict(cls, data):
        return cls(
            id=data.get("id", ""),
            text=data.get("text", ""),
            points=data.get("points", 0.0)
        )


@dataclass
class SingleChoiceQuestion(FlexoEntity):
    """A minimal stub to test FlexoEntity integration."""
    ENTITY_TYPE = EntityType.ITEM

    text: str = ""
    options: List[AnswerOption] = field(default_factory=list)

    def __post_init__(self):
        # If no FlexOID yet, generate a draft ID now.
        if not getattr(self, "flexo_id", None):
            self.flexo_id = FlexOID.safe_generate(
                domain_id=self.domain_id,
                entity_type=SingleChoiceQuestion.ENTITY_TYPE.value,     # 'I'
                state=EntityState.DRAFT.value,        # 'D'
                text=self.text_seed or self.text,
                version=1,
            )

    @classmethod
    def default(cls):
        return cls()

    def to_dict(self):
        base = super().to_dict()
        base.update({
            "text": self.text,
            "options": [opt.to_dict() for opt in self.options],
        })
        return base

    @property
    def text_seed(self) -> str:
        """Include answer options (and points) for deterministic ID generation."""

        joined = "|".join(
            f"{opt.text.strip()}:{opt.points}"
            for opt in sorted(self.options, key=lambda o: o.text.strip().lower())
        )
        return f"{self.text}{joined}"

    @classmethod
    def from_dict(cls, data):
        obj = cls(text=data.get("text", ""),
            options=[AnswerOption.from_dict(o) for o in data.get("options", [])],
        )
        # restore FlexoEntity core fields
        if "flexo_id" in data:
            obj.flexo_id = FlexOID.to_dict(data["flexo_id"])
        return obj

@pytest.fixture
def sample_domain():
    domain_id = "PY_ARITHM"
    return Domain.with_domain_id(domain_id=domain_id,
                                 fullname="PYTHON_ARITHMETIC",
                                 description="ALL ABOUT ARITHMETIC IN PYTHON")

@pytest.fixture
def sample_question(sample_domain):
    q = SingleChoiceQuestion.with_domain_id(domain_id=sample_domain.domain_id,
                                            text="What is 2 + 2?",
                                            options=[])
    q._update_fingerprint()
    return q

SYSTEM = platform.system()


# ─────────────────────────────────────────────────────────────
# Basic test data directory + PEM test files
# ─────────────────────────────────────────────────────────────

@pytest.fixture(scope="session")
def test_data_dir():
    return Path(__file__).parent / "data"


@pytest.fixture(scope="session")
def test_cert(test_data_dir):
    return test_data_dir / "testcert.pem"


@pytest.fixture(scope="session")
def test_key(test_data_dir):
    return test_data_dir / "testkey.pem"


# ─────────────────────────────────────────────────────────────
# CertificateReference fixtures for each platform
# ─────────────────────────────────────────────────────────────

@pytest.fixture(scope="session")
def cert_ref_linux(test_cert, test_key):
    """Linux: Uses OpenSSL CMS with PEM cert + PEM private key."""
    return CertificateReference(
        platform="LINUX",
        identifier=str(test_cert),
        private_key_path=str(test_key),
        public_cert_path=str(test_cert),
    )


@pytest.fixture(scope="session")
def cert_ref_macos(test_cert):
    """
    macOS: Uses Keychain identity with Common Name (CN).
    The test cert must be imported into the login keychain with CN=FlexOSignerTest.
    """
    return CertificateReference(
        platform="MACOS",
        identifier="FlexOSignerTest",
        public_cert_path=str(test_cert),
    )

@pytest.fixture(scope="session")
def backend(test_cert, test_key):
    """Return the correct backend for the current platform."""

    if SYSTEM == "Linux":
        cert_ref = CertificateReference(
            platform="LINUX",
            identifier=str(test_cert),
            private_key_path=str(test_key),
            public_cert_path=str(test_cert),
        )

    elif SYSTEM == "Darwin":
        cert_ref = CertificateReference(
            platform="MACOS",
            identifier="FlexOSignerTest",
            public_cert_path=str(test_cert),
        )

    elif SYSTEM == "Windows":
        pytest.skip("Windows signing tests not implemented yet")

    else:
        pytest.skip(f"Unsupported platform: {SYSTEM}")

    try:
        backend = get_signing_backend(cert_ref)
        # sanity check: ensures cert exists and command is available
        _ = backend.certificate_thumbprint
        return backend
    except Exception as e:
        pytest.skip(f"Backend unavailable or misconfigured: {e}")
