"""Tests for exokit.core.manifest — manifest builder."""

from __future__ import annotations

from datetime import UTC, datetime

import exokit
from exokit.core.manifest import build_manifest
from exokit.core.models import DemoManifest, ScaffoldContext


class TestBuildManifest:
    """Tests for the ``build_manifest`` function."""

    def test_returns_demo_manifest(self, sample_context: ScaffoldContext) -> None:
        """build_manifest() returns a DemoManifest instance."""
        result = build_manifest(sample_context)
        assert isinstance(result, DemoManifest)

    def test_meta_company_and_industry(self, sample_context: ScaffoldContext) -> None:
        """Meta block contains correct company and industry from context."""
        result = build_manifest(sample_context)
        assert result.meta.company == "Acme Bank"
        assert result.meta.industry == "fsi"

    def test_meta_version_matches_package(self, sample_context: ScaffoldContext) -> None:
        """Meta exokit_version matches the package __version__."""
        result = build_manifest(sample_context)
        assert result.meta.exokit_version == exokit.__version__

    def test_meta_generated_at_is_recent_iso(self, sample_context: ScaffoldContext) -> None:
        """Meta generated_at is a valid ISO timestamp close to now."""
        before = datetime.now(tz=UTC)
        result = build_manifest(sample_context)
        after = datetime.now(tz=UTC)

        ts = datetime.fromisoformat(result.meta.generated_at)
        assert before <= ts <= after

    def test_five_waves(self, sample_context: ScaffoldContext) -> None:
        """Manifest contains exactly 5 waves."""
        result = build_manifest(sample_context)
        assert len(result.waves) == 5

    def test_wave_ids_sequential(self, sample_context: ScaffoldContext) -> None:
        """Wave IDs are 1 through 5 in order."""
        result = build_manifest(sample_context)
        assert [w.id for w in result.waves] == [1, 2, 3, 4, 5]

    def test_all_waves_pending(self, sample_context: ScaffoldContext) -> None:
        """All waves start with status 'pending'."""
        result = build_manifest(sample_context)
        assert all(w.status == "pending" for w in result.waves)

    def test_waves_have_names(self, sample_context: ScaffoldContext) -> None:
        """Every wave has a non-empty name."""
        result = build_manifest(sample_context)
        for wave in result.waves:
            assert wave.name, f"Wave {wave.id} has empty name"

    def test_waves_have_components(self, sample_context: ScaffoldContext) -> None:
        """Every wave has at least one component."""
        result = build_manifest(sample_context)
        for wave in result.waves:
            assert len(wave.components) > 0, f"Wave {wave.id} has no components"

    def test_waves_have_descriptions(self, sample_context: ScaffoldContext) -> None:
        """Every wave has a non-empty description."""
        result = build_manifest(sample_context)
        for wave in result.waves:
            assert wave.description, f"Wave {wave.id} has empty description"

    def test_wave_names_match_expected(self, sample_context: ScaffoldContext) -> None:
        """Wave names match the canonical 5-wave plan."""
        result = build_manifest(sample_context)
        expected_names = [
            "Data Foundation",
            "Silver & Gold Layers",
            "ML & AI",
            "Dashboards & Genie",
            "App & Integration",
        ]
        assert [w.name for w in result.waves] == expected_names

    def test_schemas_default_empty(self, sample_context: ScaffoldContext) -> None:
        """Schemas field defaults to an empty SchemaDefinition."""
        result = build_manifest(sample_context)
        assert result.schemas.tables == {}

    def test_manifest_serialises_to_json(self, sample_context: ScaffoldContext) -> None:
        """Manifest can be serialised to JSON and deserialised back."""
        result = build_manifest(sample_context)
        json_str = result.model_dump_json()
        roundtrip = DemoManifest.model_validate_json(json_str)
        assert roundtrip.meta.company == result.meta.company
        assert len(roundtrip.waves) == len(result.waves)

    def test_different_context_different_meta(self) -> None:
        """Different contexts produce different meta blocks."""
        ctx_a = ScaffoldContext(
            company="Alpha Corp",
            industry="healthcare",
            demo_description="Healthcare demo",
            catalog="alpha_hc",
            warehouse_id="wh-001",
            workspace_host="https://alpha.databricks.net",
            notification_email="admin@alpha.com",
            bundle_name="alpha-corp-healthcare-demo",
            app_name="alpha-corp-healthcare-app",
        )
        ctx_b = ScaffoldContext(
            company="Beta Inc",
            industry="retail",
            demo_description="Test demo description",
            catalog="beta_retail",
            warehouse_id="wh-002",
            workspace_host="https://beta.databricks.net",
            notification_email="admin@beta.com",
            bundle_name="beta-inc-retail-demo",
            app_name="beta-inc-retail-app",
        )
        manifest_a = build_manifest(ctx_a)
        manifest_b = build_manifest(ctx_b)

        assert manifest_a.meta.company != manifest_b.meta.company
        assert manifest_a.meta.industry != manifest_b.meta.industry
