import tempfile
from pathlib import Path

import pytest

from djino.registry import djinoRegistry
from djino.typegen.generator import TypeGenerator


@pytest.fixture
def generator():
    djinoRegistry.autodiscover()
    return TypeGenerator(djinoRegistry.get_models(), djinoRegistry)


class TestTypeGenerator:
    def test_generate_returns_files(self, generator):
        files = generator.generate()
        assert "index.ts" in files
        assert "models/Note.ts" in files
        assert "models/Tag.ts" in files

    def test_model_file_contains_interface(self, generator):
        files = generator.generate()
        note_ts = files["models/Note.ts"]
        assert "export interface NoteData" in note_ts

    def test_model_file_contains_fields(self, generator):
        files = generator.generate()
        note_ts = files["models/Note.ts"]
        assert "export const NoteFields" in note_ts

    def test_model_file_contains_class(self, generator):
        files = generator.generate()
        note_ts = files["models/Note.ts"]
        assert "export class Note extends Model<NoteData>" in note_ts

    def test_choice_type_generated(self, generator):
        files = generator.generate()
        note_ts = files["models/Note.ts"]
        assert "NoteStatus" in note_ts
        assert "'draft'" in note_ts
        assert "'published'" in note_ts
        assert "'archived'" in note_ts

    def test_foreign_key_field(self, generator):
        files = generator.generate()
        note_ts = files["models/Note.ts"]
        assert "ForeignKeyField" in note_ts
        assert "author_id" in note_ts

    def test_sync_meta_in_model(self, generator):
        files = generator.generate()
        tag_ts = files["models/Tag.ts"]
        assert "syncScope: 'global'" in tag_ts

        note_ts = files["models/Note.ts"]
        assert "syncScope: 'user'" in note_ts
        assert "ownerField:" in note_ts

    def test_barrel_export(self, generator):
        files = generator.generate()
        index_ts = files["index.ts"]
        assert "Note" in index_ts
        assert "Tag" in index_ts
        assert "NoteData" in index_ts

    def test_write_creates_files(self, generator):
        files = generator.generate()
        with tempfile.TemporaryDirectory() as tmpdir:
            written = generator.write(tmpdir, files)
            assert len(written) > 0
            for filepath in written:
                assert (Path(tmpdir) / filepath).exists()

    def test_check_for_changes_detects_missing(self, generator):
        files = generator.generate()
        with tempfile.TemporaryDirectory() as tmpdir:
            assert generator.check_for_changes(tmpdir, files) is True

    def test_check_for_changes_detects_no_change(self, generator):
        files = generator.generate()
        with tempfile.TemporaryDirectory() as tmpdir:
            generator.write(tmpdir, files)
            assert generator.check_for_changes(tmpdir, files) is False

    def test_check_for_changes_detects_stale(self, generator):
        files = generator.generate()
        with tempfile.TemporaryDirectory() as tmpdir:
            generator.write(tmpdir, files)
            # Modify a file
            path = Path(tmpdir) / "index.ts"
            path.write_text("// modified")
            assert generator.check_for_changes(tmpdir, files) is True

    def test_output_is_deterministic(self, generator):
        files1 = generator.generate()
        files2 = generator.generate()
        assert files1 == files2

    def test_models_filter(self):
        djinoRegistry.autodiscover()
        registry = {k: v for k, v in djinoRegistry.get_models().items() if k == "testapp.Note"}
        generator = TypeGenerator(registry, djinoRegistry)
        files = generator.generate()
        assert "models/Note.ts" in files
        assert "models/Tag.ts" not in files


class TestGenerateTypesCommand:
    def test_dry_run(self):
        from io import StringIO
        from django.core.management import call_command

        djinoRegistry.autodiscover()
        out = StringIO()
        call_command("generate_types", "--dry-run", stdout=out)
        output = out.getvalue()
        assert "NoteData" in output

    def test_output_flag(self):
        from django.core.management import call_command

        djinoRegistry.autodiscover()
        with tempfile.TemporaryDirectory() as tmpdir:
            call_command("generate_types", "--output", tmpdir)
            assert (Path(tmpdir) / "index.ts").exists()
            assert (Path(tmpdir) / "models" / "Note.ts").exists()

    def test_check_flag_passes(self):
        from django.core.management import call_command

        djinoRegistry.autodiscover()
        with tempfile.TemporaryDirectory() as tmpdir:
            call_command("generate_types", "--output", tmpdir)
            # Should not raise
            call_command("generate_types", "--output", tmpdir, "--check")

    def test_check_flag_fails(self):
        from django.core.management import call_command

        djinoRegistry.autodiscover()
        with tempfile.TemporaryDirectory() as tmpdir:
            with pytest.raises(SystemExit):
                call_command("generate_types", "--output", tmpdir, "--check")

    def test_models_filter_flag(self):
        from django.core.management import call_command

        djinoRegistry.autodiscover()
        with tempfile.TemporaryDirectory() as tmpdir:
            call_command("generate_types", "--output", tmpdir, "--models", "testapp.Note")
            assert (Path(tmpdir) / "models" / "Note.ts").exists()
            assert not (Path(tmpdir) / "models" / "Tag.ts").exists()
