#!/usr/bin/env python3

import re
from datetime import datetime
from pathlib import Path

import pymysql


BASE_DIR = Path(__file__).resolve().parent.parent
DB_CONFIG = BASE_DIR / "config" / "database.php"

# ============================================================
# V1 DECISION SETTINGS
# ============================================================

MAX_STORIES = 100

# Story age rules
FRESH_MINUTES = 30
NORMAL_MAX_HOURS = 12
STALE_HOURS = 24

# Confidence
CONFIDENCE_READY = 60.0

# V1.2:
# Single-source stories may collect importance signals,
# but they are NOT allowed to become READY by themselves.
# Independent confirmation is required for READY.

# Empty / useless content
MIN_TEXT_LENGTH = 12


# ============================================================
# DB
# ============================================================

def load_db_config():

    content = DB_CONFIG.read_text(
        encoding="utf-8",
        errors="ignore"
    )

    patterns = {
        "host": r"\$dbHost\s*=\s*['\"]([^'\"]+)['\"]",
        "database": r"\$dbName\s*=\s*['\"]([^'\"]+)['\"]",
        "user": r"\$dbUser\s*=\s*['\"]([^'\"]+)['\"]",
        "password": r"\$dbPass\s*=\s*['\"]([^'\"]*)['\"]",
    }

    result = {}

    for key, pattern in patterns.items():

        match = re.search(pattern, content)

        if not match:
            raise RuntimeError(
                f"Could not read DB config: {key}"
            )

        result[key] = match.group(1)

    result["charset"] = "utf8mb4"

    return result


# ============================================================
# HELPERS
# ============================================================

def clean_text(text):

    text = text or ""

    text = re.sub(
        r"@[A-Za-z0-9_]+",
        " ",
        text
    )

    text = re.sub(
        r"https?://\S+|www\.\S+",
        " ",
        text
    )

    text = (
        text
        .replace("\u200c", " ")
        .replace("ي", "ی")
        .replace("ك", "ک")
    )

    text = re.sub(
        r"\s+",
        " ",
        text
    ).strip()

    return text


def age_minutes(dt):

    if not dt:
        return 999999

    delta = datetime.now() - dt

    return max(
        0,
        delta.total_seconds() / 60
    )


# ============================================================
# STORY DECISION
# ============================================================

def decide(story):

    reasons = []

    source_count = int(
        story["real_source_count"] or 0
    )

    item_count = int(
        story["item_count"] or 0
    )

    confidence = float(
        story["confidence_score"] or 0
    )

    max_priority = float(
        story["max_priority"] or 1
    )

    total_views = int(
        story["total_views"] or 0
    )

    headline = clean_text(
        story["headline"]
    )

    primary_text = clean_text(
        story["primary_text"]
    )

    age = age_minutes(
        story["last_seen_at"]
    )

    # --------------------------------------------------------
    # 1) INVALID / EMPTY
    # --------------------------------------------------------

    if (
        len(headline) < MIN_TEXT_LENGTH
        and len(primary_text) < MIN_TEXT_LENGTH
    ):
        return {
            "decision": "IGNORE",
            "importance": 0,
            "confidence": confidence,
            "reasons": ["empty-content"]
        }

    if headline.startswith("News Story #"):
        return {
            "decision": "IGNORE",
            "importance": 0,
            "confidence": confidence,
            "reasons": ["fallback-headline"]
        }

    # --------------------------------------------------------
    # 2) IMPORTANCE SCORE
    # --------------------------------------------------------

    importance = 20.0

    # Independent source confirmation
    if source_count >= 2:
        importance += 20
        reasons.append(
            f"{source_count}-sources"
        )

    if source_count >= 3:
        importance += 10

    # More reports / updates inside same story
    if item_count >= 2:
        importance += 8
        reasons.append(
            f"{item_count}-items"
        )

    if item_count >= 4:
        importance += 7

    # Priority source
    if max_priority > 1:
        bonus = min(
            15,
            (max_priority - 1) * 10
        )

        importance += bonus

        reasons.append(
            f"priority-{max_priority:.2f}"
        )

    # Views are only a secondary signal.
    if total_views >= 1000:
        importance += 5
        reasons.append("views-1k")

    if total_views >= 5000:
        importance += 5
        reasons.append("views-5k")

    if total_views >= 20000:
        importance += 5
        reasons.append("views-20k")

    # Freshness
    if age <= FRESH_MINUTES:
        importance += 10
        reasons.append("fresh")

    elif age <= NORMAL_MAX_HOURS * 60:
        importance += 5
        reasons.append("recent")

    # Cap
    importance = min(
        100.0,
        importance
    )

    # --------------------------------------------------------
    # 3) DECISION
    # --------------------------------------------------------

    # Very old story
    if age >= STALE_HOURS * 60:

        return {
            "decision": "IGNORE",
            "importance": importance,
            "confidence": confidence,
            "reasons": reasons + ["stale"]
        }

    # Multiple independent sources:
    # this is strong enough for V1 READY.
    if (
        source_count >= 2
        and confidence >= CONFIDENCE_READY
    ):

        return {
            "decision": "READY",
            "importance": importance,
            "confidence": confidence,
            "reasons": reasons + [
                "multi-source-confirmed"
            ]
        }

    # V1.2:
    # A single source is never enough to make a story READY.
    #
    # Repeated posts from the same source, source priority,
    # high views and freshness still affect IMPORTANCE,
    # but they are not independent confirmation.
    #
    # The story remains WAIT until another independent
    # source is attached by the Story Engine.
    if source_count == 1:
        reasons.append("single-source-unconfirmed")

    return {
        "decision": "WAIT",
        "importance": importance,
        "confidence": confidence,
        "reasons": reasons + [
            "needs-more-signal"
        ]
    }


# ============================================================
# LOAD STORIES
# ============================================================

def load_stories(db):

    sql = """
    SELECT
        st.id,
        st.headline,
        st.story_type,
        st.status,
        st.confidence_score,
        st.first_seen_at,
        st.last_seen_at,

        COUNT(DISTINCT si.id)
            AS item_count,

        COUNT(DISTINCT si.source_id)
            AS real_source_count,

        COALESCE(MAX(ns.priority), 1)
            AS max_priority,

        COALESCE(SUM(fi.source_views), 0)
            AS total_views,

        MAX(
            CASE
                WHEN si.is_primary = 1
                THEN fi.text_content
                ELSE NULL
            END
        ) AS primary_text

    FROM news_stories st

    LEFT JOIN news_story_items si
        ON si.story_id = st.id

    LEFT JOIN news_feed_items fi
        ON fi.id = si.feed_item_id

    LEFT JOIN news_sources ns
        ON ns.source_id = si.source_id

    WHERE st.status = 'collecting'

    GROUP BY
        st.id,
        st.headline,
        st.story_type,
        st.status,
        st.confidence_score,
        st.first_seen_at,
        st.last_seen_at

    ORDER BY st.last_seen_at DESC

    LIMIT %s
    """

    with db.cursor() as cursor:

        cursor.execute(
            sql,
            (MAX_STORIES,)
        )

        return cursor.fetchall()


# ============================================================
# DRY RUN
# ============================================================

def main():

    config = load_db_config()

    config["cursorclass"] = (
        pymysql.cursors.DictCursor
    )

    db = pymysql.connect(
        **config
    )

    try:

        stories = load_stories(db)

        print()
        print(
            "NEWS DECISION ENGINE V1 — DRY RUN"
        )
        print("=" * 100)
        print(
            f"Stories evaluated: {len(stories)}"
        )
        print("=" * 100)

        counts = {
            "READY": 0,
            "WAIT": 0,
            "IGNORE": 0
        }

        for story in stories:

            result = decide(story)

            counts[result["decision"]] += 1

            headline = clean_text(
                story["headline"]
            )[:110]

            print()
            print(
                f"STORY #{story['id']} "
                f"| {result['decision']}"
            )

            print(
                f"Sources: "
                f"{story['real_source_count']} "
                f"| Items: "
                f"{story['item_count']} "
                f"| Views: "
                f"{story['total_views']}"
            )

            print(
                f"Confidence: "
                f"{result['confidence']:.2f} "
                f"| Importance: "
                f"{result['importance']:.2f}"
            )

            print(
                "Reasons: "
                + ", ".join(
                    result["reasons"]
                )
            )

            print(
                f"Headline: {headline}"
            )

        print()
        print("=" * 100)
        print("SUMMARY")
        print("=" * 100)

        for key in (
            "READY",
            "WAIT",
            "IGNORE"
        ):
            print(
                f"{key}: {counts[key]}"
            )

        print()
        print(
            "DRY RUN ONLY — DATABASE NOT MODIFIED ✅"
        )

    finally:

        db.close()


if __name__ == "__main__":
    main()
