#!/usr/bin/env python3
"""
Random MOTD: shows a quote (with author if available) in a
cowsay-style bubble, colorized with a lolcat-style rainbow gradient.
Only depends on the stdlib -> no need to install cowsay/lolcat.

Expected format in quotes.txt: one quote per line, "title|author" or
just "title" if the author is unknown.

Typical setup
mkdir -p /opt/motd
copy motd.py znd quotes.txt to /opt/motd
vi /etc/profile.d/quote-motd.sh
-- content
#!/bin/sh
# only for interactive shells
case "$-" in
    *i*) python3 /opt/motd/motd.py ;;
esac
-- end content
chmod +x /etc/profile.d/quote-motd.sh

Usage:
   python3 motd.py [path/to/quotes.txt]
"""
import colorsys
import os
import random
import sys
import textwrap

QUOTES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "quotes.txt")
BUBBLE_WIDTH = 40

COW = r"""
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
"""


def pick_quote(path):
   with open(path, encoding="utf-8") as f:
      raw_lines = [l.strip() for l in f if l.strip()]
   if not raw_lines:
      return "...", None

   line = random.choice(raw_lines)
   if "|" in line:
      quote, author = line.split("|", 1)
      return quote.strip(), author.strip()
   return line, None


def build_bubble(quote, author, width=BUBBLE_WIDTH):
   lines = textwrap.wrap(f'"{quote}"', width=width) or ['""']
   if author:
      lines.append("")
      lines.append(f"-{author}")

   inner = max(len(l) for l in lines)

   padded = [l.ljust(inner) for l in lines]
   if author:
      padded[-1] = lines[-1].rjust(inner)  # right-align the author line

   top = " " + "_" * (inner + 2)
   bottom = " " + "-" * (inner + 2)

   body = []
   if len(padded) == 1:
      body.append(f"< {padded[0]} >")
   else:
      body.append(f"/ {padded[0]} \\")
      for l in padded[1:-1]:
         body.append(f"| {l} |")
      body.append(f"\\ {padded[-1]} /")

   return "\n".join([top] + body + [bottom]) + COW


def lolcat(text, freq=0.25, spread=6.0, arc_degrees=(90, 200)):
   # same diagonal position per character as before (x/spread + y*freq),
   # but instead of cycling endlessly through every hue, it is mapped onto
   # a random, bounded arc between a random start and end color
   lines = text.split("\n")
   positions = [
      x / spread + y * freq
      for y, line in enumerate(lines)
      for x, ch in enumerate(line)
      if not ch.isspace()
   ]
   pos_min = min(positions, default=0.0)
   pos_span = (max(positions, default=1.0) - pos_min) or 1.0

   hue_start = random.uniform(0, 1)
   arc = random.uniform(*arc_degrees) / 360.0 * random.choice((1, -1))

   out_lines = []
   for y, line in enumerate(lines):
      chars = []
      for x, ch in enumerate(line):
         if ch.isspace():
            chars.append(ch)
            continue
         t = (x / spread + y * freq - pos_min) / pos_span
         hue = (hue_start + t * arc) % 1.0
         r, g, b = colorsys.hsv_to_rgb(hue, 0.85, 1.0)
         chars.append(f"\x1b[38;2;{int(r * 255)};{int(g * 255)};{int(b * 255)}m{ch}")
      chars.append("\x1b[0m")
      out_lines.append("".join(chars))
   return "\n".join(out_lines)


def main():
   path = sys.argv[1] if len(sys.argv) > 1 else QUOTES_FILE
   quote, author = pick_quote(path)
   bubble = build_bubble(quote, author)
   print(lolcat(bubble))


if __name__ == "__main__":
   main()
