#!/usr/bin/python3
import cgi, sys
sys.path.insert(0, '/opt/ngon/apps')
from managers.auth_manager import AuthManager, generate_login_page_html

_form = cgi.FieldStorage()
_auth = AuthManager('inventory_check')
_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("Inventory Comparison"))
    sys.exit(0)


import csv
import os
import sys
import json
from collections import defaultdict
from datetime import datetime

# Import universal navigation
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from links import generate_dropdown_html, generate_dropdown_css, generate_dropdown_js

# --- Configuration ---
# Define the absolute paths to your data files on the server.
MINERS_CSV_PATH = '/opt/ngon/data/live/miner_status.csv'
INVENTORY_CSV_PATH = '/opt/ngon/data/live/miner_inventory.csv'

def read_csv_file(file_path):
    """Reads a CSV file and returns its content as a list of dictionaries."""
    if not os.path.exists(file_path):
        return None, f"Error: File not found at {file_path}"
    try:
        with open(file_path, mode='r', encoding='utf-8') as infile:
            reader = csv.DictReader(infile)
            return list(reader), None
    except Exception as e:
        return None, f"Error reading or parsing {file_path}: {e}"

def get_partial_serial(serial):
    """
    Creates a partial serial key from the first 6 and last 6 characters.
    Returns None if the serial is too short.
    """
    if serial and len(serial) >= 12:
        return serial[:6] + serial[-6:]
    return None

def generate_results_table(title, data, headers, include_download_button=False):
    """Generates an HTML table string for a given set of results."""
    # Clean headers for display
    display_headers = [h for h in headers if not h.startswith('__')]

    table_html = f"""
    <div class="content-panel">
        <div class="panel-header">
            <div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
                <span>{title}</span>
    """
    
    # Add download button for the "Found Online Only" section
    if include_download_button and data:
        # Extract IP addresses and create CSV content directly
        ip_list = [item.get('ip', '') for item in data if item.get('ip', '').strip()]
        csv_content = "IP Address\n" + "\n".join(ip_list)
        
        # Create data URL for direct download
        import base64
        csv_bytes = csv_content.encode('utf-8')
        csv_b64 = base64.b64encode(csv_bytes).decode('utf-8')
        data_url = f"data:text/csv;base64,{csv_b64}"
        
        table_html += f'''
                <a href="{data_url}" 
                   download="unmatched_miners_ips.csv"
                   class="download-btn">
                    <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
                    </svg>
                    Download IP List ({len(ip_list)} IPs)
                </a>
        '''
    
    table_html += """
            </div>
        </div>
        <div class="panel-content">
    """
    
    if not data:
        table_html += '<div class="no-data">No discrepancies found in this category.</div>'
    else:
        table_html += '<table class="comparison-table"><thead><tr>'
        for header in display_headers:
            table_html += f'<th>{header.replace("_", " ").title()}</th>'
        table_html += '</tr></thead><tbody>'
        for item in data:
            table_html += '<tr>'
            for header in display_headers:
                value = item.get(header, '')
                table_html += f'<td>{value}</td>'
            table_html += '</tr>'
        table_html += '</tbody></table>'

    table_html += "</div></div>"
    return table_html

def get_inventory_file_timestamp():
    """Get the inventory.csv file timestamp information"""
    try:
        if not os.path.exists(INVENTORY_CSV_PATH):
            return "N/A", "File not found"
        
        # Get file modification time
        file_mtime = os.path.getmtime(INVENTORY_CSV_PATH)
        file_time = datetime.fromtimestamp(file_mtime)
        time_ago = datetime.now() - file_time
        
        # Format timestamp
        formatted_time = file_time.strftime('%m/%d %H:%M:%S')
        
        # Format time ago nicely
        if time_ago.total_seconds() < 60:
            time_ago_str = f"{int(time_ago.total_seconds())}s ago"
        elif time_ago.total_seconds() < 3600:
            time_ago_str = f"{int(time_ago.total_seconds()/60)}m ago"
        else:
            time_ago_str = f"{int(time_ago.total_seconds()/3600)}h ago"
        
        return formatted_time, time_ago_str
        
    except Exception as e:
        return "Error", str(e)

def generate_error_page(message):
    """Generates an HTML page to display a critical error."""
    return f"""
    <div class="container mx-auto p-4 md:p-8">
        <div class="bg-red-500/20 backdrop-blur-md p-6 rounded-lg shadow-lg">
            <div class="text-white">
                <strong class="font-bold text-lg">A critical error occurred:</strong>
                <span class="block mt-2">{message}</span>
            </div>
        </div>
    </div>
    """

def main():
    """Main function to perform comparison and generate HTML."""
    results_html = ""

    # Get inventory file timestamp
    inventory_timestamp, inventory_age = get_inventory_file_timestamp()

    miners_data, miners_error = read_csv_file(MINERS_CSV_PATH)
    inventory_data, inventory_error = read_csv_file(INVENTORY_CSV_PATH)

    if miners_error: results_html += generate_error_page(miners_error)
    if inventory_error: results_html += generate_error_page(inventory_error)

    if not results_html:
        # --- 1. Create efficient lookups for inventory data ---
        # Now using FULL serial numbers for primary matching
        inventory_by_full_serial = {item.get('Serial', ''): item for item in inventory_data if item.get('Serial')}
        inventory_by_mac = {item.get('MAC', '').upper(): item for item in inventory_data if item.get('MAC')}

        # --- 2. Perform the one-way check: Online Miners vs. Inventory ---
        mac_mismatches = []
        serial_mismatches = []
        unmatched_miners = []
        incomplete_miners = []

        used_inventory_items = set()

        for miner in miners_data:
            online_full_serial = miner.get('serial', '').strip()
            online_mac = miner.get('mac', '').strip().upper()

            # Check for miners with missing serial or MAC address
            if not online_full_serial or not online_mac:
                incomplete_miners.append({
                    'serial': miner.get('serial', ''),
                    'mac': miner.get('mac', ''),
                    'ip': miner.get('ip', ''),
                    'pod': miner.get('pod', ''),
                    'miner_type': miner.get('miner_type', ''),
                    'missing_data': ('Serial' if not online_full_serial else '') + 
                                   (' & MAC' if not online_full_serial and not online_mac else 
                                    'MAC' if not online_mac else '')
                })
                continue

            matched = False

            # Priority 1: Check for match by FULL serial number
            if online_full_serial and online_full_serial in inventory_by_full_serial:
                inventory_item = inventory_by_full_serial[online_full_serial]
                inventory_mac = inventory_item.get('MAC', '').upper()
                matched = True

                if online_mac != inventory_mac:
                    mac_mismatches.append({
                        'Full Serial (Matched)': online_full_serial,
                        'Online MAC': online_mac,
                        'Inventory MAC': inventory_mac,
                    })

                used_inventory_items.add(inventory_item.get('Serial'))
                used_inventory_items.add(inventory_item.get('MAC'))

            # Priority 2: If no full serial match, check for match by MAC
            if not matched and online_mac in inventory_by_mac:
                inventory_item = inventory_by_mac[online_mac]
                if inventory_item.get('Serial') not in used_inventory_items and inventory_item.get('MAC') not in used_inventory_items:
                    inventory_full_serial = inventory_item.get('Serial', '')
                    matched = True

                    if online_full_serial != inventory_full_serial:
                        serial_mismatches.append({
                            'MAC Address (Matched)': online_mac,
                            'Online Serial': online_full_serial,
                            'Inventory Serial': inventory_full_serial,
                        })

                    used_inventory_items.add(inventory_item.get('Serial'))
                    used_inventory_items.add(inventory_item.get('MAC'))

            # If still no match by either method, it's truly unmatched
            if not matched:
                unmatched_miners.append(miner)

        # --- 3. Generate HTML Sections for All Categories ---
        results_html += generate_results_table(
            'MAC Mismatch (Full Serial Match Found)', mac_mismatches,
            ['Full Serial (Matched)', 'Online MAC', 'Inventory MAC']
        )
        results_html += generate_results_table(
            'Full Serial Mismatch (MAC Match Found)', serial_mismatches,
            ['MAC Address (Matched)', 'Online Serial', 'Inventory Serial']
        )
        results_html += generate_results_table(
            'Miners Found Online Only (No Match Found)', unmatched_miners,
            ['serial', 'mac', 'ip', 'pod', 'miner_type'],
            include_download_button=True  # Enable download button for this section
        )
        results_html += generate_results_table(
            'Miners with Incomplete Data (Missing Serial/MAC)', incomplete_miners,
            ['serial', 'mac', 'ip', 'pod', 'miner_type', 'missing_data']
        )

    print("Content-type: text/html\n")
    print(f"""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Inventory Comparison Tool</title>
    <style>
        * {{ margin: 0; padding: 0; box-sizing: border-box; }}
        body {{
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: #1a1a1a;
            color: #e0e0e0;
            min-height: 100vh;
            padding: 20px;
            line-height: 1.4;
        }}
        .container {{ max-width: none; margin: 0 auto; }}
        .header {{
            background-color: rgba(255,255,255,0.05) !important;
            border-radius: 8px !important;
            padding: 30px !important;
            margin-bottom: 30px !important;
            border: 1px solid rgba(255,255,255,0.1) !important;
            border-bottom: none !important;
            display: flex !important;
            justify-content: space-between !important;
            align-items: flex-start !important;
        }}
        .header h1.dropdown-title {{
            font-size: 2.2em !important;
            color: #00ff00 !important;
            margin: 3px 0 0 0 !important;
            line-height: 1.2 !important;
            font-weight: 600 !important;
        }}
        .subtitle {{
            color: #888;
            font-size: 14px;
            margin-bottom: 20px;
        }}
        .stats-bar {{ 
            display: flex; 
            gap: 20px; 
            align-items: center; 
            flex-wrap: wrap; 
        }}
        .stat-item {{ 
            background-color: rgba(255,255,255,0.05); 
            color: #ffffff; 
            padding: 15px 25px; 
            border-radius: 6px; 
            font-weight: 600; 
            border: 1px solid rgba(255,255,255,0.1); 
        }}
        .refresh-btn {{
            background: #00ff00;
            color: #000;
            border: none;
            padding: 12px 24px;
            border-radius: 6px;
            cursor: pointer;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 8px;
            transition: background-color 0.2s;
        }}
        .refresh-btn:hover {{
            background: #00cc00;
        }}
        .refresh-btn:disabled {{
            background: #666;
            color: #aaa;
            cursor: not-allowed;
        }}
        .content-panel {{ 
            background-color: rgba(255,255,255,0.02); 
            border-radius: 6px; 
            border: 1px solid rgba(255,255,255,0.1); 
            margin-bottom: 30px;
            overflow: hidden;
        }}
        .panel-header {{ 
            background-color: rgba(255,255,255,0.05); 
            color: #ffffff; 
            padding: 20px; 
            font-size: 1.3em; 
            font-weight: 600; 
            border-bottom: 1px solid rgba(255,255,255,0.1); 
        }}
        .panel-content {{ 
            padding: 20px; 
        }}
        .comparison-table {{
            width: 100%;
            border-collapse: collapse;
            background-color: rgba(255,255,255,0.02);
            border-radius: 6px;
            overflow: hidden;
        }}
        .comparison-table th {{
            background-color: rgba(255,255,255,0.1);
            color: #ffffff;
            padding: 15px;
            text-align: left;
            font-weight: 600;
            border-bottom: 1px solid rgba(255,255,255,0.1);
        }}
        .comparison-table td {{
            padding: 12px 15px;
            border-bottom: 1px solid rgba(255,255,255,0.05);
            color: #ffffff;
        }}
        .comparison-table tr:nth-child(even) {{
            background-color: rgba(255,255,255,0.02);
        }}
        .comparison-table tr:hover {{
            background-color: rgba(255,255,255,0.05);
        }}
        .no-data {{
            text-align: center;
            padding: 40px;
            color: #888;
            font-style: italic;
        }}
        .download-btn {{
            background: #4a9eff;
            color: #fff;
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            display: inline-flex;
            align-items: center;
            gap: 6px;
            transition: background-color 0.2s;
        }}
        .download-btn:hover {{
            background: #3a8eef;
        }}
        .spinner {{
            width: 16px;
            height: 16px;
            border: 2px solid transparent;
            border-top: 2px solid currentColor;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }}
        @keyframes spin {{
            0% {{ transform: rotate(0deg); }}
            100% {{ transform: rotate(360deg); }}
        }}
        
        {generate_dropdown_css()}
    </style>
    <script>
        function downloadIPs(ipList) {{
            if (ipList.length === 0) {{
                alert('No IP addresses found to download.');
                return;
            }}
            
            // Create CSV content
            let csvContent = "IP Address\\n";
            ipList.forEach(ip => {{
                if (ip && ip.trim()) {{
                    csvContent += ip.trim() + "\\n";
                }}
            }});
            
            // Create and download file
            const blob = new Blob([csvContent], {{ type: 'text/csv;charset=utf-8;' }});
            const link = document.createElement("a");
            const url = URL.createObjectURL(blob);
            link.setAttribute("href", url);
            link.setAttribute("download", "unmatched_miners_ips.csv");
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            URL.revokeObjectURL(url);
        }}
        
        function refreshInventory() {{
            const button = document.getElementById('refresh-btn');
            const spinner = document.getElementById('refresh-spinner');
            const buttonText = document.getElementById('refresh-text');
            
            // Disable button and show loading state
            button.disabled = true;
            spinner.style.display = 'block';
            buttonText.textContent = 'Refreshing...';
            
            // Make AJAX call to refresh script
            fetch('./refresh_inventory.py', {{
                method: 'GET'
            }})
            .then(response => response.json())
            .then(data => {{
                if (data.success) {{
                    // Reload the page after successful refresh
                    window.location.reload();
                }} else {{
                    alert('Error refreshing inventory: ' + data.error);
                    // Re-enable button on error
                    button.disabled = false;
                    spinner.style.display = 'none';
                    buttonText.textContent = 'Refresh Inventory';
                }}
            }})
            .catch(error => {{
                alert('Error refreshing inventory: ' + error);
                // Re-enable button on error
                button.disabled = false;
                spinner.style.display = 'none';
                buttonText.textContent = 'Refresh Inventory';
            }});
        }}
        // Mobile dropdown functionality
        document.addEventListener('DOMContentLoaded', function() {{
            const dropdownTitle = document.querySelector('.dropdown-title');
            const dropdownContent = document.querySelector('.dropdown-content');
            
            if (dropdownTitle && dropdownContent) {{
                dropdownTitle.addEventListener('click', function(e) {{
                    if (window.innerWidth <= 768) {{
                        e.preventDefault();
                        dropdownContent.classList.toggle('show');
                    }}
                }});
                
                // Close dropdown when clicking outside
                document.addEventListener('click', function(e) {{
                    if (!e.target.closest('.dropdown') && dropdownContent.classList.contains('show')) {{
                        dropdownContent.classList.remove('show');
                    }}
                }});
                
                // Close dropdown when window is resized to desktop
                window.addEventListener('resize', function() {{
                    if (window.innerWidth > 768) {{
                        dropdownContent.classList.remove('show');
                    }}
                }});
            }}
        }});
        
        // Add dropdown navigation JavaScript
        {generate_dropdown_js()}
    </script>
</head>
<body>
<div class="header">
    <div>
        <div class="dropdown">
            <h1 class="dropdown-title">NGON Mining - Inventory Check</h1>
            <div class="dropdown-content">{generate_dropdown_html(AuthManager.get_user_access())}</div>
        </div>
        <p class="subtitle">Comparing <code>{os.path.basename(MINERS_CSV_PATH)}</code> against <code>{os.path.basename(INVENTORY_CSV_PATH)}</code> using full serial numbers</p>
        <div class="stats-bar">
            <button id="refresh-btn" onclick="refreshInventory()" class="refresh-btn">
                <div id="refresh-spinner" class="spinner" style="display: none;"></div>
                <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
                </svg>
                <span id="refresh-text">Refresh Inventory</span>
            </button>
            <div class="stat-item">Last updated: {inventory_timestamp}</div>
            <div class="stat-item">({inventory_age})</div>
        </div>
    </div>
    <div></div>
</div>
<div class="container">
    
    {results_html}
</div>
</body>
</html>
    """)

if __name__ == "__main__":
    main()
