#!/usr/bin/python3
"""Mobile status page -- site-first navigation for phones.

The desktop status page is three dense columns; its only phone support is one
media query that stacks them into a single long scroll, which is why field crews
lose their place. This page inverts the model: pick a site, see that site.

It is a LAYOUT, not a second implementation. StatusMobile (status_mobile.js)
extends StatusData exactly as StatusCore (status_desktop.js) does, so the model,
the websocket and every piece of derived state are shared -- the two pages cannot
disagree about whether a generator is running. The gen and pod modals are the
desktop files, included unchanged.

page_key is 'status' on purpose: this is the status page, so anyone who can see
the desktop one can see this, and no user access needs editing.

Not linked from the nav yet -- it is being shown to field crews for feedback
first. Add it to links.py once they are happy with it.
"""
import cgi
import cgitb
import sys

sys.path.insert(0, '/opt/ngon/apps')
sys.path.insert(0, '/var/www/html/ngon')

from managers.auth_manager import AuthManager, generate_login_page_html
from links import generate_dropdown_html, generate_dropdown_css, generate_dropdown_js

cgitb.enable()

form = cgi.FieldStorage()
auth = AuthManager('status_mobile')
auth_required, should_exit, headers = auth.require_auth(form)

if should_exit:
    print("Content-Type: application/json")
    if headers:
        print(headers)
    print("")
    print('{"success": false, "error": "Authentication required"}' if auth_required
          else '{"success": true}')
    sys.exit(0)

if auth_required:
    # Same no-store as the page proper: a cached login form served to someone who
    # has since signed in elsewhere looks like the session was lost.
    print("Content-Type: text/html")
    print("Cache-Control: no-cache, no-store, must-revalidate")
    print("Expires: 0")
    print("")
    print(generate_login_page_html("NGON Mobile Status"))
    sys.exit(0)

nav = generate_dropdown_html(AuthManager.get_user_access(), mobile=True)
# Sleep/wake/reboot are attributed to this in powercontrol.log and the chat
# notifications, exactly as index.py does it.
current_user = AuthManager.get_current_user() or 'MOBILE'
# Gates every engine action: the picker's "Gen Control" entry and the whole-group
# control button both hide without it. Same key and same check index.py uses, so a
# phone can never offer an action the desktop would not.
_can_gen_control = AuthManager('gen_control').check_access(AuthManager.get_current_user() or '')

# no-store, matching index.py. Without a caching directive a browser applies
# HEURISTIC caching, and WebKit -- which is every browser on iOS, Chrome
# included -- is aggressive about it. That matters more here than on an
# ordinary page: this document is what carries the ?v= numbers on every
# script and stylesheet, so a cached copy of it pins the whole bundle to
# whatever versions it named and defeats cache-busting at the root. The
# symptom is a phone that keeps loading old code however many times its
# owner pulls to refresh.
print("Content-Type: text/html; charset=utf-8")
print("Cache-Control: no-cache, no-store, must-revalidate")
print("Expires: 0")
print("")

# Asset versions are stamped from file mtimes at the bottom of this file rather
# than hand-written, the same way index.py does it. Hand-pinned ?v= numbers are
# a trap on a page like this: StatusWidgets/StatusData/StatusMobile are ONE
# bundle joined by `extends`, so a stale copy of the base class does not degrade
# the page, it kills it -- a method added to StatusWidgets and called from
# StatusMobile throws on every render, and the page never leaves "Loading...".
# That is exactly what shipped: status_widgets.js gained genServicing() in
# ffa5c12 while this file still said ?v=2, so every phone holding the old copy
# broke while the desktop page -- which already stamped by mtime -- was fine.
# Buffer the body so the same rewrite can run over it.
_parts = []
def _w(chunk):
    _parts.append(str(chunk))

_w('''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#141414">
<title>NGON Status</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<link rel="stylesheet" href="mobile.css?v=18">
<link rel="stylesheet" href="shutdown_history.css?v=2">
    <link rel="stylesheet" href="/shared/miner_view.css">
<style>''')

# Bare CSS, no <style> tags of its own -- must be printed INSIDE this block or the
# dropdown never gets display:none and all ~32 links render down the page.
_w(generate_dropdown_css())

# Printed AFTER the shared CSS so these win on equal specificity without !important.
# The shared .dropdown-title is a 2.2em page heading; here it is a hamburger.
_w('''
.dropdown { position: static; }
.dropdown-title {
    font-size: 1.5em;
    margin: 0;
    padding: 0 6px;
    min-width: 40px;
    line-height: 1;
}
.dropdown-content { max-height: 100vh; overflow-y: auto; }
.header { background: none; padding: 0; border: none; }
''')

_w('''</style>
</head>
<body>

<div class="mhead">
    <button class="mback" id="back" onclick="history.back()" aria-label="Back" style="display:none">&lsaquo;</button>
    <div class="dropdown">
        <h1 class="dropdown-title" aria-label="Menu"><i class="fas fa-bars"></i></h1>
        <div class="dropdown-content">''')
_w(nav)
_w('''        </div>
    </div>
    <div class="title" id="title">Sites</div>
    <button class="mgear" id="sitegear" data-siteprefs="1" aria-label="Choose sites">
        <i class="fas fa-gear"></i>
    </button>
    <button class="mwifi" id="bridgebtn" data-bridges="1" style="display:none"
            aria-label="Wifi bridges" title="Wifi bridges">
        <i class="fas fa-wifi"></i><span class="bdg" id="bridgebadge"></span>
    </button>
    <div class="mconn" id="conn" title="Live connection"></div>
</div>

<div id="app"><div class="empty">Loading&hellip;</div></div>

<script>window.ngonUser = "''' + current_user.replace('"', '') + '''";
window.canGenControl = ''' + ('true' if _can_gen_control else 'false') + ''';</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script src="/shared/miner_view.js"></script>

<script src="constants_cgi.py"></script>
<!-- Order is mandatory: `extends` resolves at class-definition time.
     StatusWidgets -> StatusData -> StatusMobile. -->
<script src="status_widgets.js?v=3"></script>
<script src="status_data.js?v=10"></script>
<script src="status_mobile.js?v=25"></script>
<!-- Standalone: reads window.statusCore, owns its own sheet. Order-independent. -->
<script src="status_bridge_sheet.js?v=1"></script>

<!-- Desktop modals, reused unchanged. They read window.statusCore, which
     status_mobile.js binds to the StatusMobile instance. -->
<script src="status_modal_utils.js"></script>
<script src="shutdown_history.js?v=1"></script>
    <script src="status_gen_modal.js?v=17"></script>
<script src="status_pod_modal.js"></script>

<script>''')
_w(generate_dropdown_js())
_w('''</script>
</body>
</html>''')

# Same stamp and same two patterns as index.py: every LOCAL .js/.css gets the
# newest mtime of anything in this directory, and an existing ?v=N is swallowed
# so a hand-written one can never survive and re-pin the bundle.
import os as _os, re as _re
_dir = _os.path.dirname(_os.path.abspath(__file__))
_v = int(max(_os.path.getmtime(_os.path.join(_dir, f)) for f in _os.listdir(_dir)
             if f.endswith(('.css', '.js')) and not f.startswith('.')))
_html = "\n".join(_parts)
_html = _re.sub(r'href="((?!https?://)[^"]+\.css)(?:\?v=\d+)?"', 'href="\\1?v=%d"' % _v, _html)
_html = _re.sub(r'src="((?!https?://)[^"]+\.js)(?:\?v=\d+)?"',  'src="\\1?v=%d"' % _v, _html)
print(_html)
