"""
AI provider abstraction for ticket acknowledgment and activity classification.

Supports three providers via AI_PROVIDER env var:
  - anthropic (default) — Claude Haiku
  - openai              — GPT-4o Mini via Responses API
  - gemini              — Gemini Flash via google-genai SDK

All sync SDK calls are wrapped in asyncio.to_thread to avoid blocking the event loop.
"""

import asyncio
import re
import anthropic
import openai
from google import genai
from .config import settings

# Prompt for customer-facing acknowledgment reply.
# Language is auto-detected from the ticket content.
_ACK_PROMPT = (
    "You are a professional support agent at PIT Solution AG. "
    "Write a warm, concise acknowledgment for the support ticket below.\n"
    "Rules:\n"
    "- Detect the language of the ticket (Subject + Message). Reply in that language. Default to English if unsure.\n"
    "- Start with a greeting on its own line: 'Hi {customer},' or 'Dear {customer},' (use Hi for informal, Dear for formal tone).\n"
    "- Then a blank line.\n"
    "- Then 2-3 sentences: confirm receipt, show you understood the issue, assure the team will look into it.\n"
    "- Then a blank line.\n"
    "- End with exactly two lines:\n"
    "  \n"
    "  Best regards,\n"
    "  {current_user}\n"
    "  PIT Solution AG\n"
    "- Plain text only — no markdown.\n\n"
    "Customer name: {customer}\n"
    "Subject: {title}\n"
    "Message: {body}"
)

# Prompt for classifying ticket into Azure DevOps activity type.
# Must return exactly one word from the allowed set.
_ACTIVITY_PROMPT = (
    "Classify this support ticket into exactly one of these Azure DevOps activity types:\n"
    "Design, Development, Deployment, Documentation, Requirements, Testing\n\n"
    "Rules:\n"
    "- Design: UI/UX, layout, mockups, visual changes\n"
    "- Development: bugs, features, code changes, integrations\n"
    "- Deployment: releases, server issues, hosting, configuration\n"
    "- Documentation: docs, manuals, guides, content updates\n"
    "- Requirements: requests, specifications, planning\n"
    "- Testing: QA, test failures, verification\n\n"
    "Reply with only the single word. No explanation.\n\n"
    "Subject: {title}\n"
    "Message: {body}"
)

_VALID_ACTIVITIES = {"Design", "Development", "Deployment", "Documentation", "Requirements", "Testing"}

# Prompt to suggest relevant DevOps task tags for Looping Group website tickets.
# Returns semicolon-separated tags (Azure DevOps tag format).
# {context} is replaced with a structured block built from all available ticket data.
_TAGS_PROMPT = (
    "You are a project manager at Looping Group, a web agency. "
    "Analyze this support ticket and suggest relevant tags for the Azure DevOps task.\n\n"
    "Choose only from these tag categories (pick all that apply, max 10 total):\n\n"
    "Website (tag name → domain/aliases — search ALL context fields for any match):\n"
    "  Aquacolors → aquacolors.fr\n"
    "  Aquaparc → aquaparc.ch, aquaparc.com\n"
    "  Aquatico Amarante → aquaticoamarante.pt, aquatico\n"
    "  Avonturenpark Hellendoorn → avonturenpark.nl, hellendoorn\n"
    "  Parc Bagatelle → parcbagatelle.fr, bagatelle\n"
    "  Cobac Parc → cobacparc.fr, cobac\n"
    "  Drayton Manor Resort Limited → draytonmanor.co.uk, drayton manor, drayton\n"
    "  Drievliet → drievliet.nl\n"
    "  FORT FUN GmbH → fortfun.de, fort-fun.de, fort fun, fortfun\n"
    "  Grand Aquarium de Saint Malo → aquarium-st-malo.com, saint malo, saintmalo\n"
    "  Aquarium de Touraine → aquariumdetouraine.fr, touraine\n"
    "  Isla Magica → islamagica.es, isla magica\n"
    "  Istralandia → istralandia.hr\n"
    "  La Mer de Sable → lamerdesable.fr, mer de sable\n"
    "  Looping Group → loopinggroup.com (use ONLY if no specific park/site is mentioned anywhere)\n"
    "  Mini Chateaux → minichateaux.fr, mini chateaux\n"
    "  Parc Saint Paul → parcsaintpaul.com, saint paul\n"
    "  Planete Sauvage → planete-sauvage.fr, planete sauvage\n"
    "  Pleasurewood Hills → pleasurewoodhills.com, pleasurewood\n"
    "  Waterworld → waterworlduk.co.uk\n"
    "  West Midland Safari Park → wmsp.co.uk, west midland safari, west midland\n"
    "  Zoo de La Fleche → zoo-la-fleche.com, la fleche, lafleche\n\n"
    "Technology: WordPress, TYPO3, Shopify, WooCommerce, PHP, JavaScript, HTML/CSS, React, Vue, Laravel, MySQL, API\n"
    "Issue type: Bug, Feature Request, Content Update, Performance, Security, Design Change, Integration, Migration, SEO\n"
    "Urgency: Urgent, Critical, Routine\n\n"
    "Rules:\n"
    "- REQUIRED: exactly 1 website tag (use tag name, not domain/alias).\n"
    "- Check customer email domain (e.g. user@fortfun.de → FORT FUN GmbH), organization name, "
    "ticket subject, description, and all comments/replies for any site mention.\n"
    "- Default to 'Looping Group' ONLY when truly no park/site appears anywhere in any field.\n"
    "- Include other clearly applicable tags (max 10 total).\n"
    "- Return semicolon-separated on a single line, no explanation.\n"
    "- Example: FORT FUN GmbH; WordPress; Bug; Urgent\n\n"
    "{context}"
)


def _strip_html(html: str) -> str:
    """Strip HTML tags, leaving plain text."""
    return re.sub(r"<[^>]+>", " ", html).strip()


def _has_provider() -> bool:
    """Return True if the configured AI provider has an API key set."""
    if settings.ai_provider == "openai":
        return bool(settings.openai_api_key)
    if settings.ai_provider == "gemini":
        return bool(settings.gemini_api_key)
    return bool(settings.anthropic_api_key)


def _call_anthropic(prompt: str) -> str:
    client = anthropic.Anthropic(api_key=settings.anthropic_api_key)
    msg = client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=250,
        messages=[{"role": "user", "content": prompt}],
    )
    return msg.content[0].text


def _call_openai(prompt: str) -> str:
    client = openai.OpenAI(api_key=settings.openai_api_key)
    resp = client.responses.create(
        model="gpt-4o-mini",
        input=prompt,
    )
    return resp.output_text


def _call_gemini(prompt: str) -> str:
    client = genai.Client(api_key=settings.gemini_api_key)
    resp = client.models.generate_content(
        model="gemini-flash-latest",
        contents=prompt,
    )
    return resp.text


def _call_ai(prompt: str) -> str:
    """Dispatch to the configured AI provider."""
    if settings.ai_provider == "openai":
        return _call_openai(prompt)
    if settings.ai_provider == "gemini":
        return _call_gemini(prompt)
    return _call_anthropic(prompt)


async def generate_acknowledgment(
    ticket_title: str,
    ticket_body_html: str,
    customer_name: str = "",
    agent_name: str = "",
) -> str:
    """Generate a personalised acknowledgment reply in the ticket's language.
    Truncates body to 1500 chars to stay within token limits."""
    if not _has_provider():
        raise RuntimeError(f"AI provider '{settings.ai_provider}' selected but API key not set")

    body_text = _strip_html(ticket_body_html)[:1500]
    customer = customer_name or "there"
    current_user = agent_name or "Support Team"
    prompt = _ACK_PROMPT.format(title=ticket_title, body=body_text, customer=customer, current_user=current_user)
    return await asyncio.to_thread(_call_ai, prompt)


async def classify_activity(ticket_title: str, ticket_body_html: str) -> str:
    """Classify ticket into a DevOps activity type using AI.
    Falls back to 'Development' if AI is unavailable or returns an invalid value."""
    if not _has_provider():
        return "Development"

    body_text = _strip_html(ticket_body_html)[:800]
    prompt = _ACTIVITY_PROMPT.format(title=ticket_title, body=body_text)
    try:
        raw = await asyncio.to_thread(_call_ai, prompt)
        activity = raw.strip().strip(".")
        return activity if activity in _VALID_ACTIVITIES else "Development"
    except Exception:
        return "Development"


async def suggest_tags(
    ticket_title: str,
    ticket_body_html: str,
    customer_email: str = "",
    customer_org: str = "",
    customer_name: str = "",
    articles: list | None = None,
) -> str:
    """Suggest semicolon-separated Azure DevOps tags for a Looping Group website ticket.
    Uses all available context: title, description, customer info, and all article/comment bodies.
    Returns empty string if AI unavailable or classification fails."""
    if not _has_provider():
        return ""

    lines = []
    lines.append(f"Subject: {ticket_title}")
    if customer_name:
        lines.append(f"Customer: {customer_name}")
    if customer_email:
        lines.append(f"Customer email: {customer_email}")
    if customer_org:
        lines.append(f"Customer organization: {customer_org}")

    description = _strip_html(ticket_body_html)[:1000].strip()
    if description:
        lines.append(f"Description:\n{description}")

    if articles:
        for i, body_html in enumerate(articles[:10], 1):
            text = _strip_html(body_html)[:500].strip()
            if text:
                lines.append(f"Comment {i}:\n{text}")

    context = "\n\n".join(lines)
    prompt = _TAGS_PROMPT.format(context=context)
    try:
        raw = await asyncio.to_thread(_call_ai, prompt)
        tags = [t.strip() for t in raw.replace(",", ";").split(";") if t.strip()]
        return "; ".join(tags[:10])
    except Exception:
        return ""
