import json
import os
import re

import openai

from config import OPENAI_API_KEY
from logger import log

client = openai.OpenAI(api_key=OPENAI_API_KEY)


class Summarizer:
    """
    Erstellt Podcast-Zusammenfassungen und Hook-Texte.
    """

    HOOK_OPENERS = [
        "Wir schauen heute auf",
        "Heute geht es bei uns um",
        "Wir sprechen heute über",
        "Im Mittelpunkt stehen heute",
        "Heute blicken wir auf",
        "Wir nehmen heute in den Blick",
        "Heute widmen wir uns",
        "Wir werfen heute einen Blick auf",
    ]
    HOOK_OPENER_STATE_FILE = os.path.join(
        os.path.dirname(__file__),
        ".hook_opener_state.json",
    )

    def _get_next_hook_opener(self) -> str:
        if not self.HOOK_OPENERS:
            return "Wir schauen heute auf"

        current_index = -1
        try:
            with open(self.HOOK_OPENER_STATE_FILE, "r", encoding="utf-8") as state_file:
                state = json.load(state_file)
            current_index = int(state.get("current_index", -1))
        except (FileNotFoundError, json.JSONDecodeError, OSError, TypeError, ValueError):
            current_index = -1

        next_index = (current_index + 1) % len(self.HOOK_OPENERS)

        try:
            with open(self.HOOK_OPENER_STATE_FILE, "w", encoding="utf-8") as state_file:
                json.dump({"current_index": next_index}, state_file)
        except OSError as exc:
            log(
                f"Hook-Opener-Status konnte nicht gespeichert werden: {exc}",
                level="WARNING",
                source="SYSTEM",
            )

        return self.HOOK_OPENERS[next_index]

    def _normalize_hook_start(self, hook: str, opener: str) -> str:
        hook = hook.strip()
        if not hook:
            return hook

        leading_patterns = [
            r"^\s*wir\s+beleuchten\s+heute\b",
            r"^\s*wir\s+werden\s+heute\b",
            r"^\s*wir\s+schauen\s+heute\s+auf\b",
            r"^\s*wir\s+sprechen\s+heute\s+über\b",
            r"^\s*im\s+mittelpunkt\s+stehen\s+heute\b",
            r"^\s*heute\s+blicken\s+wir\s+auf\b",
            r"^\s*wir\s+nehmen\s+heute\s+in\s+den\s+blick\b",
            r"^\s*heute\s+widmen\s+wir\s+uns\b",
            r"^\s*wir\s+werfen\s+heute\s+einen\s+blick\s+auf\b",
        ]

        for pattern in leading_patterns:
            if re.match(pattern, hook, flags=re.IGNORECASE):
                return re.sub(pattern, opener, hook, count=1, flags=re.IGNORECASE).strip()

        return hook

    def summarize(
        self,
        title: str,
        content: str,
        is_fachartikel: bool = False,
        is_podcast: bool = False,
        author: str = "",
    ) -> str:
        length_hint = (
            "Die Zusammenfassung soll etwa achthundert bis eintausend Zeichen lang sein. "
            "Sie darf keinesfalls kürzer als sechshundertfünfzig und nicht länger als eintausendzweihundert Zeichen sein."
        )

        fachartikel_hint = (
            (
                f"Beginne den Beitrag mit einer kurzen Einordnung im Wortlaut: „Im Fachartikel von {author} …“. "
                f"Formuliere anschließend die Kernaussagen und beziehe Aussagen explizit auf die genannte Person, zum Beispiel: "
                f"„{author} erläutert, dass …“ oder „Nach Einschätzung von {author} …“. "
                "Vermeide objektive Behauptungen ohne Quelle. "
            )
            if author
            else (
                "Beginne den Beitrag mit einer kurzen Einordnung im Wortlaut: „Im Fachartikel …“. "
                "Formuliere anschließend die Kernaussagen. Vermeide objektive Behauptungen ohne Quelle. "
            )
        ) if is_fachartikel else ""

        podcast_hint = (
            (
                f"Beginne den Beitrag sinngemäß mit: „In der heutigen Podcast-Episode mit {author} wird … besprochen“. "
                "Schreibe im Stil einer gesprochenen Moderation: ruhig, freundlich, natürlich. "
                f"Beziehe Aussagen, wo passend, auf {author}, zum Beispiel: „{author} erklärt …“. "
            )
            if author
            else (
                "Beginne den Beitrag sinngemäß mit: „In der heutigen Podcast-Episode wird … besprochen“. "
                "Schreibe im Stil einer gesprochenen Moderation: ruhig, freundlich, natürlich. "
            )
        ) if is_podcast else ""

        number_hint = (
            "Schreibe ausnahmslos alle Zahlen als Wörter aus, einschließlich Jahreszahlen, Prozentangaben, Temperaturen, "
            "Währungen und Mengenangaben. Vermeide Ziffern vollständig. "
            "Falls Dezimalzahlen vorkommen, runde sie auf oder ab und verwende Begriffe wie „ungefähr“, „rund“, „über“, „unter“ oder „mehr als“."
        )

        closing_hint = (
            "Der letzte Satz muss vollständig und ruhig enden, mit einem klaren Punkt abgeschlossen sein. "
            "Vermeide offene Formulierungen oder Cliffhanger wie „mehr dazu gleich“ oder „das erfahren Sie später“."
        )

        content_restriction = (
            "**Verwende ausschließlich den bereitgestellten Inhalt. Erfinde keinerlei Informationen.** "
            "Füge keine weiteren Namen, Begriffe, Zahlen, Interpretationen oder Beispiele hinzu, die nicht im Text stehen. "
            "Falls der Text unvollständig ist, gib nur das wieder, was vorhanden ist, ohne zu raten oder zu spekulieren. "
            "Falls keine Personen genannt sind, nenne auch keine."
        )

        prompt = (
            f"{content_restriction} {length_hint} {podcast_hint} {fachartikel_hint} "
            f"{number_hint} {closing_hint} "
            "Formuliere in vollständigen, klaren Sätzen. "
            "Vermeide Floskeln, Aufzählungen, Meta-Kommentare oder journalistische Phrasen. "
            "Verwende keinerlei Abkürzungen. Englische Fachbegriffe wie 'Marketing Funnel' dürfen vorkommen, "
            "deutsche Begriffe bitte ausgeschrieben.\n\n"
            f"Titel: {title}\n\nInhalt: {content}"
        )

        try:
            response = client.chat.completions.create(
                model="gpt-4o-mini",
                temperature=0.4,
                messages=[
                    {
                        "role": "system",
                        "content": (
                            "Du bist Redakteurin für einen deutschsprachigen Marketing-Podcast. "
                            "Deine Texte sind ruhig, sachlich, flüssig und für das Vorlesen optimiert."
                        ),
                    },
                    {"role": "user", "content": prompt},
                ],
            )

            summary = response.choices[0].message.content.strip()

            if not summary:
                log("Leere Zusammenfassung erhalten.", level="ERROR", source="SYSTEM")
                return ""

            if not summary.endswith((".", "!", "?")):
                summary += "."

            log(
                f"Zusammenfassung erfolgreich generiert für: {title} ({len(summary)} Zeichen)",
                level="INFO",
                source="SYSTEM",
            )
            return summary

        except Exception as exc:
            log(f"Fehler bei OpenAI-Zusammenfassung: {exc}", level="ERROR", source="SYSTEM")
            return ""

    def generate_hook(self, topics: list[dict]) -> str:
        """
        topics: List of dicts with keys:
            - title: str
            - author: str (optional, from RSS)
            - is_fachartikel: bool
            - is_podcast: bool
        """

        def label_for(topic: dict) -> str:
            title = topic.get("title", "").strip()
            author = (topic.get("author") or "").strip()
            is_fachartikel = bool(topic.get("is_fachartikel"))
            is_podcast = bool(topic.get("is_podcast"))

            if is_fachartikel:
                if author:
                    return f"im Fachartikel von {author} zu „{title}“"
                return f"im Fachartikel zu „{title}“"
            if is_podcast:
                if author:
                    return f"im Podcast mit {author} über „{title}“"
                return f"im Podcast über „{title}“"
            return f"zu „{title}“"

        labels = [label_for(topic) for topic in topics if topic.get("title")]
        has_fachartikel = any(bool(topic.get("is_fachartikel")) for topic in topics)
        has_podcast = any(bool(topic.get("is_podcast")) for topic in topics)

        if not labels:
            items_text = "einige aktuelle Themen"
        elif len(labels) == 1:
            items_text = labels[0]
        elif len(labels) == 2:
            items_text = " und ".join(labels)
        else:
            items_text = ", ".join(labels[:-1]) + " und " + labels[-1]

        selected_opener = self._get_next_hook_opener()

        instruction_lines = [
            "Formuliere einen sehr kurzen Einleitungstext für einen Podcast, direkt nach dem Intro.",
            "Baue die Themen genau in der bereits vorformatierten Zielform ein, ohne Umdeklarationen.",
            "Verwende nicht die Worte „Episode“, „Folge“, „Willkommen“, „Intro“ oder ähnliche Meta-Bezüge.",
            f"Beginne exakt mit „{selected_opener}“ und führe den Satz direkt flüssig weiter.",
            "Schreibe ausnahmslos alle Zahlen als Wörter aus, auch Jahreszahlen, Prozentangaben, Währungen und Mengenangaben.",
        ]
        if has_fachartikel:
            instruction_lines.append(
                "Nur für ausdrücklich markierte Fachartikel: „im Fachartikel von {Autor} zu „{Titel}“ ...“ oder ohne Autor „im Fachartikel zu „{Titel}“ ...“."
            )
        if has_podcast:
            instruction_lines.append(
                "Nur für ausdrücklich markierte Podcasts: „im Podcast mit {Autor} über „{Titel}“ ...“ oder ohne Autor „im Podcast über „{Titel}“ ...“."
            )
        instruction_lines.append(
            "Für alle anderen Themen: Baue den Inhalt in einem flüssigen Satz ein, ohne den Titel wörtlich zu wiederholen."
        )
        instruction_lines.append(
            "Verwende die Wörter „Fachartikel“ oder „Podcast“ ausschließlich, wenn sie bereits in den vorformatierten Themen enthalten sind."
        )
        instruction_lines.append(
            "Schreibe maximal zwei Sätze, nur Fließtext, ohne Floskeln, und schließe mit einem ruhigen Punkt."
        )

        prompt = (
            " ".join(instruction_lines)
            + "\n\n"
            + f"Themen (bereits in Zielform vorformatiert): {items_text}"
        )

        try:
            response = client.chat.completions.create(
                model="gpt-4o",
                temperature=0.4,
                messages=[
                    {
                        "role": "system",
                        "content": (
                            "Du bist Redakteurin für einen professionellen deutschsprachigen Marketing-Podcast. "
                            "Dein Einleitungstext benennt die Themen präzise und natürlich."
                        ),
                    },
                    {"role": "user", "content": prompt},
                ],
            )
            hook = response.choices[0].message.content.strip()

            if hook:
                patterns = [
                    r"^\s*in\s+dieser\s+(?:podcast-)?episode\s*[:,-]?\s*",
                    r"^\s*in\s+der\s+heutigen\s+(?:podcast-)?episode\s*[:,-]?\s*",
                    r"^\s*in\s+dieser\s+folge\s*[:,-]?\s*",
                    r"^\s*in\s+der\s+heutigen\s+folge\s*[:,-]?\s*",
                    r"^\s*in\s+dieser\s+sendung\s*[:,-]?\s*",
                    r"^\s*in\s+der\s+heutigen\s+sendung\s*[:,-]?\s*",
                ]
                for pattern in patterns:
                    hook = re.sub(pattern, "", hook, flags=re.IGNORECASE)

                hook = self._normalize_hook_start(hook, selected_opener)
                hook = re.sub(r"\s{2,}", " ", hook).strip()

            if not hook:
                log("Leerer Hook-Text generiert.", level="ERROR", source="SYSTEM")
                return ""
            if not hook.endswith((".", "!", "?")):
                hook += "."

            log(
                f"Hook-Text erfolgreich generiert mit Opener: {selected_opener}",
                level="INFO",
                source="SYSTEM",
            )
            return hook

        except Exception as exc:
            log(f"Fehler beim Erzeugen des Hook-Texts: {exc}", level="ERROR", source="SYSTEM")
            return ""
