from flask import Flask, request, render_template, session, redirect
import os
import markdown
import bleach
import re
from werkzeug.security import generate_password_hash, check_password_hash
import secrets
from flask import make_response, jsonify
import time
from datetime import datetime

START_TIME = time.time()
# Active sessions will be stored as { session_id: last_activity_timestamp }
ACTIVE_SESSIONS = {}
        
if not os.path.exists("posts"):
    os.mkdir("posts")
if not os.path.exists("users"):
    os.mkdir("users")
    
id = max([int(f) for f in os.listdir("posts/") if f.isdigit()] + [-1]) + 1
app = Flask(__name__)
app.config.update(
    SESSION_COOKIE_NAME="potato_session",
    SESSION_COOKIE_HTTPONLY=True,  # cannot be accessed by JS
    SESSION_COOKIE_SECURE=True,   # set True if using HTTPS
    SESSION_COOKIE_SAMESITE="Lax", # protects against CSRF
    PERMANENT_SESSION_LIFETIME=60*60*24*7  # 7 days
)
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=2, x_proto=1, x_host=1, x_port=1)
app.secret_key = os.environ.get("POTATO_SECRET_KEY") or secrets.token_hex(64)

@app.before_request
def track_activity():
    # Use the session cookie itself or the username as a key
    user = session.get("user") or request.remote_addr
    ACTIVE_SESSIONS[user] = time.time()
    
    # Optional: Clean up sessions older than 15 minutes to keep count accurate
    cutoff = time.time() - (15 * 60)
    expired = [k for k, v in ACTIVE_SESSIONS.items() if v < cutoff]
    for k in expired:
        del ACTIVE_SESSIONS[k]
        
VGA_PALETTE = [
    "#000000", "#0000AA", "#00AA00", "#00AAAA", "#AA0000", "#AA00AA", "#AA5500", "#AAAAAA",
    "#555555", "#5555FF", "#55FF55", "#55FFFF", "#FF5555", "#FF55FF", "#FFFF55", "#FFFFFF"
]

# Add this near the top with other os.mkdir calls
if not os.path.exists("bans"):
    os.mkdir("bans")

@app.before_request
def check_banned():
    ip = request.remote_addr
    if os.path.exists(f"bans/{ip}"):
        return "Your IP address is banned.", 403

def is_admin():
    return session.get("user") == "owner"

def link_pings(content):
    # Matches @ followed by alphanumeric characters
    return re.sub(r'@(\w+)', r'<a href="/user/\1" class="fw-bold">@\1</a>', content)
    
def render_pixel_art(username, size=32):
    path = f"users/{username}.pxl"
    pixels = bytes([77] * 128)
    if os.path.exists(path):
        with open(path, "rb") as f:
            pixels = f.read()

    grid_html = f'<div style="margin-right: 4px; display: inline-grid; vertical-align: middle; grid-template-columns: repeat(16, 1fr); width: {size}px; height: {size}px; border-radius: 4px; overflow: hidden; flex-shrink: 0;">'
    for byte in pixels:
        p1, p2 = (byte >> 4) & 0x0F, byte & 0x0F
        grid_html += f'<div style="background: {VGA_PALETTE[p1]}; aspect-ratio: 1/1;"></div>'
        grid_html += f'<div style="background: {VGA_PALETTE[p2]}; aspect-ratio: 1/1;"></div>'
    grid_html += '</div>'
    return grid_html

def process_custom_syntax(content):
    content = re.sub(r"!audio\[(.*?)\]", r'<audio controls src="\1"></audio>', content)
    content = re.sub(r"!video\[(.*?)\]", r'<video controls src="\1"></video>', content)
    def yt_embed(match):
        link = match.group(1).strip()
        if "youtube.com/watch" in link or "youtu.be" in link:
            if "v=" in link:
                vid = link.split("v=")[1].split("&")[0]
            elif "youtu.be" in link:
                vid = link.split("/")[-1]
            else:
                vid = link
        else:
            vid = link
        return f'<iframe width="560" height="315" src="https://www.youtube.com/embed/{vid}" frameborder="0" allowfullscreen></iframe>'
    content = re.sub(r"!yt\[(.*?)\]", yt_embed, content)
    return content

def link_hashtags(content):
    return re.sub(r"#(\w+)", r"<a href='/hashtag/\1'>#\1</a>", content)

def sanitize_html(html):
    allowed_tags = bleach.sanitizer.ALLOWED_TAGS.union({
        "p","br","hr","h1","h2","h3","h4","h5","h6",
        "ul","ol","li","strong","em","code","pre","blockquote",
        "audio","video","iframe","a","img", "span"
    })
    
    # We allow the 'style' attribute globally for specific tags
    allowed_attrs = {
        "audio": ["src", "controls"],
        "video": ["src", "controls"],
        "iframe": ["src", "width", "height", "frameborder", "allowfullscreen"],
        "a": ["href"],
        "img": ["src", "class", "alt"],
        "span": ["style"] # This allows inline CSS like color
    }

    # Use bleach.clean without the 'styles' keyword to avoid the TypeError
    cleaned = bleach.clean(
        html, 
        tags=allowed_tags, 
        attributes=allowed_attrs,
        strip=True
    )
    
    # Existing YouTube security filter
    cleaned = re.sub(
        r'<iframe[^>]+src="(?!https://www\.youtube\.com/embed/)[^"]*"[^>]*></iframe>',
        "",
        cleaned
    )
    return cleaned

def render_post_html(pid, author, content):
    avatar = render_pixel_art(author, size=24) # Small version for posts
    content = process_custom_syntax(content)
    content = link_hashtags(content)
    content = link_pings(content)
    html = markdown.markdown(content, extensions=["extra","sane_lists"], output_format="html5")
    html = sanitize_html(html)
    
    # --- LIKES LOGIC ---
    likes_file = f"posts/{pid}.likes"
    liked_users = set()
    if os.path.exists(likes_file):
        with open(likes_file, "r") as f:
            liked_users = set(line.strip() for line in f.readlines())
    
    user = session.get("user")
    likes = len(liked_users)
    
    # FIX: Swapped 'btn-light border' for 'btn-outline-primary' to follow theme colors
    if user in liked_users:
        like_btn = f"<a href='/post/unlike/{pid}' class='btn btn-sm btn-outline-danger'>❤️ Unlike</a>"
    else:
        like_btn = f"<a href='/post/like/{pid}' class='btn btn-sm btn-outline-primary'>👍 Like</a>"

    return f'''
        <div class="post-header mb-2">
            {avatar}<a href="/user/{author}" class="text-decoration-none fw-bold" style="color: var(--potato-primary)">@{author}</a>
        </div>
        <div class="post-content mb-3" style="color: var(--potato-text)">
            {html}
        </div>
        <div class="post-footer d-flex justify-content-between align-items-center border-top pt-2" style="border-color: rgba(128,128,128,0.2) !important;">
            <small class="text-muted" style="color: var(--potato-text) !important; opacity: 0.7;">{likes} Likes</small>
            <div>
                {like_btn}
                <a href="/post/view?id={pid}" class="btn btn-sm btn-link" style="color: var(--potato-primary)">View Replies</a>
            </div>
        </div>
    '''
    
def render_reply_html(pid, rid, author, content):
    avatar = render_pixel_art(author, size=24)
    content = process_custom_syntax(content)
    content = link_hashtags(content)
    html = markdown.markdown(content, extensions=["extra","sane_lists"], output_format="html5")
    html = sanitize_html(html)
    likes_file = f"posts/{pid}.replies/{rid}.likes"
    liked_users = set()
    if os.path.exists(likes_file):
        with open(likes_file, "r") as f:
            liked_users = set(line.strip() for line in f.readlines())
    user = session.get("user")
    likes = len(liked_users)
    if user in liked_users:
        like_link = f"<a href='/post/unlike/{pid}/reply/{rid}'>Remove Like</a>"
    else:
        like_link = f"<a href='/post/like/{pid}/reply/{rid}'>Like</a>"
    return f"<div style='margin-left:20px'><b>{avatar}<a href='/user/{author}'>{author}</a></b>: {html}<br>Likes: {likes} {like_link}</div>"

@app.route("/")
def homepage():
    per_page = request.args.get('per_page', default=10, type=int)
    page = request.args.get('page', default=1, type=int)
    order = request.args.get('order', default='recent') # Get sort order
    
    all_post_ids = [int(f) for f in os.listdir("posts/") if f.isdigit()]
    
    # Pre-calculate likes if sorting by likes
    post_data = []
    for pid in all_post_ids:
        likes_file = f"posts/{pid}.likes"
        count = 0
        if os.path.exists(likes_file):
            with open(likes_file, "r") as f:
                count = len(f.readlines())
        post_data.append({'id': pid, 'likes': count})

    # Sort logic
    if order == 'likes':
        # Sort by likes descending, then by ID descending as a tie-breaker
        post_data.sort(key=lambda x: (x['likes'], x['id']), reverse=True)
    else:
        # Default: Sort by ID descending
        post_data.sort(key=lambda x: x['id'], reverse=True)

    sorted_ids = [p['id'] for p in post_data]
    total_posts = len(sorted_ids)
    
    # Handle Pinned Post (stays at the top regardless of sort)
    pinned_html = ""
    if os.path.exists("posts/pinned"):
        try:
            with open("posts/pinned", "r") as f:
                pinned_id = f.read().strip()
            if pinned_id.isdigit() and os.path.exists(f"posts/{pinned_id}"):
                if int(pinned_id) in sorted_ids:
                    sorted_ids.remove(int(pinned_id))
                
                with open(f"posts/{pinned_id}", "r") as f:
                    raw = f.read()
                author, content = raw.split("|", 1) if "|" in raw else ("unknown", raw)
                pinned_html = f'''
                    <div class="card mb-4 border-primary shadow" style="border-left: 5px solid var(--potato-primary);">
                        <div class="card-header bg-transparent border-0 pb-0">
                            <small class="text-primary fw-bold">📌 PINNED POST</small>
                        </div>
                        <div class="card-body">{render_post_html(pinned_id, author, content)}</div>
                    </div>'''
        except Exception:
            pass

    # Pagination
    start_index = (page - 1) * per_page
    end_index = start_index + per_page
    page_post_ids = sorted_ids[start_index:end_index]

    posts = []
    for pid in page_post_ids:
        try:
            with open(f"posts/{pid}", "r") as post:
                raw = post.read()
            author, content = raw.split("|", 1) if "|" in raw else ("unknown", raw)
            posts.append(render_post_html(pid, author, content))
        except (FileNotFoundError, ValueError):
            continue

    user = session.get("user")
    posts_html = "".join([f'<div class="card mb-3 shadow-sm"><div class="card-body">{p}</div></div>' for p in posts])
    
    # Sort Navigation UI
    sort_nav = f'''
        <div class="mb-3 text-end">
            <small class="text-muted">Sort by: 
                <a href="/?order=recent" class="{"fw-bold" if order=='recent' else ""}">Recent</a> | 
                <a href="/?order=likes" class="{"fw-bold" if order=='likes' else ""}">Most Liked</a>
            </small>
        </div>
    '''

    # Pagination UI
    prev_btn = f'<li class="page-item"><a class="page-link" href="/?page={page-1}&order={order}">Previous</a></li>' if page > 1 else ''
    next_btn = f'<li class="page-item"><a class="page-link" href="/?page={page+1}&order={order}">Next</a></li>' if end_index < total_posts else ''
    
    pagination_html = f'''
        <nav aria-label="Page navigation" class="mt-4">
          <ul class="pagination justify-content-center">
            {prev_btn}
            <li class="page-item disabled"><span class="page-link">Page {page}</span></li>
            {next_btn}
          </ul>
        </nav>
    '''
    
    search_bar = '''
        <form action="/search" method="GET" class="mb-4">
            <div class="input-group">
                <input type="text" name="q" class="form-control" placeholder="Search posts, #hashtags or @users..." aria-label="Search">
                <button class="btn btn-primary" type="submit">Search</button>
            </div>
        </form>
    '''

    return render_ui_template("Feed", f"{search_bar}{sort_nav}{pinned_html}{posts_html}{pagination_html}", user)
    
def render_ui_template(title, body_content, user):
    # Retrieve theme colors from cookies, falling back to defaults
    theme = {
        "bg": request.cookies.get("theme_bg", "#f8f9fa"),
        "card": request.cookies.get("theme_card", "#ffffff"),
        "text": request.cookies.get("theme_text", "#212529"),
        "primary": request.cookies.get("theme_primary", "#007bff"),
        "nav": request.cookies.get("theme_nav", "#ffffff")
    }

    auth_links = ""
    if user:
        # Inbox Notification Logic
        unread_total, _ = get_unread_count(user)
        badge = f' <span class="badge bg-danger rounded-pill" style="font-size: 0.7rem; vertical-align: middle;">{unread_total}</span>' if unread_total > 0 else ""
        
        # Admin Link for owner
        if user == "owner":
            auth_links += f'<a class="btn btn-sm btn-danger me-2" href="/admin">Admin</a>'
        
        auth_links += f'<a class="btn btn-sm btn-outline-info me-2" href="/inbox">Inbox{badge}</a>'
        auth_links += f'<a class="btn btn-sm btn-outline-secondary me-2" href="/settings">Settings</a>'
        
        # User branding
        auth_links += f'''
            <span class="navbar-text me-3" style="color: var(--potato-text) !important">
                Logged in as <b><a href="/user/{user}" style="color: var(--potato-primary)">@{user}</a></b>
            </span>'''
        auth_links += '<a class="btn btn-outline-danger btn-sm" href="/logout">Logout</a>'
    else:
        auth_links += '<a class="btn btn-outline-secondary btn-sm me-2" href="/settings">Settings</a>'
        auth_links += '<a class="btn btn-outline-primary btn-sm me-2" href="/login">Login</a>'
        auth_links += '<a class="btn btn-primary btn-sm" href="/register">Register</a>'
        
    return f'''
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{title} - PotatoNetwork</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
        <style>
            :root {{
                --potato-bg: {theme['bg']};
                --potato-card: {theme['card']};
                --potato-text: {theme['text']};
                --potato-primary: {theme['primary']};
                --potato-nav: {theme['nav']};
            }}
            
            /* Global Body & Text Updates */
            body {{ background-color: var(--potato-bg); color: var(--potato-text); min-height: 100vh; }}
            p, li, div, label {{ color: var(--potato-text); }}
            
            /* Fix for Bios and Legal text (Bootstrap overrides) */
            .text-muted, .small, small {{ color: var(--potato-text) !important; opacity: 0.7; }}
            h1, h2, h3, h4, h5, h6, .h4, .navbar-brand {{ color: var(--potato-primary) !important; }}
            
            /* Card & Navbar */
            .card {{ background-color: var(--potato-card); color: var(--potato-text); border: none; border-radius: 12px; }}
            .navbar {{ background: var(--potato-nav) !important; border-bottom: 1px solid rgba(0,0,0,0.1); }}
            .navbar-text {{ color: var(--potato-text) !important; }}
            
            /* Border fixes for dark mode visibility */
            .border-top, .border-bottom, hr {{ border-color: rgba(128, 128, 128, 0.25) !important; }}
            
            /* Button & Link UI */
            .text-primary {{ color: var(--potato-primary) !important; }}
            .btn-primary {{ background-color: var(--potato-primary); border-color: var(--potato-primary); }}
            .btn-outline-primary {{ color: var(--potato-primary); border-color: var(--potato-primary); }}
            
            .post-content img, .post-content video, .post-content iframe {{ max-width: 100%; height: auto; border-radius: 8px; }}
            .post-content iframe {{ aspect-ratio: 16 / 9; }}
            
            a {{ color: var(--potato-primary); text-decoration: none; }}
            a:hover {{ filter: brightness(0.8); }}
        </style>
    </head>
    <body>
        <nav class="navbar sticky-top mb-4 shadow-sm">
            <div class="container">
                <a class="navbar-brand fw-bold" href="/">PotatoNetwork</a>
                <div class="d-flex align-items-center">{auth_links}</div>
            </div>
        </nav>
        <div class="container" style="max-width: 700px;">
            <div class="d-flex justify-content-between align-items-center mb-4">
                <h2 class="h4 mb-0" style="color: var(--potato-text) !important">{title}</h2>
                <a href="/post/new" class="btn btn-primary">Create Post</a>
            </div>
            {body_content}
        </div>
        <footer class="mt-5 pb-4 text-center">
            <small class="text-muted">
                &copy; 2026 PotatoNetwork | <a href="/legal">Terms & Privacy</a> - 
                The real admin is <a href='/user/owner' style="color: var(--potato-primary)">@owner</a> and nobody else - <a href="/stats">Stats</a>
            </small>
        </footer>
    </body>
    </html>
    '''
    
@app.route("/hashtag/<tag>")
def hashtag_page(tag):
    order = request.args.get('order', default='recent')
    user = session.get("user")
    
    all_posts = [f for f in os.listdir("posts/") if f.isdigit()]
    matched_posts = []
    
    for p in all_posts:
        with open(f"posts/{p}", "r") as f:
            raw = f.read()
        author, content = raw.split("|", 1) if "|" in raw else ("unknown", raw)
        
        if f"#{tag}" in content:
            # Count likes for sorting
            likes_file = f"posts/{p}.likes"
            like_count = 0
            if os.path.exists(likes_file):
                with open(likes_file, "r") as f:
                    like_count = len(f.readlines())
            matched_posts.append({'id': p, 'author': author, 'content': content, 'likes': like_count})

    # Sort matching posts
    if order == 'likes':
        matched_posts.sort(key=lambda x: (x['likes'], int(x['id'])), reverse=True)
    else:
        matched_posts.sort(key=lambda x: int(x['id']), reverse=True)

    posts_html = ""
    for p in matched_posts:
        posts_html += f'<div class="card mb-3 shadow-sm"><div class="card-body">{render_post_html(p["id"], p["author"], p["content"])}</div></div>'
    
    sort_nav = f'''
        <div class="mb-3 text-end">
            <small class="text-muted">Sort by: 
                <a href="/hashtag/{tag}?order=recent" class="{"fw-bold" if order=='recent' else ""}">Recent</a> | 
                <a href="/hashtag/{tag}?order=likes" class="{"fw-bold" if order=='likes' else ""}">Most Liked</a>
            </small>
        </div>
    '''
    
    body = f"<h4>Posts tagged with <span class='text-primary'>#{tag}</span></h4><hr>{sort_nav}{posts_html}"
    return render_ui_template(f"#{tag}", body, user)
    

@app.route("/user/<username>")
def user_page(username):
    order = request.args.get('order', default='recent') #
    user = session.get("user")
    
    # Gather only posts belonging to this specific user
    all_posts = [f for f in os.listdir("posts/") if f.isdigit()]
    user_posts = []
    
    for p in all_posts:
        with open(f"posts/{p}", "r") as f:
            raw = f.read()
        
        if raw.startswith(username + "|"):
            _, content = raw.split("|", 1)
            
            # Count likes for sorting
            likes_file = f"posts/{p}.likes"
            like_count = 0
            if os.path.exists(likes_file):
                with open(likes_file, "r") as f:
                    like_count = len(f.readlines())
            
            user_posts.append({
                'id': p, 
                'author': username, 
                'content': content, 
                'likes': like_count
            })

    # Sort the user's posts
    if order == 'likes':
        user_posts.sort(key=lambda x: (x['likes'], int(x['id'])), reverse=True)
    else:
        user_posts.sort(key=lambda x: int(x['id']), reverse=True)

    posts_html = ""
    for p in user_posts:
        posts_html += f'<div class="card mb-3 shadow-sm"><div class="card-body">{render_post_html(p["id"], p["author"], p["content"])}</div></div>'

    # Bio logic
    bio = ""
    bio_path = f"users/{username}.bio"
    if os.path.exists(bio_path):
        with open(bio_path) as f:
            bio = f.read()

    bio_html = f"<p class='text-muted'>{bio}</p>" if bio else ""
    
    # Action buttons logic
    message_button = ""
    if user and user != username:
        message_button += f"<a href='/dm/{username}' class='btn btn-sm btn-outline-primary'>Message</a>"
    elif user == username:
        message_button += f"<a href='/edit_bio' class='btn btn-sm btn-outline-secondary'>Edit Bio</a> - "
        message_button += f"<a href='/edit_avatar' class='btn btn-sm btn-outline-info me-1'>Edit Avatar</a>"
        
    # Sorting UI
    sort_nav = f'''
        <div class="mb-3 text-end">
            <small class="text-muted">Sort posts by: 
                <a href="/user/{username}?order=recent" class="{"fw-bold" if order=='recent' else ""}">Recent</a> | 
                <a href="/user/{username}?order=likes" class="{"fw-bold" if order=='likes' else ""}">Most Liked</a>
            </small>
        </div>
    '''
        
    body = f"""
        <h4>Profile: <span class='text-primary'>{render_pixel_art(username, size=64)}@{username}</span></h4>
        <div class="mb-3">{message_button}</div>
        <hr>{bio_html}<hr>
        {sort_nav}
        {posts_html}
    """
    return render_ui_template(f"@{username}", body, user)
    
@app.route("/register", methods=["GET","POST"])
def register():
    if request.method == "POST":
        username = request.form.get("username", "").strip()
        password = request.form.get("password", "").strip()
        if not username.isalnum(): return "No special chars in usernames", 400
        for i in ["admin","owner"]:
            if i in username.lower():
                return "No impersonation", 400
        if not username or not password:
            return "Missing fields", 400
        if not username.isalnum():
            return "Invalid username", 400
        path = f"users/{username}"
        if os.path.exists(path):
            return "User exists", 400
            
        with open(path, "w") as f:
            f.write(generate_password_hash(password))
            
        # Log the registration IP
        with open(f"users/{username}.ip", "w") as f:
            f.write(request.remote_addr)
            
        session.permanent = True
        session["user"] = username
        return redirect("/welcome")

    body = '''
        <div class="row justify-content-center">
            <div class="col-md-6">
                <div class="card shadow-sm">
                    <div class="card-body p-4">
                        <h3 class="card-title h5 mb-4 text-center">Join PotatoNetwork</h3>
                        <form method="POST">
                            <div class="mb-3">
                                <label class="form-label">Username</label>
                                <input name="username" class="form-control" placeholder="Alphanumeric only" required>
                            </div>
                            <div class="mb-4">
                                <label class="form-label">Password</label>
                                <input name="password" type="password" class="form-control" placeholder="Choose a strong password" required>
                            </div>
                            <button class="btn btn-primary w-100 py-2" type="submit">Create Account</button>
                            <p style="text-align: center; margin-top: 15px;">
                                By registering, you agree to the contents of the <a href="/legal">Legal stuff</a> page.
                            </p>
                        </form>
                        <div class="mt-3 text-center">
                            <small class="text-muted">Already have an account? <a href="/login">Login</a></small>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    '''
    return render_ui_template("Register", body, None)
    
@app.route("/login", methods=["GET","POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username", "").strip()
        password = request.form.get("password", "").strip()
        if not username.isalnum(): return "Stop hacking", 400
        path = f"users/{username}"
        if not os.path.exists(path):
            return "Invalid credentials", 400
        with open(path, "r") as f:
            stored = f.read()
        if not check_password_hash(stored, password):
            return "Invalid credentials", 400
            
        # Update the last known IP
        with open(f"users/{username}.ip", "w") as f:
            f.write(request.remote_addr)
            
        session.permanent = True
        session["user"] = username
        return redirect("/")

    body = '''
        <div class="row justify-content-center">
            <div class="col-md-6">
                <div class="card shadow-sm">
                    <div class="card-body p-4">
                        <h3 class="card-title h5 mb-4 text-center">Login to PotatoNetwork</h3>
                        <form method="POST">
                            <div class="mb-3">
                                <label class="form-label">Username</label>
                                <input name="username" class="form-control" placeholder="Enter username" required>
                            </div>
                            <div class="mb-4">
                                <label class="form-label">Password</label>
                                <input name="password" type="password" class="form-control" placeholder="Enter password" required>
                            </div>
                            <button class="btn btn-primary w-100 py-2" type="submit">Login</button>
                        </form>
                        <div class="mt-3 text-center">
                            <small class="text-muted">New here? <a href="/register">Register</a></small>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    '''
    return render_ui_template("Login", body, None)
    
@app.route("/logout")
def logout():
    session.pop("user",None)
    return redirect("/")

@app.route("/post/new", methods=["GET", "POST"])
def new_post():
    global id
    user = session.get("user")
    if not user:
        return redirect("/login")
        
    if request.method == "POST":
        content = request.form.get("content", "").strip()
        if not content:
            return "Content required", 400
        if len(content) > 2048: return "Too long, max 2048 chars", 400
        with open(f"posts/{id}", "w") as post:
            post.write(f"{user}|{content}")
        id += 1
        pings = re.findall(r'@(\w+)', content)
        for tagged_user in set(pings):
            if os.path.exists(f"users/{tagged_user}") and tagged_user != user:
                add_notification(tagged_user, user, id, type="ping")
        # Using a themed redirect message
        return render_ui_template("Success", 
            'Post created! <a href="/" class="btn btn-primary">Go back to feed</a>', 
            user)

    # The HTML content for the 'New Post' page
    body = f'''
    <div class="card shadow-sm border-primary">
        <div class="card-body p-4">
            <h3 class="card-title h5 mb-4">Create a New Post</h3>
            <form method="POST">
                <div class="mb-3">
                    <label class="form-label small">What's on your mind?</label>
                    <textarea name="content" class="form-control" rows="6" 
                              placeholder="Markdown is supported!" 
                              required autofocus></textarea>
                </div>
                <div class="d-flex gap-2">
                    <button class="btn btn-primary px-4" type="submit">Publish Post</button>
                    <a href="/" class="btn btn-outline-secondary">Cancel</a>
                </div>
            </form>
        </div>
    </div>
    <div class="mt-3 small text-muted">
        Tips: 
        <ul>
            <li>You can use hashtags like #potatoes to categorize your post.</li>
            <li>You can embed images with ![alt text](url).</li>
            <li>You can embed YouTube videos with !yt[url].</li>
            <li>See the full markdown format docs <a href="https://quarto.org/docs/authoring/markdown-basics.html">here</a>.</li>
        </ul>
    </div>
    '''
    
    return render_ui_template("New Post", body, user)

@app.route("/post/view")
def view_post():
    pid = request.args.get('id', default="", type=str)
    order = request.args.get('order', 'recent')
    if not pid.isdigit():
        return "Invalid ID", 400
    try:
        with open(f"posts/{pid}", "r") as post:
            raw = post.read()
        author, content = raw.split("|", 1) if "|" in raw else ("unknown", raw)
        
        main_post_html = f'<div class="card mb-4 border-primary shadow"><div class="card-body">{render_post_html(pid, author, content)}</div></div>'
        
        # Replies Logic
        reply_folder = f"posts/{pid}.replies"
        replies = []
        if os.path.exists(reply_folder):
            for r in os.listdir(reply_folder):
                if not r.isdigit(): continue
                with open(f"{reply_folder}/{r}", "r") as f:
                    r_raw = f.read()
                r_author, r_content = r_raw.split("|", 1) if "|" in r_raw else ("unknown", r_raw)
                
                # Count likes for reply
                l_file = f"{reply_folder}/{r}.likes"
                r_likes = len(open(l_file).readlines()) if os.path.exists(l_file) else 0
                replies.append((int(r), r_likes, r_author, r_content))
            
            replies.sort(key=lambda x: x[1] if order == 'likes' else x[0], reverse=True)

        replies_html = ""
        for r_id, _, r_auth, r_cont in replies:
            replies_html += f'<div class="card mb-2 ms-4 shadow-sm"><div class="card-body py-2">{render_reply_html(pid, r_id, r_auth, r_cont)}</div></div>'
        
        sort_nav = f'''
            <div class="d-flex justify-content-between align-items-center mb-3">
                <h5 class="mb-0">Replies</h5>
                <small>Sort: <a href="?id={pid}&order=recent">Recent</a> | <a href="?id={pid}&order=likes">Likes</a></small>
            </div>
        '''
        
        add_reply_btn = f'<div class="mt-4"><a href="/post/reply/{pid}" class="btn btn-outline-primary w-100">Add a Reply</a></div>'
        
        return render_ui_template("View Post", f"{main_post_html}{sort_nav}{replies_html}{add_reply_btn}", session.get("user"))
    except FileNotFoundError:
        return "Post not found", 404
        
@app.route("/post/like/<int:pid>")
def like_post(pid):
    user = session.get("user")
    if not user:
        return redirect("/login")
    likes_file = f"posts/{pid}.likes"
    liked_users = set()
    if os.path.exists(likes_file):
        with open(likes_file, "r") as f:
            liked_users = set(line.strip() for line in f.readlines())
    if user in liked_users:
        return redirect(f"/post/view?id={pid}")
    liked_users.add(user)
    with open(likes_file, "w") as f:
        for u in liked_users:
            f.write(u + "\n")
    return redirect(f"/post/view?id={pid}")

@app.route("/post/like/<int:pid>/reply/<int:rid>")
def like_reply(pid, rid):
    user = session.get("user")
    if not user:
        return redirect("/login")
    likes_file = f"posts/{pid}.replies/{rid}.likes"
    liked_users = set()
    if os.path.exists(likes_file):
        with open(likes_file, "r") as f:
            liked_users = set(line.strip() for line in f.readlines())
    if user in liked_users:
        return redirect(f"/post/view?id={pid}")
    liked_users.add(user)
    with open(likes_file, "w") as f:
        for u in liked_users:
            f.write(u + "\n")
    return redirect(f"/post/view?id={pid}")

@app.route("/post/unlike/<int:pid>")
def unlike_post(pid):
    user = session.get("user")
    if not user:
        return redirect("/login")
    likes_file = f"posts/{pid}.likes"
    if os.path.exists(likes_file):
        with open(likes_file,"r") as f:
            liked_users = set(line.strip() for line in f.readlines())
        liked_users.discard(user)
        with open(likes_file,"w") as f:
            for u in liked_users:
                f.write(u+"\n")
    return redirect(f"/post/view?id={pid}")

@app.route("/post/unlike/<int:pid>/reply/<int:rid>")
def unlike_reply(pid,rid):
    user = session.get("user")
    if not user:
        return redirect("/login")
    likes_file = f"posts/{pid}.replies/{rid}.likes"
    if os.path.exists(likes_file):
        with open(likes_file,"r") as f:
            liked_users = set(line.strip() for line in f.readlines())
        liked_users.discard(user)
        with open(likes_file,"w") as f:
            for u in liked_users:
                f.write(u+"\n")
    return redirect(f"/post/view?id={pid}")
    
@app.route("/post/reply/<int:pid>", methods=["GET","POST"])
def reply_post(pid):
    user = session.get("user")
    if not user:
        return redirect("/login")
    
    # Get original post author to notify them
    post_path = f"posts/{pid}"
    if not os.path.exists(post_path):
        return "Post not found", 404
        
    with open(post_path, "r") as f:
        raw = f.read()
        original_author = raw.split("|", 1)[0] if "|" in raw else "unknown"
    
    reply_folder = f"posts/{pid}.replies"
    if not os.path.exists(reply_folder):
        os.mkdir(reply_folder)
        
    if request.method == "POST":
        content = request.form.get("content", "").strip()
        if not content:
            return "Content required", 400
        if len(content) > 512: 
            return "Too long, max 512 chars", 400
            
        existing = [int(f) for f in os.listdir(reply_folder) if f.isdigit()] or [-1]
        rid = max(existing) + 1
        
        with open(f"{reply_folder}/{rid}", "w") as f:
            f.write(f"{user}|{content}")
            
        # --- Notification Logic ---
        # 1. Notify original author (if it's not a self-reply)
        if original_author != user:
            add_notification(original_author, user, pid, type="reply")
            
        # 2. Notify anyone pinged in the reply
        pings = re.findall(r'@(\w+)', content)
        for tagged_user in set(pings):
            # Don't notify the original author twice if they were also pinged
            if os.path.exists(f"users/{tagged_user}") and tagged_user != user and tagged_user != original_author:
                add_notification(tagged_user, user, pid, type="ping")
        
        return redirect(f"/post/view?id={pid}")

    body = f'''
        <div class="card shadow-sm border-primary">
            <div class="card-body">
                <h5 class="mb-3">Replying to Post <span class="text-primary">#{pid}</span></h5>
                <form method="POST">
                    <div class="mb-3">
                        <textarea name="content" class="form-control" rows="5" 
                                  placeholder="Write your thoughts... Markdown is supported!" required autofocus></textarea>
                    </div>
                    <div class="d-flex gap-2">
                        <button class="btn btn-primary px-4" type="submit">Post Reply</button>
                        <a href="/post/view?id={pid}" class="btn btn-outline-secondary">Cancel</a>
                    </div>
                </form>
            </div>
        </div>
    '''
    return render_ui_template("Add Reply", body, user)

@app.route("/legal")
def legal_page():
    user = session.get("user")
    body = '''
        <div class="card shadow-sm mb-4">
            <div class="card-body p-4">
                <h2 class="h4 border-bottom pb-2 mb-3 text-primary">Terms of Service</h2>
                <p class="text-muted small">Effective Date: April 2026</p>
                
                <h6>1. Content Governance</h6>
                <p>PotatoNetwork operates under a principle of minimal interference. We do not enforce platform-specific conduct codes or community guidelines beyond the requirements of applicable law. We reserve the right to remove content only in the following circumstances:</p>
                <ul>
                    <li>Content that is deemed illegal under governing jurisdictions.</li>
                    <li>Automated accounts (bots) or coordinated spam activity.</li>
                </ul>

                <h6>2. Reporting and Compliance</h6>
                <p>Users who encounter technical vulnerabilities (bugs) or content that violates legal statutes are encouraged to report such findings immediately to the administration via: 
                <a href="mailto:gamer@denaro.work.gd" class="fw-bold">gamer@denaro.work.gd</a>.</p>
                
                <h2 class="h4 border-bottom pb-2 mt-5 mb-3 text-primary">Privacy Policy</h2>
                
                <h6>1. Data Collection and Retention</h6>
                <p>Our infrastructure is designed to be privacy-first. We do not engage in the sale, trade, or transfer of user data to third-party entities. The only data retained on our servers consists of the content explicitly provided by the user (posts and replies), hashed authentication credentials and user IP addresses.</p>

                <h6>2. Cookie Disclosure</h6>
                <p>PotatoNetwork utilizes cookies exclusively for functional session management. These "session cookies" are necessary to maintain your authenticated state. No tracking, profiling, or advertising cookies are deployed by this platform.</p>

                <h6>3. User Sovereignty</h6>
                <p>By using this platform, you acknowledge that your public posts are stored on our servers for the purpose of display within the network. Beyond this functional requirement, no background data harvesting or behavioral analysis is performed.</p>
                
                <div class="mt-4 pt-3 border-top text-center">
                    <a href="/" class="btn btn-primary px-4">Back to Feed</a>
                </div>
            </div>
        </div>
    '''
    return render_ui_template("Legal Information", body, user)

@app.route("/edit_bio", methods=["GET", "POST"])
def edit_bio():
    user = session.get("user")
    if not user:
        return redirect("/login")

    bio_path = f"users/{user}.bio"

    if request.method == "POST":
        bio = request.form.get("bio", "").strip()
        with open(bio_path, "w") as f:
            f.write(bio)
        return redirect(f"/user/{user}")

    current_bio = ""
    if os.path.exists(bio_path):
        with open(bio_path) as f:
            current_bio = f.read()

    body = f'''
    <div class="card p-4">
        <h5>Edit Bio</h5>
        <form method="POST">
            <textarea name="bio" class="form-control mb-3" rows="4">{current_bio}</textarea>
            <button class="btn btn-primary">Save</button>
        </form>
    </div>
    '''
    return render_ui_template("Edit Bio", body, user)

@app.route("/dm/<username>", methods=["GET", "POST"])
def dm(username):
    user = session.get("user")
    if not user:
        return redirect("/login")

    folder = dm_folder(user, username)
    os.makedirs(folder, exist_ok=True)
    
    # NEW: Mark chat as read for the current user
    import time
    with open(f"{folder}/{user}.lastseen", "w") as f:
        f.write(str(time.time()))

    if request.method == "POST":
        msg = request.form.get("message", "").strip()
        if msg:
            ids = [int(f) for f in os.listdir(folder) if f.isdigit()] or [-1]
            mid = max(ids) + 1
            with open(f"{folder}/{mid}", "w") as f:
                f.write(f"{user}|{msg}")
        return redirect(f"/dm/{username}")

    messages = []
    m_ids = sorted([int(f) for f in os.listdir(folder) if f.isdigit()])
    for mid in m_ids:
        with open(f"{folder}/{mid}", "r") as f:
            raw = f.read()
            if "|" in raw:
                sender, text = raw.split("|", 1)
                messages.append((sender, text))

    msg_html = ""
    for sender, text in messages:
        align = "end" if sender == user else "start"
        bg = "primary text-white" if sender == user else "secondary text-white"
        msg_html += f'''
            <div class="d-flex justify-content-{align} mb-2">
                <div class="rounded px-3 py-2 shadow-sm bg-{bg}" style="max-width: 80%;">
                    <small class="d-block fw-bold" style="font-size: 0.7rem;">@{sender}</small>
                    {sanitize_html(markdown.markdown(text))}
                </div>
            </div>'''

    body = f'''
        <div class="card shadow-sm h-100">
            <div class="card-header bg-transparent border-bottom d-flex align-items-center">
                {render_pixel_art(username, 30)}
                <h5 class="mb-0 ms-2">Chat with @{username}</h5>
            </div>
            <div class="card-body overflow-auto" style="height: 400px; display: flex; flex-direction: column-reverse;">
                <div>{msg_html}</div>
            </div>
            <div class="card-footer bg-transparent border-top">
                <form method="POST" class="d-flex gap-2">
                    <input name="message" class="form-control" placeholder="Type a message..." autocomplete="off" required autofocus>
                    <button class="btn btn-primary">Send</button>
                </form>
            </div>
        </div>
        <div class="mt-3 text-center">
            <a href="/inbox" class="btn btn-sm btn-link">← Back to Inbox</a>
        </div>
    '''
    return render_ui_template(f"Chat with @{username}", body, user)
    
def dm_folder(u1, u2):
    return f"dms/{'__'.join(sorted([u1, u2]))}"
    
@app.route("/inbox")
def inbox():
    user = session.get("user")
    if not user: return redirect("/login")

    _, unread_chats = get_unread_count(user)
    
    chats = [] 
    notifications = []
    
    if os.path.exists("dms"):
        for folder in os.listdir("dms"):
            # Split the folder name into the two participants
            parts = folder.split("__")
            if len(parts) != 2:
                continue
                
            # Check for EXACT match in the list of participants
            if user in parts:
                # Identify the other participant (the one who isn't 'user')
                other = parts[1] if parts[0] == user else parts[0]
                
                if other == "system":
                    notifications.append(other)
                else:
                    chats.append(other)

    def render_links(chat_list, is_system=False):
        html = ""
        for u in chat_list:
            is_unread = u in unread_chats
            weight = "fw-bold" if is_unread else ""
            label = "Notifications" if is_system else f"Chat with @{u}"
            badge = '<span class="badge bg-danger ms-2">NEW</span>' if is_unread else ""
            html += f'<a href="/dm/{u}" class="list-group-item list-group-item-action {weight}">{label}{badge}</a>'
        return html

    body = f'''
        <h5>Notifications</h5>
        <div class="list-group mb-4 shadow-sm">
            {render_links(notifications, True) or "<p class='p-3 text-muted'>No new notifications.</p>"}
        </div>
        <h5>Messages</h5>
        <div class="list-group shadow-sm">
            {render_links(chats) or "<p class='p-3 text-muted'>No messages yet.</p>"}
        </div>
    '''
    return render_ui_template("Inbox", body, user)
    
@app.route("/settings", methods=["GET", "POST"])
def settings():
    user = session.get("user")
    
    if request.method == "POST":
        resp = make_response(redirect("/settings"))
        
        # Handle Quick presets
        preset = request.form.get("preset")
        if preset == "dark":
            colors = {"bg": "#121212", "card": "#1e1e1e", "text": "#ffffff", "primary": "#00a8ff", "nav": "#1e1e1e"}
        elif preset == "light":
            colors = {"bg": "#f8f9fa", "card": "#ffffff", "text": "#212529", "primary": "#007bff", "nav": "#ffffff"}
        else:
            # Handle precise color selection
            colors = {
                "bg": request.form.get("theme_bg"),
                "card": request.form.get("theme_card"),
                "text": request.form.get("theme_text"),
                "primary": request.form.get("theme_primary"),
                "nav": request.form.get("theme_nav")
            }
        
        for key, val in colors.items():
            resp.set_cookie(f"theme_{key}", val, max_age=31536000) # Save for 1 year
        return resp

    # Current values for the form inputs
    theme = {
        "bg": request.cookies.get("theme_bg", "#f8f9fa"),
        "card": request.cookies.get("theme_card", "#ffffff"),
        "text": request.cookies.get("theme_text", "#212529"),
        "primary": request.cookies.get("theme_primary", "#007bff"),
        "nav": request.cookies.get("theme_nav", "#ffffff")
    }

    body = f'''
    <div class="card p-4 shadow-sm">
        <h5>Quick Presets</h5>
        <form method="POST" class="mb-4 d-flex gap-2">
            <button name="preset" value="light" class="btn btn-outline-secondary">☀ Light Mode</button>
            <button name="preset" value="dark" class="btn btn-dark">🌙 Dark Mode</button>
        </form>
        
        <hr>
        
        <h5>Custom Branding</h5>
        <form method="POST">
            <div class="row g-3">
                <div class="col-6">
                    <label class="form-label small">Background Color</label>
                    <input type="color" name="theme_bg" class="form-control form-control-color w-100" value="{theme['bg']}">
                </div>
                <div class="col-6">
                    <label class="form-label small">Card Color</label>
                    <input type="color" name="theme_card" class="form-control form-control-color w-100" value="{theme['card']}">
                </div>
                <div class="col-6">
                    <label class="form-label small">Text Color</label>
                    <input type="color" name="theme_text" class="form-control form-control-color w-100" value="{theme['text']}">
                </div>
                <div class="col-6">
                    <label class="form-label small">Primary/Links</label>
                    <input type="color" name="theme_primary" class="form-control form-control-color w-100" value="{theme['primary']}">
                </div>
                <div class="col-12">
                    <label class="form-label small">Navbar Color</label>
                    <input type="color" name="theme_nav" class="form-control form-control-color w-100" value="{theme['nav']}">
                </div>
            </div>
            <button type="submit" class="btn btn-primary w-100 mt-4">Save Custom Theme</button>
        </form>
    </div>
    '''
    return render_ui_template("Settings", body, user)

@app.route("/edit_avatar", methods=["GET", "POST"])
def edit_avatar():
    user = session.get("user")
    if not user: return redirect("/login")

    if request.method == "POST":
        data = request.json.get("pixels") # List of 256 integers (0-15)
        if len(data) == 256:
            binary_data = bytearray()
            for i in range(0, 256, 2):
                # Pack two 4-bit pixels into one 8-bit byte
                byte = (data[i] << 4) | data[i+1]
                binary_data.append(byte)
            with open(f"users/{user}.pxl", "wb") as f:
                f.write(binary_data)
            return jsonify({"status": "success"})

    # Load existing pixels for the editor
    path = f"users/{user}.pxl"
    current_pixels = [7] * 256
    if os.path.exists(path):
        with open(path, "rb") as f:
            raw = f.read()
            current_pixels = []
            for b in raw:
                current_pixels.extend([(b >> 4) & 0x0F, b & 0x0F])

    palette_html = "".join([f'<div class="color-swatch" onclick="selectedColor={i}" style="background:{c}; width:30px; height:30px; cursor:pointer; border:2px solid #fff;"></div>' for i, c in enumerate(VGA_PALETTE)])
    
    body = f'''
    <div class="card p-4 shadow-sm text-center">
        <h4>Avatar Designer (16x16)</h4>
        <div id="pixel-grid" style="display: grid; grid-template-columns: repeat(16, 1fr); width: 320px; margin: 20px auto; border: 1px solid #ccc; cursor: crosshair;"></div>
        <div class="d-flex justify-content-center gap-1 mb-4">{palette_html}</div>
        <button onclick="saveArt()" class="btn btn-primary">Save Avatar</button>
    </div>
    <script>
        let selectedColor = 15;
        let pixels = {current_pixels};
        const grid = document.getElementById('pixel-grid');
        let isDrawing = false;

        function initGrid() {{
            grid.innerHTML = '';
            pixels.forEach((p, i) => {{
                const div = document.createElement('div');
                div.style.background = {VGA_PALETTE}[p];
                div.style.aspectRatio = "1/1";

                div.onmousedown = (e) => {{
                    e.preventDefault();
                    isDrawing = true;
                    pixels[i] = selectedColor;
                    initGrid();
                }};
                div.onmouseover = () => {{
                    if (isDrawing) {{
                        pixels[i] = selectedColor;
                        initGrid();
                    }}
                }};
                grid.appendChild(div);
            }});
        }}

        document.addEventListener('mouseup', () => {{
            isDrawing = false;
        }});

        async function saveArt() {{
            await fetch('/edit_avatar', {{
                method: 'POST',
                headers: {{'Content-Type': 'application/json'}},
                body: JSON.stringify({{pixels: pixels}})
            }});
            window.location.href = '/user/{user}';
        }}

        initGrid();
    </script>
    '''
    return render_ui_template("Edit Avatar", body, user)

@app.route("/admin")
def admin_panel():
    if not is_admin(): return "Unauthorized", 403

    # User Management
    all_users = [f for f in os.listdir("users") if not f.endswith((".bio", ".pxl", ".ip"))]
    user_mgmt = ""
    for u in all_users:
        ip_path = f"users/{u}.ip"
        last_ip = open(ip_path).read() if os.path.exists(ip_path) else "Unknown"
        is_banned = os.path.exists(f"bans/{last_ip}") if last_ip != "Unknown" else False
        
        ban_btn = f'<a href="/admin/ban/{u}" class="btn btn-sm btn-danger">Ban IP</a>' if not is_banned else '<span class="badge bg-secondary">Banned</span>'
        user_mgmt += f'''
            <tr class="align-middle">
                <td>{render_pixel_art(u, 20)} @{u}</td>
                <td><small class="text-muted">{last_ip}</small></td>
                <td>{ban_btn} <a href="/admin/del_user/{u}" class="btn btn-sm btn-outline-danger" onclick="return confirm('Delete user?')">Del</a></td>
            </tr>'''

    # Post Management
    all_posts = sorted([int(f) for f in os.listdir("posts/") if f.isdigit()], reverse=True)
    post_mgmt = ""
    for pid in all_posts[:20]: # Show last 20
        post_mgmt += f'''
            <div class="d-flex justify-content-between py-1 border-bottom">
                <span>#{pid}</span>
                <div>
                    <a href="/admin/pin/{pid}" class="btn btn-sm btn-outline-warning">Pin</a>
                    <a href="/admin/del_post/{pid}" class="btn btn-sm btn-outline-danger">Del</a>
                </div>
            </div>'''

    body = f'''
        <div class="card p-4 shadow-sm mb-4">
            <h5 class="text-primary mb-3">User & IP Management</h5>
            <table class="table table-sm">
                <thead><tr><th>User</th><th>Last IP</th><th>Actions</th></tr></thead>
                <tbody>{user_mgmt}</tbody>
            </table>
        </div>
        <div class="card p-4 shadow-sm">
            <h5 class="text-primary mb-3">Post Moderation</h5>
            {post_mgmt}
            <div class="mt-2"><a href="/admin/unpin" class="btn btn-sm btn-link text-muted">Clear Pinned Post</a></div>
        </div>
    '''
    return render_ui_template("Admin Panel", body, "owner")

@app.route("/admin/ban/<username>")
def admin_ban(username):
    if not is_admin(): return "Unauthorized", 403
    ip_path = f"users/{username}.ip"
    if os.path.exists(ip_path):
        with open(ip_path, "r") as f:
            ip = f.read().strip()
        with open(f"bans/{ip}", "w") as f:
            f.write(f"Banned user: {username}")
    return redirect("/admin")

@app.route("/admin/pin/<int:pid>")
def admin_pin(pid):
    if not is_admin(): return "Unauthorized", 403
    with open("posts/pinned", "w") as f:
        f.write(str(pid))
    return redirect("/")

@app.route("/admin/unpin")
def admin_unpin():
    if not is_admin(): return "Unauthorized", 403
    if os.path.exists("posts/pinned"):
        os.remove("posts/pinned")
    return redirect("/")

@app.route("/admin/del_post/<int:pid>")
def admin_del_post(pid):
    if not is_admin(): return "Unauthorized", 403
    if os.path.exists(f"posts/{pid}"):
        os.remove(f"posts/{pid}")
    return redirect("/admin")

import time

def get_unread_count(user):
    if not os.path.exists("dms"):
        return 0, []
    
    total_unread = 0
    unread_chats = []
    
    for folder_name in os.listdir("dms"):
        # FIX: Split the folder and check for exact username matches only
        parts = folder_name.split("__")
        if len(parts) != 2: continue
        
        if user in parts:  # This checks for exact equality (e.g., 'thing' == 'thing2' is False)
            path = f"dms/{folder_name}"
            other_user = parts[1] if parts[0] == user else parts[0]
            
            last_seen = 0
            last_seen_path = f"{path}/{user}.lastseen"
            if os.path.exists(last_seen_path):
                with open(last_seen_path, "r") as f:
                    try: last_seen = float(f.read())
                    except: last_seen = 0
            
            chat_has_unread = False
            for f in os.listdir(path):
                if f.isdigit():
                    mtime = os.path.getmtime(f"{path}/{f}")
                    if mtime > last_seen:
                        with open(f"{path}/{f}", "r") as msg_file:
                            content = msg_file.read()
                            # Ensure we don't count our own sent messages as unread
                            if not content.startswith(f"{user}|"):
                                total_unread += 1
                                chat_has_unread = True
            
            if chat_has_unread:
                unread_chats.append(other_user)
                
    return total_unread, unread_chats
    
@app.route("/welcome")
def welcome_tutorial():
    user = session.get("user")
    if not user:
        return redirect("/login")

    body = f'''
        <div class="card shadow-sm border-primary">
            <div class="card-body p-4">
                <h3 class="text-primary mb-4">Welcome to PotatoNetwork, @{user}!</h3>
                <p class="mb-4">Here’s a quick guide to get you started:</p>
                
                <ul class="list-group list-group-flush mb-4">
                    <li class="list-group-item bg-transparent border-bottom">
                        <strong>Customize:</strong> To edit your bio and avatar, head over to <a href="/user/{user}">your user page</a>.
                    </li>
                    <li class="list-group-item bg-transparent border-bottom">
                        <strong>Chat:</strong> To message someone, simply visit their profile page and click the "Message" button.
                    </li>
                    <li class="list-group-item bg-transparent border-bottom">
                        <strong>Discuss:</strong> You can join the conversation by replying to posts using the "View Replies" link.
                    </li>
                    <li class="list-group-item bg-transparent border-bottom">
                        <strong>Tag:</strong> You can tag your posts with #hashtags to make them discoverable.
                    </li>
                </ul>

                <div class="text-center">
                    <a href="/" class="btn btn-primary px-5">Got it, take me to the feed!</a>
                </div>
            </div>
        </div>
    '''
    return render_ui_template("Getting Started", body, user)

def add_notification(target_user, trigger_user, post_id, type="reply"):
    # We use a special 'system' folder so it appears in the inbox logic
    folder = dm_folder("system", target_user)
    os.makedirs(folder, exist_ok=True)
    
    ids = [int(f) for f in os.listdir(folder) if f.isdigit()] or [-1]
    mid = max(ids) + 1
    
    msg = f"replied to your post <a href=\"/post/view?id={post_id}\">#{post_id}</a>" if type == "reply" else f"mentioned you in post <a href=\"/post/view?id={post_id-1}\">#{post_id-1}</a>"
    
    with open(f"{folder}/{mid}", "w") as f:
        f.write(f"{trigger_user}|{msg}")

@app.route("/search")
def search():
    query = request.args.get('q', '').strip().lower()
    user = session.get("user")
    if not query:
        return redirect("/")

    # Remove the first character if it is @ or #
    if query.startswith(('@', '#')):
        query = query[1:]

    all_post_ids = [f for f in os.listdir("posts/") if f.isdigit()]
    all_users = [f for f in os.listdir("users") if not f.endswith((".bio", ".pxl", ".ip"))]
    
    results = []
    found_hashtags = set()
    found_users = []

    # Search for matching users
    for u in all_users:
        if query in u.lower():
            found_users.append(u)

    # Search posts and hashtags
    for pid in all_post_ids:
        try:
            with open(f"posts/{pid}", "r") as f:
                raw = f.read()
            author, content = raw.split("|", 1) if "|" in raw else ("unknown", raw)
            
            # Search content and author
            if query in content.lower() or query in author.lower():
                results.append(render_post_html(pid, author, content))
            
            # Extract hashtags for partial matching
            tags = re.findall(r"#(\w+)", content)
            for t in tags:
                if query in t.lower():
                    found_hashtags.add(t)
        except Exception:
            continue

    # UI for matching Users
    user_html = ""
    if found_users:
        user_links = "".join([
            f'<a href="/user/{u}" class="list-group-item list-group-item-action d-flex align-items-center">'
            f'{render_pixel_art(u, 24)} <span class="ms-2">@{u}</span></a>' 
            for u in sorted(found_users)
        ])
        user_html = f'<div class="mb-4"><h6>Users:</h6><div class="list-group shadow-sm">{user_links}</div></div>'

    # UI for matching hashtags
    hashtag_html = ""
    if found_hashtags:
        hashtag_links = " ".join([f'<a href="/hashtag/{t}" class="badge bg-primary text-decoration-none me-1">#{t}</a>' for t in sorted(found_hashtags)])
        hashtag_html = f'<div class="mb-4"><h6>Related Hashtags:</h6>{hashtag_links}</div>'

    # UI for matching posts
    posts_html = "".join([f'<div class="card mb-3 shadow-sm"><div class="card-body">{p}</div></div>' for p in results])
    
    if not results and not found_hashtags and not found_users:
        body = f'<div class="text-center py-5"><p class="text-muted">No results found for "{query}"</p><a href="/" class="btn btn-outline-primary">Back Home</a></div>'
    else:
        body = f'<h5>Search results for "{query}"</h5><hr>{user_html}{hashtag_html}{"<h6>Posts:</h6>" if results else ""}{posts_html}'

    return render_ui_template("Search Results", body, user)

@app.route("/stats")
def stats_page():
    user = session.get("user")
    
    # 1. Count Posts
    post_count = len([f for f in os.listdir("posts/") if f.isdigit()])
    
    # 2. Count Users (ignoring metadata files)
    user_count = len([f for f in os.listdir("users") if not f.endswith((".bio", ".pxl", ".ip"))])
    
    # 3. Active Sessions (from our global dict)
    active_count = len(ACTIVE_SESSIONS)
    
    # 4. Calculate Uptime
    uptime_seconds = int(time.time() - START_TIME)
    days, rem = divmod(uptime_seconds, 86400)
    hours, rem = divmod(rem, 3600)
    minutes, seconds = divmod(rem, 60)
    uptime_string = f"{days}d {hours}h {minutes}m {seconds}s"

    body = f'''
        <div class="row text-center">
            <div class="col-md-6 mb-4">
                <div class="card shadow-sm p-4">
                    <h6 class="text-muted text-uppercase small">Total Posts</h6>
                    <h2 class="display-6 fw-bold text-primary">{post_count}</h2>
                </div>
            </div>
            <div class="col-md-6 mb-4">
                <div class="card shadow-sm p-4">
                    <h6 class="text-muted text-uppercase small">Registered Users</h6>
                    <h2 class="display-6 fw-bold text-primary">{user_count}</h2>
                </div>
            </div>
            <div class="col-md-6 mb-4">
                <div class="card shadow-sm p-4">
                    <h6 class="text-muted text-uppercase small">Active Sessions (15m)</h6>
                    <h2 class="display-6 fw-bold text-success">{active_count}</h2>
                </div>
            </div>
            <div class="col-md-6 mb-4">
                <div class="card shadow-sm p-4">
                    <h6 class="text-muted text-uppercase small">Server Uptime</h6>
                    <h3 class="fw-bold text-info">{uptime_string}</h3>
                </div>
            </div>
        </div>
        <div class="text-center mt-3">
            <small class="text-muted">Statistics are not updated in real-time.</small>
        </div>
    '''
    return render_ui_template("Network Statistics", body, user)
    
if __name__=='__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
