"""Tests for the interactive forms engine."""

import json
from unittest.mock import patch, MagicMock

import pytest
from click.testing import CliRunner

from corpo.cli import cli
from corpo.forms.definitions import get_form_definition, list_form_types, form_definition_to_dict
from corpo.forms.validator import validate_field, validate_form_data, _validate_entity_name
from corpo.forms.schema import generate_json_schema
from corpo.forms.engine import FormEngine


# ---------------------------------------------------------------------------
# Definitions
# ---------------------------------------------------------------------------

class TestDefinitions:
    def test_list_form_types(self):
        types = list_form_types()
        assert "entity-formation" in types

    def test_get_form_definition(self):
        form = get_form_definition("entity-formation")
        assert form is not None
        assert form.form_id == "entity-formation"
        assert len(form.sections) == 4

    def test_get_unknown_form(self):
        assert get_form_definition("nonexistent") is None

    def test_entity_formation_fields(self):
        form = get_form_definition("entity-formation")
        field_ids = [f.id for s in form.sections for f in s.fields]
        assert "entity_name" in field_ids
        assert "entity_type" in field_ids
        assert "registered_agent" in field_ids
        assert "management_type" in field_ids
        assert "governance_platform" in field_ids
        assert "initial_token_supply" in field_ids
        assert "voting_threshold" in field_ids
        assert "donation_percentage" in field_ids

    def test_form_to_dict(self):
        form = get_form_definition("entity-formation")
        d = form_definition_to_dict(form)
        assert d["form_id"] == "entity-formation"
        assert len(d["sections"]) == 4
        assert d["sections"][0]["fields"][0]["id"] == "entity_name"


# ---------------------------------------------------------------------------
# Validator
# ---------------------------------------------------------------------------

class TestValidator:
    def test_valid_entity_name_lao_llc(self):
        assert _validate_entity_name("Alpine LAO LLC") is None

    def test_valid_entity_name_lao_llc_dots(self):
        assert _validate_entity_name("Alpine LAO L.L.C.") is None

    def test_invalid_entity_name_no_suffix(self):
        err = _validate_entity_name("Alpine")
        assert err is not None
        assert "LAO LLC" in err

    def test_invalid_entity_name_dao(self):
        err = _validate_entity_name("Alpine DAO LLC")
        assert err is not None
        assert "LAO" in err

    def test_invalid_entity_name_plain_llc(self):
        err = _validate_entity_name("Alpine LLC")
        assert err is not None
        assert "LAO LLC" in err

    def test_required_field_missing(self):
        form = get_form_definition("entity-formation")
        field = form.sections[0].fields[0]  # entity_name, required
        assert validate_field(field, "") is not None
        assert validate_field(field, None) is not None

    def test_integer_validation(self):
        form = get_form_definition("entity-formation")
        # Find initial_token_supply
        field = None
        for s in form.sections:
            for f in s.fields:
                if f.id == "initial_token_supply":
                    field = f
                    break
        assert field is not None
        assert validate_field(field, 1000000) is None
        assert validate_field(field, "abc") is not None
        assert validate_field(field, -1) is not None

    def test_percentage_validation(self):
        form = get_form_definition("entity-formation")
        field = None
        for s in form.sections:
            for f in s.fields:
                if f.id == "voting_threshold":
                    field = f
                    break
        assert validate_field(field, 50) is None
        assert validate_field(field, 0) is not None
        assert validate_field(field, 101) is not None

    def test_choice_validation(self):
        form = get_form_definition("entity-formation")
        field = form.sections[0].fields[1]  # entity_type
        assert validate_field(field, "dao_llc") is None
        assert validate_field(field, "invalid") is not None

    def test_validate_form_data_valid(self):
        form = get_form_definition("entity-formation")
        data = {
            "entity_name": "Test LAO LLC",
            "entity_type": "dao_llc",
            "initial_token_supply": 1000000,
            "voting_threshold": 50,
        }
        errors = validate_form_data(form, data)
        assert errors == []

    def test_validate_form_data_invalid(self):
        form = get_form_definition("entity-formation")
        data = {"entity_name": "Bad Name", "entity_type": "invalid"}
        errors = validate_form_data(form, data)
        assert len(errors) >= 2


# ---------------------------------------------------------------------------
# JSON Schema
# ---------------------------------------------------------------------------

class TestSchema:
    def test_generate_schema(self):
        form = get_form_definition("entity-formation")
        schema = generate_json_schema(form)
        assert schema["type"] == "object"
        assert "entity_name" in schema["properties"]
        assert "entity_name" in schema["required"]
        assert schema["properties"]["voting_threshold"]["maximum"] == 100
        assert schema["properties"]["entity_type"]["enum"] == ["dao_llc"]

    def test_schema_has_defaults(self):
        form = get_form_definition("entity-formation")
        schema = generate_json_schema(form)
        assert schema["properties"]["voting_threshold"]["default"] == 50
        assert schema["properties"]["initial_token_supply"]["default"] == 1000000


# ---------------------------------------------------------------------------
# Engine
# ---------------------------------------------------------------------------

class TestEngine:
    def test_engine_init(self):
        engine = FormEngine("entity-formation")
        assert engine.form is not None

    def test_engine_unknown_form(self):
        with pytest.raises(ValueError):
            FormEngine("nonexistent")

    def test_engine_validate_valid(self):
        engine = FormEngine()
        data = {
            "entity_name": "Test LAO LLC",
            "entity_type": "dao_llc",
            "initial_token_supply": 1000000,
            "voting_threshold": 50,
        }
        errors = engine.validate(data)
        assert errors == []

    def test_engine_validate_invalid(self):
        engine = FormEngine()
        errors = engine.validate({"entity_name": "Bad"})
        assert len(errors) > 0

    def test_engine_dump_schema(self):
        engine = FormEngine()
        schema = engine.dump_schema()
        assert schema["$schema"] == "https://json-schema.org/draft/2020-12/schema"

    def test_engine_submit_invalid(self):
        engine = FormEngine()
        result = engine.submit({"entity_name": "Bad"})
        assert result["valid"] is False
        assert len(result["errors"]) > 0

    def test_engine_submit_valid(self):
        mock_client = MagicMock()
        mock_client.create_quote.return_value = {"quote_id": "q-123", "name_available": True}

        engine = FormEngine()
        data = {
            "entity_name": "Test LAO LLC",
            "entity_type": "dao_llc",
            "initial_token_supply": 1000000,
            "voting_threshold": 50,
        }
        result = engine.submit(data, client=mock_client)
        assert result["valid"] is True
        assert result["quote"]["quote_id"] == "q-123"
        mock_client.create_quote.assert_called_once_with("Test LAO LLC", "dao_llc")


# ---------------------------------------------------------------------------
# CLI integration
# ---------------------------------------------------------------------------

class TestFormCLI:
    @pytest.fixture
    def runner(self):
        return CliRunner()

    def test_form_non_interactive(self, runner):
        result = runner.invoke(cli, ["form", "--non-interactive"])
        assert result.exit_code == 0
        schema = json.loads(result.output)
        assert schema["type"] == "object"
        assert "entity_name" in schema["properties"]

    def test_form_post_invalid_json(self, runner):
        result = runner.invoke(cli, ["form", "post", "not-json"])
        assert result.exit_code == 1

    def test_form_post_validation_errors(self, runner):
        result = runner.invoke(cli, ["form", "post", '{"entity_name": "Bad"}'])
        assert result.exit_code == 1
        out = json.loads(result.output)
        assert out["ok"] is False

    @patch("corpo.auth_config.load_auth_config", return_value={})
    @patch("corpo.auth_config.get_api_url", return_value="http://localhost")
    @patch("corpo.auth_config.get_api_key", return_value="test-key")
    @patch("corpo.client.CorpoClient")
    def test_form_post_valid(self, mock_client_cls, mock_key, mock_url, mock_cfg, runner):
        mock_client = MagicMock()
        mock_client.create_quote.return_value = {"quote_id": "q-123", "name_available": True}
        mock_client_cls.return_value = mock_client

        data = json.dumps({
            "entity_name": "Test LAO LLC",
            "entity_type": "dao_llc",
            "initial_token_supply": 1000000,
            "voting_threshold": 50,
        })
        result = runner.invoke(cli, ["form", "post", data])
        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["ok"] is True
        assert out["data"]["valid"] is True

    def test_form_faq(self, runner):
        result = runner.invoke(cli, ["form", "faq"])
        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["ok"] is True
        assert len(out["data"]["hints"]) > 0

    def test_form_faq_field(self, runner):
        result = runner.invoke(cli, ["form", "faq", "--field", "name"])
        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["ok"] is True

    def test_form_schema_command(self, runner):
        result = runner.invoke(cli, ["form", "schema"])
        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["ok"] is True
        assert out["data"]["form_id"] == "entity-formation"

    def test_form_help(self, runner):
        result = runner.invoke(cli, ["form", "--help"])
        assert result.exit_code == 0
        assert "Interactive" in result.output or "interactive" in result.output
