import base64
import json
import mimetypes
import re

from django.conf import settings

from claims.models import Category, Claim

VALID_CLAIM_STATUSES = {choice[0] for choice in Claim.Status.choices}
VALID_EVIDENCE_LEVELS = {choice[0] for choice in Claim.EvidenceLevel.choices}

SYSTEM_PROMPT = """Du bist ein sachlicher Fakten-Assistent für die Plattform „Faktenkompass“.
Deine Aufgabe: Aus Artikeln, Bildern und Nutzereingaben einen überprüfbaren Fakteneintrag erstellen.

Regeln:
- Schreibe auf Deutsch, neutral und wissenschaftlich.
- Keine parteipolitische Sprache, keine Übertreibungen.
- Die Behauptung (title) formuliert eine häufig geäußerte Aussage, die überprüft wird.
- Die Kurzantwort ist in ~20 Sekunden lesbar.
- Quellen müssen echte, nachvollziehbare Referenzen sein (URL aus Eingabe bevorzugen).
- Wähle eine passende bestehende Kategorie (use_existing_slug) ODER schlage eine neue vor.
- Icons für neue Kategorien: Bootstrap Icons Klassen wie bi-thermometer-half, bi-virus, bi-lightning-charge.

Antworte ausschließlich als gültiges JSON mit dieser Struktur:
{
  "category": {
    "use_existing_slug": "slug-oder-null",
    "new_category": {
      "name": "Name",
      "description": "Kurzbeschreibung",
      "icon": "bi-icon-name"
    }
  },
  "claim": {
    "title": "Die zu prüfende Behauptung",
    "status": "falsch|irrefuehrend|teilweise_richtig|unbelegt|wissenschaftlicher_konsens|offene_frage",
    "short_answer": "Kurze Einordnung",
    "detailed_explanation": "Ausführlichere Erklärung",
    "evidence_level": "sehr_stark|stark|mittel|schwach"
  },
  "tags": ["Schlagwort1", "Schlagwort2"],
  "sources": [
    {
      "title": "Titel",
      "organization": "Autor/Organisation",
      "url": "https://...",
      "description": "Kurzbeschreibung"
    }
  ],
  "counter_arguments": [
    {
      "argument": "Häufiges Gegenargument",
      "response": "Sachliche Antwort"
    }
  ],
  "assistant_summary": "Kurze Zusammenfassung für den Admin (1-2 Sätze)"
}
"""


class AIAssistantError(Exception):
    pass


def _categories_context():
    categories = Category.objects.all().values('name', 'slug', 'description')
    if not categories:
        return 'Keine Kategorien vorhanden – neue Kategorie vorschlagen.'
    lines = []
    for cat in categories:
        lines.append(f"- {cat['name']} (slug: {cat['slug']}): {cat['description'][:120]}")
    return '\n'.join(lines)


def _build_user_prompt(*, article_url='', article_text='', user_notes='', refinement=''):
    parts = [
        'Bestehende Kategorien:',
        _categories_context(),
        '',
        f'Artikel-URL: {article_url or "(keine)"}',
        '',
        'Artikeltext:',
        article_text or '(nicht verfügbar)',
        '',
        'Zusätzliche Hinweise des Admins:',
        user_notes or '(keine)',
    ]
    if refinement:
        parts.extend(['', 'Überarbeitungswunsch:', refinement])
    return '\n'.join(parts)


def _svg_to_png_bytes(svg_data):
    try:
        import cairosvg
    except ImportError:
        return None

    try:
        return cairosvg.svg2png(bytestring=svg_data)
    except Exception:
        return None


def _svg_text_fallback(svg_data):
    text = svg_data.decode('utf-8', errors='replace')
    text = re.sub(r'<script[^>]*>.*?</script>', '', text, flags=re.IGNORECASE | re.DOTALL)
    return text[:4000]


def _image_data_url(data, mime_type):
    encoded = base64.b64encode(data).decode('ascii')
    return {
        'type': 'image_url',
        'image_url': {'url': f'data:{mime_type};base64,{encoded}'},
    }


def _image_message_part(uploaded_file):
    uploaded_file.open('rb')
    try:
        data = uploaded_file.read()
    finally:
        uploaded_file.close()

    mime_type = mimetypes.guess_type(uploaded_file.name)[0]
    if mime_type == 'application/pdf' or uploaded_file.name.lower().endswith('.pdf'):
        return {
            'type': 'text',
            'text': '[PDF-Datei hochgeladen – nutze vor allem die Admin-Beschreibung und den Artikeltext.]',
        }

    if mime_type == 'image/svg+xml' or uploaded_file.name.lower().endswith('.svg'):
        png_data = _svg_to_png_bytes(data)
        if png_data:
            return _image_data_url(png_data, 'image/png')
        return {
            'type': 'text',
            'text': (
                '[SVG-Grafik hochgeladen – OpenAI unterstützt SVG nicht direkt. '
                'Nutze diesen XML-Auszug und die Admin-Beschreibung:]\n'
                + _svg_text_fallback(data)
            ),
        }

    mime_type = mime_type or 'image/jpeg'
    if mime_type not in {'image/png', 'image/jpeg', 'image/gif', 'image/webp'}:
        mime_type = 'image/jpeg'
    return _image_data_url(data, mime_type)


def _call_openai(messages, *, has_image=False):
    api_key = getattr(settings, 'OPENAI_API_KEY', '')
    if not api_key:
        raise AIAssistantError(
            'OPENAI_API_KEY ist nicht gesetzt. Bitte in der Umgebung oder .env konfigurieren.'
        )

    try:
        from openai import OpenAI
    except ImportError as exc:
        raise AIAssistantError('OpenAI-Paket fehlt. Bitte pip install -r requirements.txt ausführen.') from exc

    model = settings.OPENAI_MODEL_VISION if has_image else settings.OPENAI_MODEL
    client = OpenAI(api_key=api_key, timeout=settings.OPENAI_TIMEOUT)

    response = client.chat.completions.create(
        model=model,
        messages=messages,
        response_format={'type': 'json_object'},
        temperature=0.3,
    )
    content = response.choices[0].message.content
    if not content:
        raise AIAssistantError('Leere Antwort von der KI erhalten.')
    return json.loads(content)


def _validate_payload(payload):
    claim = payload.get('claim') or {}
    status = claim.get('status', '')
    evidence = claim.get('evidence_level', '')
    if status and status not in VALID_CLAIM_STATUSES:
        raise AIAssistantError(f'Ungültiger Status von der KI: {status}')
    if evidence and evidence not in VALID_EVIDENCE_LEVELS:
        raise AIAssistantError(f'Ungültiges Evidenzlevel von der KI: {evidence}')
    return payload


def generate_draft_payload(
    *,
    article_url='',
    article_text='',
    user_notes='',
    uploaded_file=None,
    refinement='',
    previous_payload=None,
):
    user_prompt = _build_user_prompt(
        article_url=article_url,
        article_text=article_text,
        user_notes=user_notes,
        refinement=refinement,
    )
    if previous_payload:
        user_prompt += '\n\nBisheriger Entwurf (JSON):\n' + json.dumps(previous_payload, ensure_ascii=False)

    user_content = [{'type': 'text', 'text': user_prompt}]
    has_image = False
    extra_text = ''
    if uploaded_file and uploaded_file.name:
        part = _image_message_part(uploaded_file)
        if part.get('type') == 'image_url':
            user_content.append(part)
            has_image = True
        elif part.get('type') == 'text':
            extra_text = part['text']

    if extra_text:
        user_prompt = f'{user_prompt}\n\n{extra_text}'

    if has_image:
        user_content[0] = {'type': 'text', 'text': user_prompt}
        user_message = user_content
    else:
        user_message = user_prompt

    messages = [
        {'role': 'system', 'content': SYSTEM_PROMPT},
        {'role': 'user', 'content': user_message},
    ]
    payload = _call_openai(messages, has_image=has_image)
    return _validate_payload(payload)


def apply_payload_to_draft(draft, payload):
    category = payload.get('category') or {}
    claim = payload.get('claim') or {}

    draft.category_slug = category.get('use_existing_slug') or ''
    new_category = category.get('new_category') or {}
    draft.new_category_name = new_category.get('name', '')
    draft.new_category_description = new_category.get('description', '')
    draft.new_category_icon = new_category.get('icon', '')

    draft.title = claim.get('title', '')
    draft.claim_status = claim.get('status', Claim.Status.UNBELEGT)
    draft.short_answer = claim.get('short_answer', '')
    draft.detailed_explanation = claim.get('detailed_explanation', '')
    draft.evidence_level = claim.get('evidence_level', Claim.EvidenceLevel.MITTEL)
    draft.tags = payload.get('tags') or []
    draft.sources = payload.get('sources') or []
    draft.counter_arguments = payload.get('counter_arguments') or []
    draft.ai_raw_response = payload
    summary = payload.get('assistant_summary', 'Entwurf wurde erstellt.')
    draft.add_message('assistant', summary)
