# retoor <retoor@molodetz.nl>
from __future__ import annotations

import re

from devplacepy.services.jobs.isslop.analysis.signals import (
    AXIS_ORIGIN,
    AXIS_QUALITY,
    SEVERITY_MEDIUM,
    SEVERITY_STRONG,
    SEVERITY_WEAK,
    FileContext,
    Signal,
)

PLACEHOLDER_PATTERNS: tuple[tuple[re.Pattern[str], str], ...] = (
    (re.compile(r"your (code|logic|implementation) here", re.IGNORECASE), "Placeholder body left in source"),
    (re.compile(r"\.\.\. ?rest of (the )?code", re.IGNORECASE), "Truncated generation marker"),
    (re.compile(r"\.\.\. ?(existing|other|previous) code", re.IGNORECASE), "Elided-code marker"),
    (re.compile(r"/\*\s*implementation\s*\*/", re.IGNORECASE), "Empty implementation stub"),
    (re.compile(r"TODO:? implement( this)?\b", re.IGNORECASE), "Unimplemented TODO stub"),
    (re.compile(r"TODO:? add (logic|implementation|code)", re.IGNORECASE), "Unimplemented TODO stub"),
    (re.compile(r"<!--\s*add content here\s*-->", re.IGNORECASE), "Placeholder HTML content"),
    (re.compile(r"rest omitted", re.IGNORECASE), "Truncated generation marker"),
)

PROMPT_LEAK_PATTERNS: tuple[re.Pattern[str], ...] = (
    re.compile(r"as an ai (language )?model", re.IGNORECASE),
    re.compile(r"i (cannot|can't) (assist|help) with", re.IGNORECASE),
    re.compile(r"here('|’)?s the (updated|complete|revised|full|corrected) code", re.IGNORECASE),
    re.compile(r"\bcertainly!\s", re.IGNORECASE),
    re.compile(r"sure, here('s| is)", re.IGNORECASE),
    re.compile(r"i hope this helps", re.IGNORECASE),
    re.compile(r"let me know if (you|there)", re.IGNORECASE),
    re.compile(r"feel free to (adjust|modify|change|customize)", re.IGNORECASE),
    re.compile(r"note that this is a (simplified|basic) (example|implementation|version)", re.IGNORECASE),
    re.compile(r"in a (real|production)([- ]world)? (application|scenario|environment|setting)", re.IGNORECASE),
    re.compile(r"this is a placeholder", re.IGNORECASE),
    re.compile(r"replace .{1,40} with your (actual|own)", re.IGNORECASE),
)

AI_SIGNATURE_PATTERNS: tuple[re.Pattern[str], ...] = (
    re.compile(r"generated (by|with) (chatgpt|copilot|claude|cursor|gemini|codeium|v0|bolt|ai)", re.IGNORECASE),
    re.compile(r"co-authored-by:\s*(claude|copilot)", re.IGNORECASE),
    re.compile(r"\U0001F916 generated with"),
    re.compile(r"created with the help of ai", re.IGNORECASE),
    re.compile(r"\bclaude code\b", re.IGNORECASE),
    re.compile(r"powered by (chatgpt|gpt-4|claude|gemini)", re.IGNORECASE),
)

EMOJI_PATTERN = re.compile(
    "[\U0001F300-\U0001FAFF☀-➿⬀-⯿✅❌✨❗⭐]"
)

ENTHUSIASM_PATTERN = re.compile(
    r"blazingly fast|seamlessly|robust and scalable|cutting[- ]edge|revolutioniz|effortlessly|supercharge|game[- ]chang",
    re.IGNORECASE,
)

LLM_LEXICON_PATTERN = re.compile(
    r"\b(delve|delving|delves|showcas(?:e|es|ing)|pivotal|intricate|meticulous(?:ly)?|realm"
    r"|bolster(?:s|ing)?|garnered|underpins|seamless(?:ly)?|leverag(?:e|es|ing)|streamlin(?:e|es|ing)"
    r"|elevat(?:e|es|ing)|holistic|robust and|comprehensive suite|game[- ]chang(?:er|ing)"
    r"|it'?s (?:important|worth) (?:to note|noting)|keep in mind|as you can see|in this example)\b",
    re.IGNORECASE,
)
LLM_LEXICON_THRESHOLD: int = 3
EM_DASH_THRESHOLD: int = 3

WORD_PATTERN = re.compile(r"[a-z][a-z0-9]+")
NARRATION_MIN_OVERLAP: float = 0.55
NARRATION_MIN_TOKENS: int = 2


def _narration_signals(context: FileContext) -> list[Signal]:
    findings: list[Signal] = []
    line_lookup = {number: text for number, text in context.comments}
    for number, comment in context.comments:
        if number + 1 in line_lookup or number >= len(context.lines):
            continue
        comment_tokens = set(WORD_PATTERN.findall(comment.lower()))
        if len(comment_tokens) < NARRATION_MIN_TOKENS:
            continue
        code_line = context.lines[number].lower()
        code_tokens = set(WORD_PATTERN.findall(code_line))
        if not code_tokens:
            continue
        overlap = len(comment_tokens & code_tokens) / len(comment_tokens)
        if overlap >= NARRATION_MIN_OVERLAP:
            findings.append(
                Signal(
                    code="COMMENT_NARRATION",
                    title="Comment narrates the next line of code",
                    severity=SEVERITY_MEDIUM,
                    axis=AXIS_ORIGIN,
                    weight=2.0,
                    line=number,
                    evidence=comment,
                )
            )
    return findings[:8]


def detect_textual(context: FileContext) -> list[Signal]:
    findings: list[Signal] = []
    for number, raw in enumerate(context.lines, start=1):
        for pattern, title in PLACEHOLDER_PATTERNS:
            if pattern.search(raw):
                findings.append(
                    Signal("PLACEHOLDER_COMMENT", title, SEVERITY_STRONG, AXIS_QUALITY, 4.0, number, raw.strip())
                )
                break
        for pattern in AI_SIGNATURE_PATTERNS:
            if pattern.search(raw):
                findings.append(
                    Signal(
                        "AI_SIGNATURE",
                        "AI tool self-attribution signature",
                        SEVERITY_STRONG,
                        AXIS_ORIGIN,
                        4.0,
                        number,
                        raw.strip(),
                    )
                )
                break
    for number, comment in context.comments:
        for pattern in PROMPT_LEAK_PATTERNS:
            if pattern.search(comment):
                findings.append(
                    Signal(
                        "PROMPT_LEAK",
                        "Assistant conversation leakage in committed source",
                        SEVERITY_STRONG,
                        AXIS_QUALITY,
                        4.0,
                        number,
                        comment,
                    )
                )
                break
    emoji_hits = [(number, comment) for number, comment in context.comments if EMOJI_PATTERN.search(comment)]
    if len(emoji_hits) >= 2:
        number, comment = emoji_hits[0]
        findings.append(
            Signal(
                "EMOJI_COMMENTS",
                f"Emoji used in {len(emoji_hits)} comments",
                SEVERITY_MEDIUM,
                AXIS_ORIGIN,
                1.0,
                number,
                comment,
            )
        )
    enthusiasm_hits = [(number, comment) for number, comment in context.comments if ENTHUSIASM_PATTERN.search(comment)]
    if enthusiasm_hits:
        number, comment = enthusiasm_hits[0]
        findings.append(
            Signal(
                "MARKETING_TONE",
                "Marketing-style enthusiasm in comments",
                SEVERITY_WEAK,
                AXIS_QUALITY,
                1.0,
                number,
                comment,
            )
        )
    comment_text = " ".join(comment for _, comment in context.comments)
    lexicon_hits = {hit.lower() if isinstance(hit, str) else hit for hit in LLM_LEXICON_PATTERN.findall(comment_text)}
    if len(lexicon_hits) >= LLM_LEXICON_THRESHOLD:
        findings.append(
            Signal(
                "LLM_LEXICON",
                f"{len(lexicon_hits)} distinct LLM signature words in comments",
                SEVERITY_MEDIUM,
                AXIS_ORIGIN,
                2.0,
                context.comments[0][0] if context.comments else 1,
                ", ".join(sorted(str(hit) for hit in lexicon_hits)[:6]),
            )
        )
    em_dashes = comment_text.count("\u2014")
    if em_dashes >= EM_DASH_THRESHOLD:
        findings.append(
            Signal(
                "EM_DASH_OVERUSE",
                f"{em_dashes} em dashes in comments, a strong LLM prose habit",
                SEVERITY_MEDIUM,
                AXIS_ORIGIN,
                1.0,
                context.comments[0][0] if context.comments else 1,
                f"{em_dashes} em dashes",
            )
        )
    findings.extend(_narration_signals(context))
    return findings
