#!/usr/bin/env python3
import cgi, 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

_form = cgi.FieldStorage()
_auth = AuthManager('site_uptime')
_auth_required, _should_exit, _headers = _auth.require_auth(_form)

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

if _auth_required:
    print("Content-Type: text/html")
    print("")
    print(generate_login_page_html("Site Uptime"))
    sys.exit(0)

_user_access = AuthManager.get_user_access()


"""
Site Non-Zero Hash based Uptime
Historical site uptime/downtime analysis with 2025 yearly data and custom date range queries.
"""

import cgi
import csv
import sys
import os
import calendar
from datetime import datetime, timedelta, timezone
import cgitb

cgitb.enable()

# Add paths for imports
sys.path.insert(0, '/opt/ngon/apps')
from managers.config_manager import config

# Configuration
CSV_2025_PATH = "/var/www/html/ngon/status/site_uptime_2025_yearly.csv"
CSV_2026_PATH = "/var/www/html/ngon/status/site_uptime_2026_yearly.csv"

MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
INFLUXDB_URL = "https://us-central1-1.gcp.cloud2.influxdata.com"
INFLUXDB_TOKEN = "rehPqmCSlCkQnEAaFQEX75JD7J9lAsflaIS6TTYnIh3SetVyzEumX_dRMizxFEsEISG4VUdTd70rUHcJig7V6w=="
ORG = "ngonsolutions"
HASHRATE_BUCKET = "MARAGON_Hashrate"

# Sites to skip
SKIP_SITES = ["Out of Service", "Spares"]

# Thresholds
MIN_HASHRATE_THRESHOLD = 1_000_000_000_000  # 1 TH/s
GAP_THRESHOLD_MINUTES = 15


def load_yearly_data(csv_path):
    """Load yearly site uptime data from a CSV file."""
    data = []
    try:
        with open(csv_path, 'r') as f:
            reader = csv.DictReader(f)
            for row in reader:
                data.append(row)
    except Exception as e:
        return [], str(e)
    return data, None


def _parse_downtime_minutes(s):
    """Parse 'Xh Ym' format into total minutes. Returns None for missing data."""
    if s is None:
        return None
    s = str(s).strip()
    if not s or s in ('No data', 'N/A'):
        return None
    if s == '0m' or s == '0':
        return 0
    total = 0
    parsed_any = False
    for part in s.replace('h', 'h ').split():
        part = part.strip()
        if not part:
            continue
        try:
            if part.endswith('h'):
                total += int(part[:-1]) * 60
                parsed_any = True
            elif part.endswith('m'):
                total += int(part[:-1])
                parsed_any = True
        except ValueError:
            return None
    return total if parsed_any else None


def _format_minutes_to_hm(minutes):
    if minutes is None or minutes < 0:
        return 'No data'
    hours = int(minutes) // 60
    mins = int(minutes) % 60
    if hours > 0:
        return f"{hours}h {mins}m"
    return f"{mins}m"


def compute_ytd_totals(row, year):
    """Compute totals from per-month columns, using only months that have data.

    For an in-progress year, the denominator is the elapsed time across months
    with data (full-month for completed months, elapsed-to-now for the current
    month). This avoids the "all months at zero -> bogus 100%" failure mode.
    """
    now = datetime.now()
    is_current_year = (year == now.year)

    total_downtime_min = 0
    total_period_hours = 0.0
    total_events = 0
    months_with_data = 0

    for i, month_name in enumerate(MONTHS, 1):
        downtime_str = row.get(f'{month_name}_downtime', '')
        downtime_min = _parse_downtime_minutes(downtime_str)
        if downtime_min is None:
            continue

        if is_current_year and i > now.month:
            continue
        if is_current_year and i == now.month:
            month_hours = (now.day - 1) * 24 + now.hour + now.minute / 60.0
            if month_hours <= 0:
                continue
        else:
            days = calendar.monthrange(year, i)[1]
            month_hours = days * 24

        total_downtime_min += downtime_min
        total_period_hours += month_hours

        try:
            total_events += int(row.get(f'{month_name}_events', '0') or 0)
        except (ValueError, TypeError):
            pass

        months_with_data += 1

    if months_with_data == 0 or total_period_hours <= 0:
        return {
            'total_downtime': 'No data',
            'total_uptime%': 'N/A',
            'total_events': 0,
        }

    uptime_pct = 100.0 * (1.0 - (total_downtime_min / 60.0) / total_period_hours)
    return {
        'total_downtime': _format_minutes_to_hm(total_downtime_min),
        'total_uptime%': round(uptime_pct, 2),
        'total_events': total_events,
    }


def get_site_pods():
    """Get mapping of sites to their pod names from master config."""
    site_pods = {}
    master_config = config._master_config
    if not master_config:
        return site_pods

    for site_name, site_data in master_config.get('sites', {}).items():
        if site_name in SKIP_SITES:
            continue

        pods = []
        for group_name, group_data in site_data.get('generator_groups', {}).items():
            for pod_name in group_data.get('pods', {}).keys():
                pods.append(pod_name)

        if pods:
            site_pods[site_name] = pods

    return site_pods


def query_pod_timestamps(client, pod_name, start_date, end_date):
    """Query all timestamps where we have hashrate data for a pod."""
    query_api = client.query_api()

    query = f'''
    from(bucket: "{HASHRATE_BUCKET}")
        |> range(start: {start_date}T00:00:00Z, stop: {end_date}T23:59:59Z)
        |> filter(fn: (r) => r["_measurement"] == "{pod_name}")
        |> filter(fn: (r) => r["_field"] == "hashrate")
        |> sort(columns: ["_time"])
    '''

    try:
        tables = query_api.query(query)
        records = []
        for table in tables:
            for record in table.records:
                records.append({
                    'time': record.get_time(),
                    'hashrate': record.get_value() or 0
                })
        return records
    except Exception as e:
        return []


def calculate_site_downtime(pod_data, pod_names, start_dt, end_dt):
    """Calculate total downtime for a site."""
    if not pod_data:
        return 0, []

    all_timestamps = set()
    for pod_name, records in pod_data.items():
        for record in records:
            all_timestamps.add(record['time'])

    if not all_timestamps:
        return 0, []

    sorted_times = sorted(all_timestamps)

    total_downtime = timedelta()
    downtime_periods = []

    time_pod_data = {}
    for pod_name, records in pod_data.items():
        for record in records:
            ts = record['time']
            if ts not in time_pod_data:
                time_pod_data[ts] = {}
            time_pod_data[ts][pod_name] = record['hashrate']

    in_downtime = False
    downtime_start = None
    gap_threshold = timedelta(minutes=GAP_THRESHOLD_MINUTES)

    for i in range(len(sorted_times)):
        current_time = sorted_times[i]

        if i > 0:
            prev_time = sorted_times[i - 1]
            gap = current_time - prev_time

            # Only count gap as downtime if we're NOT already in a zero-hash
            # window — otherwise the zero-hash duration calc covers this span
            # and we'd double count.
            if gap > gap_threshold and not in_downtime:
                gap_downtime = gap - timedelta(minutes=5)
                if gap_downtime.total_seconds() > 0:
                    total_downtime += gap_downtime
                    downtime_periods.append({
                        'start': prev_time,
                        'end': current_time,
                        'duration': gap_downtime,
                        'type': 'gap'
                    })

        pod_hashrates = time_pod_data.get(current_time, {})
        pods_with_data = [pod for pod in pod_names if pod in pod_hashrates]

        if pods_with_data:
            all_zero = all(
                pod_hashrates.get(pod, 0) < MIN_HASHRATE_THRESHOLD
                for pod in pods_with_data
            )

            if all_zero:
                if not in_downtime:
                    in_downtime = True
                    downtime_start = current_time
            else:
                if in_downtime:
                    duration = current_time - downtime_start
                    if duration.total_seconds() > 300:
                        total_downtime += duration
                        downtime_periods.append({
                            'start': downtime_start,
                            'end': current_time,
                            'duration': duration,
                            'type': 'zero_hash'
                        })
                    in_downtime = False
                    downtime_start = None

    if in_downtime and downtime_start:
        duration = sorted_times[-1] - downtime_start
        if duration.total_seconds() > 300:
            total_downtime += duration
            downtime_periods.append({
                'start': downtime_start,
                'end': sorted_times[-1],
                'duration': duration,
                'type': 'zero_hash'
            })

    # Safety cap: downtime can't exceed the queried period.
    period_seconds = (end_dt - start_dt).total_seconds()
    downtime_seconds = total_downtime.total_seconds()
    if period_seconds > 0 and downtime_seconds > period_seconds:
        downtime_seconds = period_seconds

    return downtime_seconds, downtime_periods


def format_duration(seconds):
    """Format seconds as human-readable duration."""
    if seconds == 0:
        return "0m"

    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)

    if hours > 0:
        return f"{hours}h {minutes}m"
    return f"{minutes}m"


def run_custom_query(start_date_str, end_date_str):
    """Run a custom date range query against InfluxDB."""
    try:
        from influxdb_client import InfluxDBClient
    except ImportError:
        return [], "InfluxDB client not available"

    site_pods = get_site_pods()
    if not site_pods:
        return [], "No sites found in config"

    try:
        start_date = datetime.strptime(start_date_str, "%Y-%m-%d")
        end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
    except ValueError:
        return [], "Invalid date format"

    end_dt = end_date.replace(hour=23, minute=59, second=59, tzinfo=timezone.utc)
    start_dt = start_date.replace(hour=0, minute=0, second=0, tzinfo=timezone.utc)

    total_period_seconds = (end_dt - start_dt).total_seconds()

    client = InfluxDBClient(url=INFLUXDB_URL, token=INFLUXDB_TOKEN, org=ORG)
    results = []

    for site_name, pods in sorted(site_pods.items()):
        pod_data = {}
        for pod_name in pods:
            records = query_pod_timestamps(client, pod_name, start_date_str, end_date_str)
            if records:
                pod_data[pod_name] = records

        if not pod_data:
            results.append({
                'site': site_name,
                'pods': len(pods),
                'downtime_seconds': 0,
                'downtime_formatted': 'No data',
                'uptime_percent': 'N/A',
                'downtime_events': 0
            })
            continue

        downtime_seconds, periods = calculate_site_downtime(
            pod_data, pods, start_dt, end_dt
        )

        uptime_percent = 100 * (1 - downtime_seconds / total_period_seconds) if total_period_seconds > 0 else 100

        results.append({
            'site': site_name,
            'pods': len(pods),
            'downtime_seconds': downtime_seconds,
            'downtime_formatted': format_duration(downtime_seconds),
            'uptime_percent': round(uptime_percent, 2),
            'downtime_events': len(periods)
        })

    client.close()
    return results, None


def get_uptime_class(uptime_str):
    """Get CSS class based on uptime percentage."""
    if uptime_str == 'N/A':
        return 'no-data'
    try:
        uptime = float(uptime_str)
        if uptime >= 99:
            return 'excellent'
        elif uptime >= 95:
            return 'good'
        elif uptime >= 90:
            return 'warning'
        else:
            return 'critical'
    except:
        return 'no-data'


def render_yearly_table(data, year, title=None, dynamic_totals=False, total_label='Year Total'):
    """Render a yearly site uptime table.

    If dynamic_totals=True, total_downtime / total_uptime% / total_events are
    recomputed from per-month columns (skipping no-data months and using
    elapsed time for the current month). Useful for in-progress years.
    """
    if title is None:
        title = f'{year} Yearly Site Uptime'

    months = MONTHS

    html = f"""
    <div class="section">
        <h2>{title}</h2>
        <div class="table-wrapper">
            <table class="downtime-table">
                <thead>
                    <tr>
                        <th rowspan="2">Site</th>
                        <th rowspan="2">Pods</th>
    """

    for month in months:
        html += f'<th colspan="2" class="month-header">{month}</th>'

    html += f"""
                        <th colspan="3" class="total-header">{total_label}</th>
                    </tr>
                    <tr class="sub-header">
    """

    for _ in months:
        html += '<th class="month-start">Up%</th><th>Evt</th>'

    html += """
                        <th>Downtime</th>
                        <th>Uptime</th>
                        <th>Events</th>
                    </tr>
                </thead>
                <tbody>
    """

    for row in data:
        if dynamic_totals:
            totals = compute_ytd_totals(row, year)
            row = dict(row)
            row['total_downtime'] = totals['total_downtime']
            row['total_uptime%'] = str(totals['total_uptime%'])
            row['total_events'] = str(totals['total_events'])

        uptime_class = get_uptime_class(row.get('total_uptime%', 'N/A'))
        html += f'<tr class="site-row {uptime_class}">'
        html += f'<td class="site-name">{row["site"]}</td>'
        html += f'<td class="pods">{row["pods"]}</td>'

        for month in months:
            uptime = row.get(f'{month}_uptime%', 'N/A')
            events = row.get(f'{month}_events', '0')
            uptime_cell_class = get_uptime_class(uptime)

            html += f'<td class="uptime month-start {uptime_cell_class}">{uptime}%</td>' if uptime != 'N/A' else f'<td class="uptime month-start no-data">{uptime}</td>'
            html += f'<td class="events">{events}</td>'

        total_downtime = row.get('total_downtime', 'N/A')
        total_uptime = row.get('total_uptime%', 'N/A')
        total_events = row.get('total_events', 'N/A')

        html += f'<td class="total-downtime">{total_downtime}</td>'
        html += f'<td class="total-uptime {uptime_class}">{total_uptime}%</td>' if total_uptime != 'N/A' else f'<td class="total-uptime no-data">{total_uptime}</td>'
        html += f'<td class="total-events">{total_events}</td>'
        html += '</tr>'

    html += """
                </tbody>
            </table>
        </div>
    </div>
    """

    return html


def render_custom_results(results, start_date, end_date):
    """Render custom query results table."""
    html = f"""
    <div class="section">
        <h2>Custom Query Results: {start_date} to {end_date}</h2>
        <table class="downtime-table custom-results">
            <thead>
                <tr>
                    <th>Site</th>
                    <th>Pods</th>
                    <th>Total Downtime</th>
                    <th>Uptime %</th>
                    <th>Downtime Events</th>
                </tr>
            </thead>
            <tbody>
    """

    # Sort by uptime (lowest first to highlight problem sites)
    sorted_results = sorted(results, key=lambda x: x['uptime_percent'] if isinstance(x['uptime_percent'], (int, float)) else 999)

    for row in sorted_results:
        uptime_class = get_uptime_class(str(row['uptime_percent']))
        html += f'<tr class="site-row {uptime_class}">'
        html += f'<td class="site-name">{row["site"]}</td>'
        html += f'<td class="pods">{row["pods"]}</td>'
        html += f'<td class="downtime">{row["downtime_formatted"]}</td>'

        if row['uptime_percent'] == 'N/A':
            html += f'<td class="uptime no-data">{row["uptime_percent"]}</td>'
        else:
            html += f'<td class="uptime {uptime_class}">{row["uptime_percent"]}%</td>'

        html += f'<td class="events">{row["downtime_events"]}</td>'
        html += '</tr>'

    html += """
            </tbody>
        </table>
    </div>
    """

    return html


def main():
    print("Content-Type: text/html\n")

    form = cgi.FieldStorage()
    start_date = form.getvalue('start_date', '')
    end_date = form.getvalue('end_date', '')

    # Load yearly data
    yearly_data, yearly_error = load_yearly_data(CSV_2025_PATH)
    yearly_data_2026, yearly_error_2026 = load_yearly_data(CSV_2026_PATH)

    # Run custom query if dates provided
    custom_results = None
    custom_error = None
    if start_date and end_date:
        custom_results, custom_error = run_custom_query(start_date, end_date)

    # Calculate summary stats from 2025 data (weighted average by peak hashrate)
    total_sites = len(yearly_data)
    avg_uptime = 0
    if yearly_data:
        weighted_sum = 0
        total_weight = 0
        for row in yearly_data:
            try:
                uptime = float(row.get('total_uptime%', 0))
                weight = float(row.get('peak_hashrate_th', 0))
                if weight > 0:
                    weighted_sum += uptime * weight
                    total_weight += weight
            except:
                pass
        if total_weight > 0:
            avg_uptime = weighted_sum / total_weight

    # Calculate summary stats from 2026 data (weighted by pod count, dynamic YTD totals)
    total_sites_2026 = len(yearly_data_2026) if yearly_data_2026 else 0
    avg_uptime_2026 = 0
    avg_uptime_2026_has = False
    if yearly_data_2026:
        weighted_sum = 0
        total_weight = 0
        for row in yearly_data_2026:
            totals = compute_ytd_totals(row, 2026)
            try:
                uptime = float(totals['total_uptime%'])
                weight = float(row.get('pods', 0) or 0)
                if weight > 0:
                    weighted_sum += uptime * weight
                    total_weight += weight
            except (ValueError, TypeError):
                pass
        if total_weight > 0:
            avg_uptime_2026 = weighted_sum / total_weight
            avg_uptime_2026_has = True
    avg_uptime_2026_str = f"{avg_uptime_2026:.1f}%" if avg_uptime_2026_has else "N/A"

    # Default dates for the custom query form
    default_end = datetime.now().strftime('%Y-%m-%d')
    default_start = '2026-01-01'

    # Last-updated timestamp for the in-progress year CSV (refreshed by cron)
    csv_2026_mtime_str = ''
    csv_2026_age_str = ''
    try:
        mtime = datetime.fromtimestamp(os.path.getmtime(CSV_2026_PATH))
        csv_2026_mtime_str = mtime.strftime('%Y-%m-%d %H:%M')
        age_hours = (datetime.now() - mtime).total_seconds() / 3600
        if age_hours < 1:
            csv_2026_age_str = f"{int(age_hours * 60)}m ago"
        elif age_hours < 48:
            csv_2026_age_str = f"{int(age_hours)}h ago"
        else:
            csv_2026_age_str = f"{int(age_hours / 24)}d ago"
    except OSError:
        pass

    print(f"""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Site Non-Zero Hash based Uptime - NGON Mining</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <style>
        {generate_dropdown_css()}

        body {{
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            margin: 0;
            padding: 0;
            background-color: #1a1a1a;
            color: #e0e0e0;
            line-height: 1.4;
        }}

        /* Floating-box header (matches status/gen_manager) */
        .header {{
            background-color: rgba(255,255,255,0.05) !important;
            border: 1px solid rgba(255,255,255,0.1) !important;
            border-radius: 8px !important;
            padding: 30px !important;
            margin: 20px !important;
            display: flex !important;
            justify-content: space-between !important;
            align-items: flex-start !important;
            border-bottom: none !important;
        }}
        .header h1.dropdown-title {{
            font-size: 2.2em !important;
            font-weight: 600 !important;
            margin: 3px 0 0 0 !important;
            line-height: 1.2 !important;
            color: #00ff00 !important;
        }}
        .header .subtitle {{
            margin: 6px 0 0 0;
            color: #888;
            font-size: 14px;
        }}

        .summary-stats {{
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin: 0 20px 20px 20px;
        }}

        .stat-card {{
            background-color: rgba(255,255,255,0.08);
            border: 1px solid rgba(255,255,255,0.1);
            border-radius: 6px;
            padding: 15px;
            text-align: center;
        }}
        .stat-card h3 {{
            margin: 0 0 5px 0;
            font-size: 12px;
            color: #888;
            text-transform: uppercase;
            font-weight: 600;
        }}
        .stat-card h3 small {{
            font-size: 10px;
            text-transform: none;
            opacity: 0.8;
        }}
        .stat-card .value {{
            font-size: 24px;
            font-weight: bold;
            color: #00ff00;
        }}

        .section {{
            background-color: rgba(255,255,255,0.05);
            border: 1px solid rgba(255,255,255,0.1);
            border-radius: 8px;
            padding: 20px;
            margin: 0 20px 20px 20px;
            color: #e0e0e0;
        }}
        .section h2 {{
            margin-top: 0;
            color: #00ff00;
            border-bottom: 1px solid rgba(255,255,255,0.15);
            padding-bottom: 10px;
        }}
        .section p {{ color: #aaa; }}

        .controls {{
            background-color: rgba(255,255,255,0.05);
            border: 1px solid rgba(255,255,255,0.1);
            border-radius: 8px;
            padding: 15px;
            margin: 0 0 15px 0;
            display: flex;
            gap: 15px;
            align-items: center;
            flex-wrap: wrap;
        }}
        .controls label {{
            font-weight: 600;
            color: #888;
            font-size: 12px;
        }}
        .controls input[type="date"] {{
            padding: 8px 12px;
            border: 1px solid #3a3a3a;
            border-radius: 4px;
            background: #2a2a2a;
            color: #e0e0e0;
            font-size: 13px;
            color-scheme: dark;
        }}
        .controls button {{
            padding: 8px 16px;
            background: #00ff00;
            color: #000;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 600;
            font-size: 13px;
        }}
        .controls button:hover {{ background: #00cc00; }}

        .table-wrapper {{ overflow-x: auto; }}

        .downtime-table {{
            width: 100%;
            border-collapse: collapse;
            font-size: 12px;
            color: #e0e0e0;
        }}
        .downtime-table th {{
            background: rgba(0, 255, 0, 0.10);
            color: #00ff00;
            padding: 10px 6px;
            text-align: center;
            font-weight: 600;
            font-size: 11px;
            text-transform: uppercase;
            white-space: nowrap;
            border-bottom: 1px solid rgba(255,255,255,0.1);
        }}
        .downtime-table th.month-header {{
            background: rgba(0, 255, 0, 0.18);
            border-left: 2px solid rgba(255,255,255,0.2);
        }}
        .downtime-table th.month-header:first-of-type {{ border-left: none; }}
        .downtime-table th.total-header {{
            background: rgba(255,255,255,0.10);
            color: #fff;
            border-left: 2px solid rgba(255,255,255,0.2);
        }}
        .downtime-table .sub-header th {{
            background: rgba(0, 255, 0, 0.05);
            color: #aaa;
            font-size: 10px;
            padding: 6px 4px;
        }}
        .downtime-table td {{
            padding: 8px 6px;
            border-bottom: 1px solid rgba(255,255,255,0.05);
            text-align: center;
            white-space: nowrap;
            color: #e0e0e0;
        }}
        .downtime-table td:first-child {{ text-align: left; }}

        .site-row {{ transition: background-color 0.2s; }}
        .site-row:hover           {{ background-color: rgba(255,255,255,0.05); }}
        .site-row.excellent       {{ background-color: rgba(40, 167, 69, 0.20); }}
        .site-row.good            {{ background-color: rgba(40, 167, 69, 0.10); }}
        .site-row.warning         {{ background-color: rgba(255, 193, 7, 0.15); }}
        .site-row.critical        {{ background-color: rgba(220, 53, 69, 0.18); }}
        .site-row.no-data         {{ background-color: rgba(255,255,255,0.02); opacity: 0.6; }}

        .site-name {{ font-weight: 600; color: #e0e0e0; }}

        .uptime {{ font-weight: 600; }}
        .uptime.excellent {{ color: #00ff00; }}
        .uptime.good      {{ color: #5cb85c; }}
        .uptime.warning   {{ color: #ffaa00; }}
        .uptime.critical  {{ color: #ff4444; }}
        .uptime.no-data   {{ color: #666; }}

        .total-downtime, .total-uptime, .total-events {{
            font-weight: 600;
            background-color: rgba(255,255,255,0.04);
        }}
        .total-downtime {{ border-left: 2px solid rgba(255,255,255,0.2); }}
        .month-start    {{ border-left: 2px solid rgba(255,255,255,0.15); }}

        .custom-results {{ font-size: 14px; }}
        .custom-results th, .custom-results td {{ padding: 12px 10px; }}

        .error-message {{
            background: rgba(220, 53, 69, 0.15);
            border: 1px solid rgba(220, 53, 69, 0.4);
            color: #ff8888;
            padding: 15px;
            border-radius: 8px;
            margin: 0 20px 20px 20px;
        }}

        .data-freshness {{
            background: rgba(74, 158, 255, 0.12);
            border: 1px solid rgba(74, 158, 255, 0.3);
            color: #cfe3ff;
            padding: 8px 14px;
            border-radius: 6px;
            margin: 0 20px 12px 20px;
            font-size: 12px;
            border-left: 3px solid #00ff00;
        }}

        .loading {{
            text-align: center;
            padding: 20px;
            color: #888;
        }}

        .timestamp {{
            text-align: center;
            color: #666;
            margin: 20px;
            font-size: 12px;
        }}

        @media (max-width: 768px) {{
            .downtime-table {{ font-size: 10px; }}
            .downtime-table th,
            .downtime-table td {{ padding: 4px 2px; }}
            .controls {{ flex-direction: column; align-items: stretch; }}
        }}
    </style>
</head>
<body>
    <div class="header">
        <div>
            <div class="dropdown">
                <h1 class="dropdown-title">NGON Mining - Site Uptime</h1>
                <div class="dropdown-content">{generate_dropdown_html(_user_access)}</div>
            </div>
            <div class="subtitle">Downtime detected via data gaps and zero hashrate (site hashrate is zero)</div>
        </div>
    </div>

    <div class="summary-stats">
        <div class="stat-card">
            <h3>2026 Sites Tracked</h3>
            <div class="value">{total_sites_2026}</div>
        </div>
        <div class="stat-card">
            <h3>2026 Avg Uptime<br><small>(pod weighted, YTD)</small></h3>
            <div class="value">{avg_uptime_2026_str}</div>
        </div>
        <div class="stat-card">
            <h3>Data Period</h3>
            <div class="value">2026 YTD</div>
        </div>
    </div>

    <div class="summary-stats">
        <div class="stat-card">
            <h3>2025 Sites Tracked</h3>
            <div class="value">{total_sites}</div>
        </div>
        <div class="stat-card">
            <h3>2025 Avg Uptime<br><small>(peak hash weighted)</small></h3>
            <div class="value">{avg_uptime:.1f}%</div>
        </div>
        <div class="stat-card">
            <h3>Data Period</h3>
            <div class="value">2025 Full Year</div>
        </div>
    </div>
    """)

    # Render 2026 YTD table (above 2025 since it's the current year)
    current_year = datetime.now().year
    if csv_2026_mtime_str:
        print(f'<div class="data-freshness">Current-year data refreshed daily at 03:30. '
              f'Last update: <strong>{csv_2026_mtime_str}</strong> ({csv_2026_age_str}).</div>')
    if yearly_error_2026:
        print(f'<div class="error-message">Error loading 2026 data: {yearly_error_2026}</div>')
    elif yearly_data_2026:
        print(render_yearly_table(
            yearly_data_2026,
            2026,
            title='2026 YTD Site Uptime',
            dynamic_totals=True,
            total_label='YTD Total',
        ))

    # Render 2025 yearly data
    if yearly_error:
        print(f'<div class="error-message">Error loading 2025 data: {yearly_error}</div>')
    elif yearly_data:
        print(render_yearly_table(yearly_data, 2025, title='2025 Yearly Site Uptime'))
    else:
        print('<div class="error-message">No 2025 data available</div>')

    # Custom date range query section
    print(f"""
    <div class="section">
        <h2>Custom Date Range Query</h2>
        <p>Query historical uptime data for any date range to compare against 2025 performance.</p>

        <form method="get" class="controls">
            <label for="start_date">Start Date:</label>
            <input type="date" name="start_date" id="start_date" value="{start_date or default_start}">

            <label for="end_date">End Date:</label>
            <input type="date" name="end_date" id="end_date" value="{end_date or default_end}">

            <button type="submit">Run Query</button>
        </form>
    </div>
    """)

    # Render custom query results if available
    if custom_error:
        print(f'<div class="error-message">Error running query: {custom_error}</div>')
    elif custom_results:
        print(render_custom_results(custom_results, start_date, end_date))

    print(f"""
    <div class="timestamp">
        Page generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
    </div>
    <script>{generate_dropdown_js()}</script>
</body>
</html>
    """)


if __name__ == "__main__":
    main()
