#!/usr/bin/env python3
"""
Glass by Unrivaled - rebrand overlay for Postiz (upstream v1.47.0, AGPL-3.0).

Applies user-facing branding only. Does NOT rename code symbols, imports,
package names, or env keys. Upstream copyright and LICENSE are untouched.

Every anchor is required: if an upstream bump moves a string, this script
exits non-zero and the image build FAILS rather than producing a silently
half-branded Glass. That is the point - re-applying is mechanical, and
drift is loud.
"""
import os, re, sys, shutil

FRONTEND = "/app/apps/frontend"
SRC      = FRONTEND + "/src"
PUBLIC   = FRONTEND + "/public"
ASSETS   = "/glass-assets"

BRAND = "Glass by Unrivaled"
SHORT = "Glass"

touched, problems = [], []

def read(p):  return open(p, encoding="utf-8").read()
def write(p, s):
    open(p, "w", encoding="utf-8").write(s)
    if p not in touched: touched.append(p)

def sub_required(path, pattern, repl, label, flags=0):
    """Regex-replace; fail loudly if the anchor is gone."""
    if not os.path.exists(path):
        problems.append("MISSING FILE: %s (%s)" % (path, label)); return
    s = read(path)
    new, n = re.subn(pattern, repl, s, flags=flags)
    if n == 0:
        problems.append("ANCHOR NOT FOUND: %s in %s" % (label, path)); return
    if new != s: write(path, new)

# ---------------------------------------------------------------- 1. TITLES
# The generic  ${isGeneralServerSide() ? 'Postiz' : 'Gitroom'}  token.
TERNARY = r"\$\{\s*isGeneralServerSide\(\)\s*\?\s*'Postiz'\s*:\s*'Gitroom'\s*\}"
# Two one-off shapes that bake the page name into both branches.
ODDBALLS = [
    (r"\$\{\s*isGeneralServerSide\(\)\s*\?\s*'Postiz Calendar'\s*:\s*'Gitroom Launches'\s*\}",
     BRAND + " - Calendar"),
    (r"\$\{\s*isGeneralServerSide\(\)\s*\?\s*'Postiz Integrations'\s*:\s*'Gitroom Integrations'\s*\}",
     BRAND + " - Integrations"),
]

title_files = []
for root, _, files in os.walk(SRC + "/app"):
    for f in files:
        if f.endswith((".tsx", ".ts")):
            title_files.append(os.path.join(root, f))

hits = 0
for p in title_files:
    s = orig = read(p)
    for pat, rep in ODDBALLS:
        s = re.sub(pat, rep, s)
    s = re.sub(TERNARY, BRAND, s)
    s = s.replace("title: 'Postiz - Agent'", "title: '%s - Agent'" % BRAND)
    if s != orig:
        write(p, s); hits += 1
if hits == 0:
    problems.append("no title files changed - upstream title shape moved")

# Logged-out surfaces must read EXACTLY the app name, no page suffix.
EXACT = ["auth/page.tsx", "auth/login/page.tsx", "auth/forgot/page.tsx",
         "auth/forgot/[token]/page.tsx", "auth/activate/page.tsx",
         "auth/activate/[code]/page.tsx"]
for rel in EXACT:
    p = os.path.join(SRC, "app", "(app)", rel)
    if not os.path.exists(p):
        problems.append("MISSING auth page: %s" % p); continue
    s = read(p)
    new = re.sub(r"title:\s*`" + re.escape(BRAND) + r"[^`]*`",
                 "title: `%s`" % BRAND, s)
    if new == s and ("title: `%s`" % BRAND) not in s:
        problems.append("could not force exact title in %s" % p)
    if new != s: write(p, new)

# Every other titled page becomes "Glass by Unrivaled - <Page>".
for p in title_files:
    if any(p.endswith(os.path.join("(app)", r).replace("/", os.sep)) for r in EXACT):
        continue
    s = read(p)
    new = re.sub(r"title:\s*`" + re.escape(BRAND) + r" (?!- )([^`]+)`",
                 lambda m: "title: `%s - %s`" % (BRAND, m.group(1)), s)
    if new != s: write(p, new)

# ------------------------------------------------------- 2. DISPLAY STRINGS
# Capitalised product names only. Import specifiers are lowercase
# (@gitroom/...), so they are untouched by construction.
for root, _, files in os.walk(SRC):
    for f in files:
        if not f.endswith((".tsx", ".ts")): continue
        p = os.path.join(root, f)
        s = orig = read(p)
        s = s.replace("Postiz", SHORT).replace("Gitroom", SHORT)
        if s != orig: write(p, s)

# ------------------------------------------------------------- 3. THE LOGO
LOGO = os.path.join(SRC, "components", "ui", "logo-text.component.tsx")
if not os.path.exists(LOGO):
    problems.append("MISSING logo component: %s" % LOGO)
else:
    write(LOGO, '''import React from 'react';

// Glass by Unrivaled - replaces the upstream inline "Postiz" wordmark.
export const LogoTextComponent = () => {
  return (
    <div
      style={{
        display: 'flex',
        alignItems: 'center',
        gap: '10px',
        lineHeight: 1,
      }}
    >
      <img
        src="/glass-mark.png"
        alt="Glass by Unrivaled"
        width={22}
        height={33}
        style={{ display: 'block' }}
      />
      <span
        style={{
          fontSize: '21px',
          fontWeight: 600,
          letterSpacing: '0.2px',
          color: 'currentColor',
        }}
      >
        Glass
      </span>
    </div>
  );
};
''')

# The preview page points at the old raster mark.
sub_required(os.path.join(SRC, "app", "(app)", "(preview)", "p", "[id]", "page.tsx"),
             r"'/postiz\.svg'", "'/glass-mark.png'", "preview logo src")

# ----------------------------------------------------------------- 4. ICONS
pairs = [("glass-mark.png", "glass-mark.png"),
         ("favicon.ico",    "favicon.ico"),
         ("glass-tile-512.png", "favicon.png"),
         ("glass-tile-512.png", "postiz-fav.png")]  # filename kept: still referenced
for src_name, dst_name in pairs:
    s, d = os.path.join(ASSETS, src_name), os.path.join(PUBLIC, dst_name)
    if not os.path.exists(s):
        problems.append("MISSING asset: %s" % s); continue
    shutil.copyfile(s, d); touched.append(d)

# ------------------------------------------- 4b. TRUTHFULNESS + COMPLIANCE
# Upstream's auth page carries marketing that becomes FALSE once rebranded:
#   - "Over 20,000+ Entrepreneurs use <product>" - untrue of Glass
#   - a carousel of attributed testimonials from named, photographed people
#     endorsing Postiz - rewording those would fabricate endorsements for a
#     product those people never used.
# Both are removed rather than reworded.
AUTH_LAYOUT = os.path.join(SRC, "app", "(app)", "auth", "layout.tsx")
sub_required(AUTH_LAYOUT,
             r'\n\s*<div className="text-\[36px\] flex-1 pt-\[88px\][^>]*>.*?<TestimonialComponent />\s*</div>',
             "", "auth marketing panel (20,000+ claim + testimonials)",
             flags=re.DOTALL)

# Belt and braces: blank the testimonial data itself, in case it is rendered
# anywhere else. This file lives outside apps/frontend/src, so the display
# string pass above never reached it.
TESTI = "/app/libraries/react-shared-libraries/src/helpers/testomonials.tsx"
if not os.path.exists(TESTI):
    problems.append("MISSING testimonials helper: %s" % TESTI)
else:
    write(TESTI, """import React from 'react';

// Glass by Unrivaled: upstream shipped attributed testimonials from named,
// photographed individuals endorsing Postiz. Rebranding them would fabricate
// endorsements for a product those people never used, so they are removed.
export const testimonials1: any[] = [];
export const testimonials2: any[] = [];
""")

# Legal links must point at Glass's own published policies, not upstream's.
REGISTER = os.path.join(SRC, "components", "auth", "register.tsx")
sub_required(REGISTER, r"https://postiz\.com/terms",
             "https://unrivaledmarketingsolutions.com/terms", "terms link")
sub_required(REGISTER, r"https://postiz\.com/privacy",
             "https://unrivaledmarketingsolutions.com/privacy", "privacy link")

# AGPL-3.0 s.13: users interacting over a network must be offered the
# Corresponding Source. Served by Caddy from /opt/glass-source on this host.
APP_LAYOUT = os.path.join(SRC, "app", "(app)", "layout.tsx")
SOURCE_LINK = """        <a
          href="/source/"
          target="_blank"
          rel="noopener noreferrer"
          title="Corresponding Source for this modified AGPL-3.0 work"
          style={{
            position: 'fixed',
            right: '10px',
            bottom: '8px',
            zIndex: 2147483000,
            fontSize: '11px',
            opacity: 0.5,
            color: 'currentColor',
            textDecoration: 'underline',
          }}
        >
          Source
        </a>
      </body>"""
sub_required(APP_LAYOUT, r"\n      </body>", "\n" + SOURCE_LINK, "source link anchor")


# ----------------------------------------------------------------- 5. VERIFY
leftovers = []
for base in (SRC, '/app/libraries/react-shared-libraries/src'):
  for root, _, files in os.walk(base):
      for f in files:
          if not f.endswith((".tsx", ".ts")): continue
          p = os.path.join(root, f)
          for i, line in enumerate(read(p).splitlines(), 1):
              if ("Postiz" in line or "Gitroom" in line):
                  leftovers.append("%s:%d: %s" % (p, i, line.strip()[:110]))

print("=== Glass rebrand overlay ===")
print("files touched: %d" % len(set(touched)))
for t in sorted(set(touched)): print("  " + t.replace(FRONTEND + "/", ""))
if leftovers:
    print("\n!! residual Postiz/Gitroom in source (%d):" % len(leftovers))
    for l in leftovers[:25]: print("   " + l)
if problems:
    print("\n!! PROBLEMS (%d):" % len(problems))
    for p_ in problems: print("   " + p_)
    sys.exit(1)
print("\nrebrand OK")
