"""Tests for arkiv CLI."""

import json
import subprocess
import sys
import pytest


def run_arkiv(*args):
    result = subprocess.run(
        [sys.executable, "-m", "arkiv.cli", *args],
        capture_output=True,
        text=True,
    )
    return result


class TestCLI:
    def test_version(self):
        result = run_arkiv("--version")
        assert "0.1.0" in result.stdout

    def test_help(self):
        result = run_arkiv("--help")
        assert "import" in result.stdout
        assert "export" in result.stdout
        assert "detect" in result.stdout
        assert "mcp" in result.stdout

    def test_import_jsonl(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "hello"}\n')
        db_path = tmp_path / "test.db"
        result = run_arkiv("import", str(f), "--db", str(db_path))
        assert result.returncode == 0
        assert db_path.exists()

    def test_query(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "hello"}\n')
        db_path = tmp_path / "test.db"
        run_arkiv("import", str(f), "--db", str(db_path))
        result = run_arkiv(
            "query", str(db_path), "SELECT content FROM records"
        )
        assert result.returncode == 0
        assert "hello" in result.stdout

    def test_schema(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"metadata": {"role": "user"}}\n')
        result = run_arkiv("schema", str(f))
        assert result.returncode == 0
        assert "role" in result.stdout

    def test_schema_from_db(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"metadata": {"role": "user"}}\n')
        db_path = tmp_path / "test.db"
        run_arkiv("import", str(f), "--db", str(db_path))
        result = run_arkiv("schema", str(db_path))
        assert result.returncode == 0
        assert "role" in result.stdout

    def test_info(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "a"}\n{"content": "b"}\n')
        db_path = tmp_path / "test.db"
        run_arkiv("import", str(f), "--db", str(db_path))
        result = run_arkiv("info", str(db_path))
        assert result.returncode == 0
        assert "2" in result.stdout

    def test_import_db_file_rejected(self, tmp_path):
        db_path = tmp_path / "test.db"
        db_path.write_bytes(b"")
        result = run_arkiv("import", str(db_path))
        assert result.returncode == 1
        assert "database file" in result.stderr.lower()

    def test_query_nonexistent_db(self, tmp_path):
        db_path = tmp_path / "nope.db"
        result = run_arkiv("query", str(db_path), "SELECT 1")
        assert result.returncode == 1
        assert "Error" in result.stderr
        assert not db_path.exists()

    def test_info_nonexistent_db(self, tmp_path):
        db_path = tmp_path / "nope.db"
        result = run_arkiv("info", str(db_path))
        assert result.returncode == 1
        assert "Error" in result.stderr

    def test_schema_nonexistent_db(self, tmp_path):
        db_path = tmp_path / "nope.db"
        result = run_arkiv("schema", str(db_path))
        assert result.returncode == 1
        assert "Error" in result.stderr

    def test_double_import_no_duplicates(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "hello"}\n')
        db_path = tmp_path / "test.db"
        run_arkiv("import", str(f), "--db", str(db_path))
        run_arkiv("import", str(f), "--db", str(db_path))
        result = run_arkiv(
            "query", str(db_path), "SELECT COUNT(*) as cnt FROM records"
        )
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert data[0]["cnt"] == 1

    # --- info on JSONL ---

    def test_info_jsonl(self, tmp_path):
        f = tmp_path / "conversations.jsonl"
        f.write_text('{"content": "a", "metadata": {"role": "user"}}\n{"content": "b"}\n')
        result = run_arkiv("info", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert data["total_records"] == 2
        assert "conversations" in data["collections"]

    def test_info_jsonl_shows_schema_keys(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"metadata": {"role": "user"}}\n{"metadata": {"role": "assistant"}}\n')
        result = run_arkiv("info", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert "role" in data["collections"]["test"].get("metadata_keys", {})

    # --- helpful errors for JSONL on DB-only commands ---

    def test_query_jsonl_suggests_import(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "hello"}\n')
        result = run_arkiv("query", str(f), "SELECT content FROM records")
        assert result.returncode == 1
        assert "import" in result.stderr.lower()

    def test_export_jsonl_suggests_import(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "hello"}\n')
        result = run_arkiv("export", str(f))
        assert result.returncode == 1
        assert "import" in result.stderr.lower()

    # --- detect command ---

    def test_detect_valid_jsonl(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text(
            '{"content": "hello", "mimetype": "text/plain", "metadata": {"role": "user"}}\n'
            '{"content": "world"}\n'
        )
        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert data["valid_jsonl"] is True
        assert data["total_records"] == 2

    def test_detect_shows_fields_and_keys(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "hi", "metadata": {"lang": "en"}}\n')
        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert "content" in data["fields_used"]
        assert "lang" in data["metadata_keys"]

    def test_detect_warns_on_invalid_lines(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "ok"}\nnot json\n{"content": "fine"}\n')
        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert data["valid_jsonl"] is False  # parse errors make it invalid
        assert data["total_records"] == 2
        assert len(data["warnings"]) > 0

    def test_detect_warns_unknown_fields(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "hi", "url": "https://example.com"}\n')
        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert data["valid_jsonl"] is True  # unknown fields are warnings, not errors
        assert "url" in data["unknown_fields"]
        assert any("uri" in w for w in data["warnings"])

    def test_detect_empty_file(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text("")
        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert data["total_records"] == 0

    # --- detect --strict ---

    def test_detect_strict_exits_1_on_warnings(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"url": "https://example.com"}\n')
        result = run_arkiv("detect", "--strict", str(f))
        assert result.returncode == 1

    def test_detect_strict_exits_0_when_clean(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"uri": "https://example.com", "content": "hi"}\n')
        result = run_arkiv("detect", "--strict", str(f))
        assert result.returncode == 0

    # --- detect --fix ---

    def test_fix_duplicates_url_to_uri(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"url": "https://example.com", "content": "hi"}\n')
        result = run_arkiv("fix", str(f))
        assert result.returncode == 0
        assert "fixed" in result.stdout.lower() or "uri" in result.stdout.lower()
        # Verify the file was rewritten with uri added, url preserved
        import json as json_mod
        line = f.read_text().strip()
        obj = json_mod.loads(line)
        assert obj["uri"] == "https://example.com"
        assert obj["url"] == "https://example.com"

    def test_fix_skips_if_target_exists(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"url": "old", "uri": "correct", "content": "hi"}\n')
        result = run_arkiv("fix", str(f))
        assert result.returncode == 0
        import json as json_mod
        obj = json_mod.loads(f.read_text().strip())
        assert obj["uri"] == "correct"
        assert obj["url"] == "old"

    def test_fix_idempotent(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"url": "https://example.com"}\n')
        run_arkiv("fix", str(f))
        content_after_first = f.read_text()
        run_arkiv("fix", str(f))
        content_after_second = f.read_text()
        assert content_after_first == content_after_second

    # --- mcp subcommand ---

    def test_mcp_in_help(self):
        result = run_arkiv("--help")
        assert "mcp" in result.stdout

    def test_export(self, tmp_path):
        f = tmp_path / "test.jsonl"
        f.write_text('{"content": "hello"}\n')
        db_path = tmp_path / "test.db"
        run_arkiv("import", str(f), "--db", str(db_path))
        out = tmp_path / "exported"
        result = run_arkiv("export", str(db_path), "--output", str(out))
        assert result.returncode == 0
        assert (out / "test.jsonl").exists()
        assert (out / "README.md").exists()
        assert (out / "schema.yaml").exists()

    def test_import_readme(self, tmp_path):
        (tmp_path / "data.jsonl").write_text('{"content": "hello"}\n')
        readme_text = (
            "---\nname: Test\ncontents:\n- path: data.jsonl\n---\n# Test\n"
        )
        (tmp_path / "README.md").write_text(readme_text)
        db_path = tmp_path / "test.db"
        result = run_arkiv(
            "import", str(tmp_path / "README.md"), "--db", str(db_path)
        )
        assert result.returncode == 0
        assert "1" in result.stdout

    def test_import_directory(self, tmp_path):
        archive_dir = tmp_path / "archive"
        archive_dir.mkdir()
        (archive_dir / "data.jsonl").write_text('{"content": "hello"}\n')
        readme_text = (
            "---\nname: Test\ncontents:\n- path: data.jsonl\n---\n# Test\n"
        )
        (archive_dir / "README.md").write_text(readme_text)
        db_path = tmp_path / "test.db"
        result = run_arkiv(
            "import", str(archive_dir), "--db", str(db_path)
        )
        assert result.returncode == 0
        assert "1" in result.stdout

    def test_import_empty_directory(self, tmp_path):
        archive_dir = tmp_path / "archive"
        archive_dir.mkdir()
        db_path = tmp_path / "test.db"
        result = run_arkiv(
            "import", str(archive_dir), "--db", str(db_path)
        )
        assert result.returncode == 1
        assert "No README.md" in result.stderr

    def test_export_preserves_readme_metadata(self, tmp_path):
        (tmp_path / "data.jsonl").write_text('{"content": "hello"}\n')
        readme_text = (
            "---\nname: CLI test archive\ndescription: A test\n"
            "contents:\n- path: data.jsonl\n  description: Test data\n---\n"
            "# CLI test archive\n"
        )
        (tmp_path / "README.md").write_text(readme_text)
        db_path = tmp_path / "test.db"
        run_arkiv("import", str(tmp_path / "README.md"), "--db", str(db_path))
        out = tmp_path / "exported"
        result = run_arkiv("export", str(db_path), "--output", str(out))
        assert result.returncode == 0

        import yaml
        readme_text = (out / "README.md").read_text()
        # Parse frontmatter
        from arkiv.readme import parse_readme
        exported = parse_readme(out / "README.md")
        assert exported.frontmatter["name"] == "CLI test archive"
        assert exported.frontmatter["description"] == "A test"
        coll_by_path = {
            c["path"]: c for c in exported.frontmatter.get("contents", [])
        }
        assert coll_by_path["data.jsonl"]["description"] == "Test data"

    # --- detect with schema.yaml ---

    def test_detect_with_schema_warns_missing_key(self, tmp_path):
        """Key in schema.yaml but not in data → warning."""
        f = tmp_path / "test.jsonl"
        f.write_text('{"metadata": {"role": "user"}}\n')
        schema_yaml = (
            "test:\n  record_count: 1\n  metadata_keys:\n"
            "    role:\n      type: string\n      count: 1\n"
            "    old_key:\n      type: string\n      count: 5\n"
            "      description: Deprecated\n"
        )
        (tmp_path / "schema.yaml").write_text(schema_yaml)

        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert any("old_key" in w and "not found" in w for w in data["warnings"])

    def test_detect_with_schema_info_undocumented(self, tmp_path):
        """Key in data but not in schema.yaml → info."""
        f = tmp_path / "test.jsonl"
        f.write_text('{"metadata": {"role": "user", "extra": "val"}}\n')
        schema_yaml = (
            "test:\n  record_count: 1\n  metadata_keys:\n"
            "    role:\n      type: string\n      count: 1\n"
        )
        (tmp_path / "schema.yaml").write_text(schema_yaml)

        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert any("extra" in i for i in data.get("schema_info", []))

    def test_detect_with_schema_type_mismatch(self, tmp_path):
        """Type mismatch between schema and data → warning."""
        f = tmp_path / "test.jsonl"
        f.write_text('{"metadata": {"count": 42}}\n')
        schema_yaml = (
            "test:\n  record_count: 1\n  metadata_keys:\n"
            "    count:\n      type: string\n      count: 1\n"
        )
        (tmp_path / "schema.yaml").write_text(schema_yaml)

        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert any("Type mismatch" in w for w in data["warnings"])

    def test_detect_strict_with_schema_warnings(self, tmp_path):
        """Schema warnings should trigger --strict exit 1."""
        f = tmp_path / "test.jsonl"
        f.write_text('{"metadata": {"role": "user"}}\n')
        schema_yaml = (
            "test:\n  record_count: 1\n  metadata_keys:\n"
            "    role:\n      type: string\n      count: 1\n"
            "    missing:\n      type: string\n      count: 5\n"
        )
        (tmp_path / "schema.yaml").write_text(schema_yaml)

        result = run_arkiv("detect", "--strict", str(f))
        assert result.returncode == 1

    def test_detect_without_schema_unchanged(self, tmp_path):
        """Without schema.yaml, no schema_info field."""
        f = tmp_path / "test.jsonl"
        f.write_text('{"metadata": {"role": "user"}}\n')
        result = run_arkiv("detect", str(f))
        assert result.returncode == 0
        data = json.loads(result.stdout)
        assert "schema_info" not in data
