#!/usr/bin/env python3
"""
LangChain Form Generator Agent
===============================

This example shows a LangChain agent that generates AgentForms dynamically
from natural-language prompts — e.g. "Build me a job application form for
a restaurant" or "Create a checkout form for event tickets."

Pattern:
    1. Human provides a prompt describing the desired form.
    2. The LangChain LLM parses the prompt and defines the fields.
    3. The agent calls the AgentForms SDK to create the form.
    4. A shareable URL is returned to the human.
    5. The agent optionally polls for submissions and summarises results.

Prerequisites:
    pip install langchain langchain-openai agentforms requests

    # Configure your LLM provider (e.g. OPENAI_API_KEY)
    # Configure AgentForms:
    #   export AGENTFORMS_API_KEY='***'
"""

import os
import sys
import time
from typing import Optional

# ---------------------------------------------------------------------------
# AgentForms integration
# ---------------------------------------------------------------------------

try:
    from agentforms import AgentForms, FieldDefinition
except ImportError:
    print("ERROR: agentforms SDK not installed. Run: pip install agentforms")
    sys.exit(1)


# ---------------------------------------------------------------------------
# LangChain tool definition
# ---------------------------------------------------------------------------

try:
    from langchain.tools import tool
    from langchain_core.output_parsers import StrOutputParser
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    LANGCHAIN_AVAILABLE = True
except ImportError:
    LANGCHAIN_AVAILABLE = False

    # Fallback stubs so the file is readable even without langchain installed.
    class _FakeTool:
        def __init__(self, name, description, **kw):
            self.name = name
            self.description = description
        def __call__(self, *a, **k):
            return None
    tool = _FakeTool


# ---------------------------------------------------------------------------
# AgentForms tool for LangChain
# ---------------------------------------------------------------------------

@tool
def create_agentform(
    name: str,
    fields: list,
    metadata: Optional[dict] = None,
) -> str:
    """
    Create an AgentForm and return its share URL.

    Args:
        name: Human-readable form name.
        fields: List of dicts with keys:
            - name (str):        snake_case field id
            - label (str):       display label
            - type (str):        one of text, email, tel, textarea,
                                 select, number, date, hidden
            - required (bool):   optional, default False
            - options (list):    optional, for select-type fields
            - placeholder (str): optional hint text
            - default (str):     optional default value

    Returns:
        Short share URL the human can open in a browser.
    """
    client = AgentForms(api_key=os.environ["AGENTFORMS_API_KEY"])
    form = client.forms.create(name=name, fields=fields, metadata=metadata)
    return form.share_url


@tool
def generate_agentform(prompt: str, form_name: Optional[str] = None) -> str:
    """
    Use AgentForms' built-in AI to generate a form from a natural-language prompt.

    Args:
        prompt:     e.g. "A feedback survey for a product launch event"
        form_name:  optional override for the AI-generated name

    Returns:
        Short share URL for the generated form.
    """
    client = AgentForms(api_key=os.environ["AGENTFORMS_API_KEY"])
    form = client.forms.generate(prompt=prompt, form_name=form_name)
    return form.share_url


@tool
def poll_submissions(
    form_url: str,
    timeout_seconds: int = 60,
    poll_interval: int = 10,
) -> str:
    """
    Wait for submissions on a form and return them as a summary string.

    Args:
        form_url:        The share URL returned by create_agentform.
        timeout_seconds: How long to wait before giving up.
        poll_interval:   Seconds between API calls.

    Returns:
        Markdown summary of collected submissions.
    """
    client = AgentForms(api_key=os.environ["AGENTFORMS_API_KEY"])

    # Extract token from share URL: https://agentforms.io/abc123
    token = form_url.rstrip("/").split("/")[-1]

    deadline = time.time() + timeout_seconds
    while time.time() < deadline:
        result = client.submissions.list(form_token=token, limit=10)
        submissions = result.get("submissions", [])

        if submissions:
            lines = [f"## Submissions collected ({len(submissions)})"]
            for i, sub in enumerate(submissions, 1):
                lines.append(f"\n### Submission {i} (id={sub.id})")
                for key, value in sub.dynamic_data.items():
                    lines.append(f"- **{key}**: {value}")
            return "\n".join(lines)

        time.sleep(poll_interval)

    return "No submissions received within the timeout window."


# ---------------------------------------------------------------------------
# Build the LangChain chain
# ---------------------------------------------------------------------------

def build_form_agent(temperature: float = 0.3):
    """
    Assemble a LangChain chat chain that uses the AgentForms tools.

    Returns:
        A runnable chain (langchain Runnable) and the tools list.
    """
    if not LANGCHAIN_AVAILABLE:
        raise ImportError(
            "LangChain is not installed. Run: pip install langchain langchain-openai"
        )

    llm = ChatOpenAI(model="gpt-4o", temperature=temperature)

    tools = [generate_agentform, create_agentform, poll_submissions]

    prompt = ChatPromptTemplate.from_messages([
        ("system", """You are a Form Builder Assistant. Your job is to help users
create data-collection forms by calling the AgentForms API.

Available tools:
- generate_agentform(prompt, form_name): AI-powered form generation from a
  natural language prompt. Use this when the user's request is broad or open-ended.
- create_agentform(name, fields, metadata): Programmatic form creation when you
  already know the exact fields. Build the fields list as dicts with:
  name, label, type, required, options, placeholder, default.
  Supported types: text, email, tel, textarea, select, number, date, hidden.
- poll_submissions(form_url, timeout_seconds, poll_interval): Wait for and
  summarise form submissions.

Workflow:
1. Ask clarifying questions if the request is vague.
2. Call generate_agentform or create_agentform to build the form.
3. Share the returned URL with the user.
4. Optionally call poll_submissions to collect responses.

Always explain what form was created and which fields it contains.""",),
        ("human", "{input}"),
    ])

    chain = prompt | llm | StrOutputParser()
    return chain, tools


# ---------------------------------------------------------------------------
# Standalone demo (runs without LangChain if not installed)
# ---------------------------------------------------------------------------

def demo_direct_api():
    """
    Minimal demo that uses the AgentForms SDK directly (no LangChain dependency).
    This is useful for verifying the SDK integration works on its own.
    """
    client = AgentForms(api_key=os.environ["AGENTFORMS_API_KEY"])

    print("📝 Creating a form via the SDK directly...\n")

    # Approach A: Programmatic creation
    form1 = client.forms.create(
        name="Quick Contact Form",
        fields=[
            {"name": "full_name", "label": "Full Name", "type": "text", "required": True},
            {"name": "email", "label": "Email Address", "type": "email", "required": True},
            {"name": "company", "label": "Company", "type": "text", "required": False},
            {"name": "topic", "label": "Topic", "type": "select",
             "options": ["Partnership", "Support", "Sales", "Other"], "required": True},
            {"name": "message", "label": "Message", "type": "textarea",
             "placeholder": "Tell us how we can help...", "required": False},
        ],
        metadata={"source": "demo", "pattern": "langchain-form-agent"},
    )
    print(f"✅ Form created: {form1.name}")
    print(f"   Share URL: {form1.share_url}")
    print(f"   Public URL: {form1.public_url}")
    print(f"   Fields: {len(form1.fields)}\n")

    for field in form1.fields:
        req = "⚠️ " if field.required else "   "
        print(f"  {req} [{field.type}] {field.label} ({field.name})")

    print("\n" + "-" * 60 + "\n")

    # Approach B: AI-powered generation (requires Starter+ tier)
    try:
        print("🤖 Generating a form with AI...\n")
        form2 = client.forms.generate(
            prompt="A food preference and allergy checklist for a corporate catering order",
            form_name="Catering Preferences",
        )
        print(f"✅ AI-generated form: {form2.name}")
        print(f"   Share URL: {form2.share_url}")
        print(f"   Fields: {len(form2.fields)}\n")

        for field in form2.fields:
            req = "⚠️ " if field.required else "   "
            print(f"  {req} [{field.type}] {field.label} ({field.name})")

    except Exception as e:
        print(f"⚠️ AI generation skipped (may require higher tier): {e}")

    return form1, form2 if "form2" in dir() else None


# ---------------------------------------------------------------------------
# Full LangChain agent demo
# ---------------------------------------------------------------------------

def demo_langchain_agent():
    """
    Interactive demo of the LangChain form agent.

    If LangChain is installed, this builds the agent and runs a sample query.
    """
    if not LANGCHAIN_AVAILABLE:
        print("LangChain not installed — running direct SDK demo instead.")
        print("Install with: pip install langchain langchain-openai\n")
        demo_direct_api()
        return

    print("🔗 Building LangChain Form Agent...\n")

    chain, tools = build_form_agent()

    sample_prompt = (
        "Create a registration form for a tech conference. "
        "Include name, email, company, job title, "
        "and a dropdown for which workshops they want to attend."
    )

    print(f"Prompt: {sample_prompt}\n")
    result = chain.invoke({"input": sample_prompt})
    print("Agent response:\n")
    print(result)
    print()

    # Show available tools
    print("Available tools:")
    for t in tools:
        print(f"  - {t.name}: {t.description[:80]}...")


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    if "AGENTFORMS_API_KEY" not in os.environ:
        print("Set the AGENTFORMS_API_KEY environment variable and try again.")
        print("  export AGENTFORMS_API_KEY='***'")
        sys.exit(1)

    mode = sys.argv[1] if len(sys.argv) > 1 else "direct"

    if mode == "langchain":
        demo_langchain_agent()
    else:
        demo_direct_api()
