# retoor <retoor@molodetz.nl>

import re

from devplacepy.config import (
    AQUALITY_NEWS_GRADING_MODEL,
    AQUALITY_NEWS_GRADING_URL,
    INTERNAL_GATEWAY_URL,
    INTERNAL_MODEL,
)

NEWS_API_URL_DEFAULT = "https://news.app.molodetz.nl/api"
AQUALITY_URL_DEFAULT = AQUALITY_NEWS_GRADING_URL
AQUALITY_MODEL_DEFAULT = AQUALITY_NEWS_GRADING_MODEL
AI_URL_DEFAULT = AQUALITY_URL_DEFAULT
AI_MODEL_DEFAULT = AQUALITY_MODEL_DEFAULT
FORMAT_URL_DEFAULT = INTERNAL_GATEWAY_URL
FORMAT_MODEL_DEFAULT = INTERNAL_MODEL
GRADE_THRESHOLD_DEFAULT = 7
GRADE_MAX_TOKENS = 2000
FORMAT_MAX_TOKENS = 6000
FORMAT_INPUT_MAX_CHARS = 14000
FORMAT_OUTPUT_MAX_CHARS = 30000

MIN_TITLE_CHARS = 12
MIN_BODY_CHARS = 200
MAX_TITLE_CAPS_RATIO = 0.6
MIN_IMAGE_DIMENSION = 100
PHASH_DISTANCE = 5
IMG_PER_ARTICLE = 5
IMG_FETCH_MAX_BYTES = 5_000_000
IMG_FETCH_TIMEOUT = 8.0
UNIQUE_IMAGE_BONUS = 2
THIN_CONTENT_PENALTY = 2
FEATURE_MIN_SCORE = 8
LANDING_MIN_SCORE = 9
LANDING_MAX = 6
LANDING_RECENCY_DAYS = 4

JUNK_PATTERNS = (
    re.compile(r"submitted by\s+/?u/\S+(?:\s+to\s+/?r/\S+)?", re.IGNORECASE),
    re.compile(r"submitted by\s+\S+\s+to\s+/?r/\S+", re.IGNORECASE),
    re.compile(r"\[\s*link\s*\]", re.IGNORECASE),
    re.compile(r"\[\s*\d*\s*comments?\s*\]", re.IGNORECASE),
    re.compile(r"submitted by[^\[]*\[link\]\s*\[comments?\]", re.IGNORECASE),
)
WHITESPACE_PATTERN = re.compile(r"\s+")

NEWS_SUMMARY = (
    "Fully automatic, zero-maintenance news pipeline: fetches the feed, cleans "
    "each article, fetches and perceptually compares images to reject "
    "placeholders, grades deterministically, and auto-rotates Featured and "
    "Landing. The full grading algorithm is detailed below."
)

GRADE_PROMPT_SPEC = (
    "You are an expert content strategist and editor for DevPlace, a "
    "server-rendered social network for software developers. Decide how "
    "strongly a given article should be published, expressed as a single "
    "grade.\n\n"
    "Site context:\n"
    "- Audience: software developers, hobbyists and professionals, "
    "interested in software development, AI agents, open-source tools, "
    "systems programming, and self-hosting.\n"
    "- Mission: deliver deep technical insight, critical analysis, and "
    "practical value; build authority and reader trust; avoid low-effort, "
    "rehashed, or clickbait content.\n"
    "- Tone: technical, practical, nuanced, skeptical of hype; no "
    "sensationalism or unverified claims.\n\n"
    "Evaluate the article against these weighted criteria and weigh them "
    "internally:\n"
    "1. Relevance and alignment to the developer niche and audience needs "
    "(20%).\n"
    "2. Quality and accuracy: depth, originality, factual correctness, "
    "sourcing, technical correctness, clear writing (25%).\n"
    "3. Engagement and readability: strong title/intro/flow, useful "
    "examples, appropriate length (15%).\n"
    "4. Originality and uniqueness: new perspective, data, or analysis; not "
    "duplicated generic web content (15%).\n"
    "5. SEO and discoverability: keyword opportunity without stuffing, "
    "shareable (10%).\n"
    "6. Ethics, legality and risk: no misinformation, plagiarism, or "
    "brand-safety issues; controversial topics handled with nuance and "
    "evidence (10%).\n"
    "7. Strategic fit for the site (5%).\n\n"
    "Convert your overall judgment to a single integer grade from 1 to 10:\n"
    "- 9-10: excellent, publish as-is.\n"
    "- 7-8: good, worth publishing.\n"
    "- 5-6: marginal, weak fit or quality.\n"
    "- 1-4: reject, low quality or off-topic.\n\n"
    "Return ONLY a single integer from 1 to 10, nothing else."
)

FORMAT_PROMPT_SPEC = (
    "You are an expert editor for DevPlace, a social network for software "
    "developers. You are given the raw body of a news article that arrived as "
    "an undifferentiated wall of text. Reformat it into clean, readable "
    "Markdown for a technical audience.\n\n"
    "Rules:\n"
    "- Break the text into short, well-structured paragraphs separated by a "
    "blank line.\n"
    "- Add Markdown section headings (## Heading) where the topic clearly "
    "shifts, so the article scans well.\n"
    "- Use bullet or numbered lists for enumerations, and inline code or "
    "fenced code blocks where code, commands, or identifiers appear.\n"
    "- Preserve every fact, name, number, and quotation exactly as given. "
    "Never invent, add, remove, or reorder information.\n"
    "- Only restructure and lightly polish wording for flow and grammar; do "
    "not add an introduction, conclusion, opinion, or commentary of your "
    "own.\n"
    "- Do not repeat the article title as a heading and do not wrap the whole "
    "answer in a code fence.\n"
    "- Output only the reformatted article body as Markdown, nothing else."
)

GRADING_RULES_DESCRIPTION = (
    "Each run fetches the feed, cleans every article, fetches and perceptually "
    "compares images, grades deterministically, and auto-rotates Featured and "
    "Landing.\n\n"
    "Content cleaning: after HTML stripping it removes Reddit boilerplate "
    "(submitted by /u/<user>, submitted by ... to /r/<sub>, standalone [link], "
    "[comments], [N comments] and trailing submitted-by [link] [comments]) and "
    "collapses whitespace, applied to title (light), description and content "
    "before grading and before storage.\n\n"
    "Reliability gate (forces draft, never Featured or Landing) when: the url is "
    f"empty or invalid; the cleaned title is shorter than {MIN_TITLE_CHARS} chars; "
    f"the combined cleaned body is shorter than {MIN_BODY_CHARS} chars; or the "
    f"title uppercase-letter ratio exceeds {MAX_TITLE_CAPS_RATIO}.\n\n"
    "Image uniqueness: up to "
    f"{IMG_PER_ARTICLE} candidate images per article are fetched (max "
    f"{IMG_FETCH_MAX_BYTES} bytes), decoded and perceptually hashed (phash). An "
    f"image under {MIN_IMAGE_DIMENSION}px on either side, or one that fails to "
    "fetch or decode, is a placeholder. Hashes within Hamming distance "
    f"{PHASH_DISTANCE} that span two or more different articles are a shared "
    "logo or placeholder and are rejected for all of them. An article with at "
    "least one genuinely unique image is marked has_unique_image and its first "
    "such image becomes the primary image.\n\n"
    "Grading prompt: the exact rubric is the editable news_grade_prompt field "
    "(Configuration tab), sent to the model at temperature 0 with the cleaned "
    "Title, Description and Content appended; it must return a single integer "
    "from 1 to 10.\n\n"
    "Formatting: after grading, every article that clears the publish "
    "threshold (i.e. will be stored as published, not draft) is reformatted "
    "by the AI into clean Markdown (paragraphs, section headings, lists and "
    "code spans) using the editable news_format_prompt field, preserving "
    "every fact while turning the source wall of text into a readable "
    "article. Draft-bound articles are never sent for formatting. The "
    "formatted Markdown replaces the stored content; on any failure the "
    "cleaned original is kept. Disable with the news_format_enabled "
    "toggle.\n\n"
    "Scoring: the AI grade (1-10, temperature 0) is the base. effective_score = "
    f"clamp(ai_grade + {UNIQUE_IMAGE_BONUS} if unique image - "
    f"{THIN_CONTENT_PENALTY} if the body is marginal but not gated, 1..10). The "
    "effective score is stored in grade (so the listing reflects final quality) "
    "and the raw AI grade in ai_grade.\n\n"
    "Promotion: an article is published when valid and effective_score is at or "
    "above the grade threshold. It is Featured when published, has a unique "
    f"image and scores at least {FEATURE_MIN_SCORE}. After the run the service "
    "rotates Landing: the top "
    f"{LANDING_MAX} published, featured, unique-image articles from the last "
    f"{LANDING_RECENCY_DAYS} days scoring at least {LANDING_MIN_SCORE} are shown "
    "on the landing page and the rest of the service-managed set is cleared. "
    "Rows an admin has manually toggled are locked (featured_locked or "
    "landing_locked) and the service never auto-manages them again."
)
