"""TestDataGeneratorAgent — AI-powered realistic test data for forms.

Instead of hardcoded "test123" values, generates contextual test data:
- Valid data for happy paths
- Invalid data for negative tests (wrong formats, boundary values)
- Edge case data (long strings, special chars, XSS, SQLi)
"""

from __future__ import annotations

import json
import logging
from typing import Any

from agno.agent import Agent

from antiqa.config.models import resolve_model
from antiqa.config.schema import AntiQAConfig

log = logging.getLogger("antiqa.agents")

DATA_GEN_PROMPT = """\
You are a QA test data expert. Generate realistic test data for form fields.

Given a list of form fields (with their names/types), generate multiple data sets:
1. **valid**: Realistic data that should pass all validation
2. **invalid**: Data that should trigger validation errors (wrong formats, missing required)
3. **boundary**: Edge cases (empty strings, max length, min/max numbers, unicode, special chars)
4. **security**: Potentially dangerous inputs (SQL injection, XSS, path traversal) for security testing

Rules:
- Use realistic, contextual values (not "test123")
- For email fields: use real-looking emails for valid, malformed for invalid
- For password fields: use strong passwords for valid, too-short for invalid
- For numeric fields: use 0, -1, MAX_INT, decimals for boundary
- For text fields: use very long strings (200+ chars), unicode, emojis for boundary
- Security data must be actual payloads (e.g. `' OR 1=1--`, `<script>alert(1)</script>`)

Return ONLY valid JSON:
```json
{
  "valid": [
    {"field_name": "value", ...}
  ],
  "invalid": [
    {"field_name": "bad_value", "_expect_error": "description of expected validation error", ...}
  ],
  "boundary": [
    {"field_name": "edge_value", "_scenario": "empty field", ...}
  ],
  "security": [
    {"field_name": "payload", "_test_type": "sql_injection", ...}
  ]
}
```
"""


class TestDataGeneratorAgent:
    """Generate realistic test data for form fields using AI."""

    def __init__(self, config: AntiQAConfig) -> None:
        self.config = config
        self.model = resolve_model(config.llm)

    def generate(
        self,
        fields: list[dict[str, str]],
        form_context: str = "",
    ) -> dict[str, list[dict[str, Any]]]:
        """Generate test data sets for a list of form fields.

        Args:
            fields: List of dicts like [{"name": "email", "type": "email"}, ...]
            form_context: Optional description of the form purpose

        Returns:
            Dict with keys: valid, invalid, boundary, security — each a list of data dicts.
        """
        user_prompt = self._build_prompt(fields, form_context)

        from agno.utils.log import logger as agno_logger
        prev_handlers = agno_logger.handlers[:]
        prev_propagate = agno_logger.propagate
        agno_logger.handlers = [logging.NullHandler()]
        agno_logger.propagate = False
        try:
            agent = Agent(
                model=self.model,
                instructions=[DATA_GEN_PROMPT],
                markdown=False,
            )
            response = agent.run(user_prompt)
            content = _clean(response.content or "{}")
            return json.loads(content)
        except Exception as exc:
            log.warning("Test data generation failed: %s", exc)
            return self._fallback(fields)
        finally:
            agno_logger.handlers = prev_handlers
            agno_logger.propagate = prev_propagate

    def generate_for_page(
        self,
        page_html: str,
        page_url: str = "",
    ) -> dict[str, list[dict[str, Any]]]:
        """Extract form fields from HTML and generate test data."""
        fields = self._extract_fields_from_html(page_html)
        if not fields:
            return {"valid": [], "invalid": [], "boundary": [], "security": []}
        return self.generate(fields, form_context=f"Form on page: {page_url}")

    def _build_prompt(self, fields: list[dict[str, str]], context: str) -> str:
        parts = ["## Form Fields\n"]
        if context:
            parts.append(f"**Context:** {context}\n")
        for f in fields:
            parts.append(f"- **{f.get('name', 'unknown')}**: type={f.get('type', 'text')}")
        return "\n".join(parts)

    def _extract_fields_from_html(self, html: str) -> list[dict[str, str]]:
        """Simple extraction of input fields from HTML."""
        import re
        fields: list[dict[str, str]] = []
        for match in re.finditer(r'<input[^>]*>', html, re.IGNORECASE):
            tag = match.group()
            name_m = re.search(r'name=["\']([^"\']+)', tag)
            type_m = re.search(r'type=["\']([^"\']+)', tag)
            if name_m:
                fields.append({
                    "name": name_m.group(1),
                    "type": type_m.group(1) if type_m else "text",
                })
        return fields

    def _fallback(self, fields: list[dict[str, str]]) -> dict[str, list[dict[str, Any]]]:
        """Deterministic fallback when AI is unavailable."""
        valid: dict[str, Any] = {}
        invalid: dict[str, Any] = {}
        boundary: dict[str, Any] = {}
        security: dict[str, Any] = {}

        for f in fields:
            name = f.get("name", "field")
            ftype = f.get("type", "text")

            if ftype == "email":
                valid[name] = "testuser@example.com"
                invalid[name] = "not-an-email"
                boundary[name] = ""
                security[name] = "test@example.com' OR 1=1--"
            elif ftype == "password":
                valid[name] = "SecureP@ss123!"
                invalid[name] = "123"
                boundary[name] = ""
                security[name] = "' OR 1=1--"
            elif "number" in ftype or "price" in name or "stock" in name:
                valid[name] = "100"
                invalid[name] = "abc"
                boundary[name] = "-1"
                security[name] = "999999999999"
            else:
                valid[name] = f"Test {name.title()}"
                invalid[name] = ""
                boundary[name] = "A" * 256
                security[name] = "<script>alert(1)</script>"

        return {
            "valid": [valid],
            "invalid": [{**invalid, "_expect_error": "Validation should fail"}],
            "boundary": [{**boundary, "_scenario": "Edge case values"}],
            "security": [{**security, "_test_type": "injection_tests"}],
        }


def _clean(content: str) -> str:
    content = content.strip()
    if content.startswith("```"):
        first_newline = content.index("\n") if "\n" in content else 3
        content = content[first_newline + 1:]
    if content.endswith("```"):
        content = content[:-3]
    return content.strip()
