"""
Visual effects: noise, glitch, fade, ANSI-aware truncation, firehose, headline pool.
Depends on: config, terminal, sources.

These are low-level functional implementations of visual effects. They are used
internally by the EffectPlugin system (effects_plugins/*.py) and also directly
by layers.py and scroll.py for rendering.

The plugin system provides a higher-level OOP interface with configuration
support, while these legacy functions provide direct functional access.
Both systems coexist - there are no current plans to deprecate the legacy functions.
"""

import random
from datetime import datetime

from engine import config
from engine.sources import FEEDS, POETRY_SOURCES
from engine.terminal import C_DIM, DIM, G_DIM, G_LO, RST, W_GHOST


def noise(w):
    d = random.choice([0.15, 0.25, 0.35, 0.12])
    return "".join(
        f"{random.choice([G_LO, G_DIM, C_DIM, W_GHOST])}"
        f"{random.choice(config.GLITCH + config.KATA)}{RST}"
        if random.random() < d
        else " "
        for _ in range(w)
    )


def glitch_bar(w):
    c = random.choice(["░", "▒", "─", "╌"])
    n = random.randint(3, w // 2)
    o = random.randint(0, w - n)
    return " " * o + f"{G_LO}{DIM}" + c * n + RST


def fade_line(s, fade):
    """Dissolve a rendered line by probabilistically dropping characters."""
    if fade >= 1.0:
        return s
    if fade <= 0.0:
        return ""
    result = []
    i = 0
    while i < len(s):
        if s[i] == "\033" and i + 1 < len(s) and s[i + 1] == "[":
            j = i + 2
            while j < len(s) and not s[j].isalpha():
                j += 1
            result.append(s[i : j + 1])
            i = j + 1
        elif s[i] == " ":
            result.append(" ")
            i += 1
        else:
            result.append(s[i] if random.random() < fade else " ")
            i += 1
    return "".join(result)


def vis_trunc(s, w):
    """Truncate string to visual width w, skipping ANSI escape codes."""
    result = []
    vw = 0
    i = 0
    while i < len(s):
        if vw >= w:
            break
        if s[i] == "\033" and i + 1 < len(s) and s[i + 1] == "[":
            j = i + 2
            while j < len(s) and not s[j].isalpha():
                j += 1
            result.append(s[i : j + 1])
            i = j + 1
        else:
            result.append(s[i])
            vw += 1
            i += 1
    return "".join(result)


def vis_offset(s, offset):
    """Offset string by skipping first offset visual characters, skipping ANSI escape codes."""
    if offset <= 0:
        return s
    result = []
    vw = 0
    i = 0
    skipping = True
    while i < len(s):
        if s[i] == "\033" and i + 1 < len(s) and s[i + 1] == "[":
            j = i + 2
            while j < len(s) and not s[j].isalpha():
                j += 1
            if skipping:
                i = j + 1
                continue
            result.append(s[i : j + 1])
            i = j + 1
        else:
            if skipping:
                if vw >= offset:
                    skipping = False
                    result.append(s[i])
                vw += 1
                i += 1
            else:
                result.append(s[i])
                i += 1
    return "".join(result)


def next_headline(pool, items, seen):
    """Pull the next unique headline from pool, refilling as needed."""
    while True:
        if not pool:
            pool.extend(items)
            random.shuffle(pool)
            seen.clear()
        title, src, ts = pool.pop()
        sig = title.lower().strip()
        if sig not in seen:
            seen.add(sig)
            return title, src, ts


def firehose_line(items, w):
    """Generate one line of rapidly cycling firehose content."""
    r = random.random()
    if r < 0.35:
        # Raw headline text
        title, src, ts = random.choice(items)
        text = title[: w - 1]
        color = random.choice([G_LO, G_DIM, W_GHOST, C_DIM])
        return f"{color}{text}{RST}"
    elif r < 0.55:
        # Dense glitch noise
        d = random.choice([0.45, 0.55, 0.65, 0.75])
        return "".join(
            f"{random.choice([G_LO, G_DIM, C_DIM, W_GHOST])}"
            f"{random.choice(config.GLITCH + config.KATA)}{RST}"
            if random.random() < d
            else " "
            for _ in range(w)
        )
    elif r < 0.78:
        # Status / program output
        sources = FEEDS if config.MODE == "news" else POETRY_SOURCES
        src = random.choice(list(sources.keys()))
        msgs = [
            f"  SIGNAL :: {src} :: {datetime.now().strftime('%H:%M:%S.%f')[:-3]}",
            f"  ░░ FEED ACTIVE :: {src}",
            f"  >> DECODE 0x{random.randint(0x1000, 0xFFFF):04X} :: {src[:24]}",
            f"  ▒▒ ACQUIRE :: {random.choice(['TCP', 'UDP', 'RSS', 'ATOM', 'XML'])} :: {src}",
            f"  {''.join(random.choice(config.KATA) for _ in range(3))} STRM "
            f"{random.randint(0, 255):02X}:{random.randint(0, 255):02X}",
        ]
        text = random.choice(msgs)[: w - 1]
        color = random.choice([G_LO, G_DIM, W_GHOST])
        return f"{color}{text}{RST}"
    else:
        # Headline fragment with glitch prefix
        title, _, _ = random.choice(items)
        start = random.randint(0, max(0, len(title) - 20))
        frag = title[start : start + random.randint(10, 35)]
        pad = random.randint(0, max(0, w - len(frag) - 8))
        gp = "".join(random.choice(config.GLITCH) for _ in range(random.randint(1, 3)))
        text = (" " * pad + gp + " " + frag)[: w - 1]
        color = random.choice([G_LO, C_DIM, W_GHOST])
        return f"{color}{text}{RST}"
