"""JSON to JSON Schema builder component.

Allows users to paste a JSON example and automatically generates
a JSON schema using Genson. Shows real-time validation errors.
"""

import json
from typing import Any, cast

from genson import SchemaBuilder
from textual import on
from textual.app import ComposeResult
from textual.containers import Vertical
from textual.message import Message
from textual.widget import Widget
from textual.widgets import Static, TextArea


def generate_schema_from_json(json_str: str) -> dict[str, Any] | None:
    """Generate JSON schema from a JSON example string.

    Args:
        json_str: A JSON string representing an example object

    Returns:
        The generated JSON schema as a dict, or None if invalid JSON
    """
    if not json_str.strip():
        return None

    data = json.loads(json_str)
    builder: Any = SchemaBuilder()
    builder.add_object(data)
    return cast(dict[str, Any], builder.to_schema())


class JsonSchemaBuilder(Widget):
    """Widget that converts JSON examples to JSON Schema.

    Users paste a JSON object example, and this widget automatically
    generates the corresponding JSON schema using Genson.

    Usage:
        yield JsonSchemaBuilder(id="output-schema-builder")

        # Get the generated schema
        builder = self.query_one("#output-schema-builder", JsonSchemaBuilder)
        schema = builder.generated_schema  # dict or None

    Features:
        - Real-time JSON validation with error display
        - Auto-generates schema as user types
        - Shows preview of generated schema
    """

    class SchemaChanged(Message):
        """Posted when the generated schema changes."""

        def __init__(self, schema: dict[str, Any] | None) -> None:
            self.schema = schema
            super().__init__()

    def __init__(
        self,
        placeholder: str = '{"name": "John", "age": 30, "active": true}',
        **kwargs: Any,
    ) -> None:
        super().__init__(**kwargs)
        self._placeholder = placeholder
        self._generated_schema: dict[str, Any] | None = None

    @property
    def generated_schema(self) -> dict[str, Any] | None:
        """Get the generated JSON schema, or None if no valid input."""
        return self._generated_schema

    @property
    def json_input(self) -> str:
        """Get the raw JSON input text."""
        return self.query_one("#json-input", TextArea).text.strip()

    def compose(self) -> ComposeResult:
        with Vertical():
            yield TextArea(
                placeholder=self._placeholder,
                id="json-input",
                classes="json-input",
            )
            yield Static("", classes="error-label hidden", id="json-error")
            yield Static("Generated Schema:", classes="section-label")
            yield TextArea(
                "Paste a JSON example above to generate schema",
                read_only=True,
                id="schema-preview",
                classes="schema-preview",
            )

    @on(TextArea.Changed, "#json-input")
    def on_json_changed(self, event: TextArea.Changed) -> None:
        """Validate JSON and generate schema as user types."""
        self._update_schema(event.text_area.text)

    def _update_schema(self, json_text: str) -> None:
        """Update the generated schema from JSON input."""
        error_label = self.query_one("#json-error", Static)
        preview = self.query_one("#schema-preview", TextArea)

        json_text = json_text.strip()

        if not json_text:
            # Empty input
            self._generated_schema = None
            error_label.update("")
            error_label.add_class("hidden")
            preview.text = "Paste a JSON example above to generate schema"
            self.post_message(self.SchemaChanged(None))
            return

        try:
            schema = generate_schema_from_json(json_text)
            self._generated_schema = schema

            # Show success - display the schema
            error_label.update("")
            error_label.add_class("hidden")

            if schema:
                schema_str = json.dumps(schema, indent=2)
                preview.text = schema_str
            else:
                preview.text = "Empty schema generated"

            self.post_message(self.SchemaChanged(schema))

        except json.JSONDecodeError as e:
            # Show error
            self._generated_schema = None
            error_label.update(
                f"Invalid JSON: {e.msg} (line {e.lineno}, col {e.colno})"
            )
            error_label.remove_class("hidden")
            preview.text = "Fix JSON errors to generate schema"
            self.post_message(self.SchemaChanged(None))

    def clear(self) -> None:
        """Clear the input and generated schema."""
        self.query_one("#json-input", TextArea).clear()
        self._generated_schema = None
        self._update_schema("")
