#!/usr/bin/env python3
"""
P44 OCR Viewer launcher.
Starts a local HTTP server at port 5044 and opens the viewer in the default browser.
Run from any directory; the server root is always the project folder (this file's location).
If port 5044 is already in use (server already running), just opens the browser tab.
"""
import http.server
import webbrowser
import threading
import socket
import os
import sys
import json
from urllib.parse import urlparse, parse_qs

PORT = 5044
ROOT     = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(ROOT, 'viewer', 'data')


class QuietHandler(http.server.SimpleHTTPRequestHandler):
    """Serve files from ROOT, suppress request logs, disable browser caching."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, directory=ROOT, **kwargs)

    def log_message(self, fmt, *args):
        pass  # silence per-request noise

    def end_headers(self):
        self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate')
        self.send_header('Pragma', 'no-cache')
        self.send_header('Expires', '0')
        super().end_headers()

    def do_POST(self):
        """Handle POST /save-flags, /save-pass2-flags, /save-corrections, /clear-all-data.
        All endpoints accept an optional ?diary=ID query param to use per-diary files.
        """
        parsed = urlparse(self.path)
        qs     = parse_qs(parsed.query)
        diary  = qs.get('diary', ['unknown'])[0]
        # Sanitize: only allow alphanumeric, hyphens, underscores
        diary  = ''.join(c for c in diary if c.isalnum() or c in '-_')
        if not diary:
            diary = 'unknown'

        length = int(self.headers.get('Content-Length', 0))
        body   = self.rfile.read(length)
        try:
            data = json.loads(body)
        except json.JSONDecodeError:
            self.send_response(400); self.end_headers()
            return

        if parsed.path == '/save-flags':
            os.makedirs(DATA_DIR, exist_ok=True)
            dest = os.path.join(DATA_DIR, 'flags-' + diary + '.json')
            with open(dest, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2)
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            self.wfile.write(b'{"ok":true}')

        elif parsed.path == '/save-corrections':
            os.makedirs(DATA_DIR, exist_ok=True)
            dest = os.path.join(DATA_DIR, 'corrections-' + diary + '.json')
            existing = {}
            if os.path.exists(dest):
                try:
                    with open(dest, 'r', encoding='utf-8') as f:
                        existing = json.load(f)
                except Exception:
                    pass
            page       = str(data.get('page', ''))
            correction = data.get('correction')
            if correction:
                existing[page] = correction
            else:
                existing.pop(page, None)
            with open(dest, 'w', encoding='utf-8') as f:
                json.dump(existing, f, indent=2)
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            self.wfile.write(b'{"ok":true}')

        elif parsed.path == '/save-pass2-flags':
            os.makedirs(DATA_DIR, exist_ok=True)
            dest = os.path.join(DATA_DIR, 'pass2_flags-' + diary + '.json')
            try:
                with open(dest, 'w', encoding='utf-8') as f:
                    json.dump(data, f, indent=2)
            except Exception:
                self.send_response(500); self.end_headers()
                return
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            self.wfile.write(b'{"ok":true}')

        elif parsed.path == '/clear-all-data':
            for prefix in ['flags-', 'corrections-', 'pass2_flags-']:
                fpath = os.path.join(DATA_DIR, prefix + diary + '.json')
                if os.path.exists(fpath):
                    try:
                        os.remove(fpath)
                    except Exception:
                        pass
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            self.wfile.write(b'{"ok":true}')

        else:
            self.send_response(404); self.end_headers()


def port_in_use(port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        return s.connect_ex(('localhost', port)) == 0


def start_server():
    server = http.server.HTTPServer(('localhost', PORT), QuietHandler)
    server.serve_forever()


if __name__ == '__main__':
    url = 'http://localhost:{}/viewer/p44-landing.html'.format(PORT)

    if port_in_use(PORT):
        print('\n  P44 OCR Viewer  (server already running)')
        print('  URL: {}\n'.format(url))
        webbrowser.open(url)
    else:
        t = threading.Thread(target=start_server, daemon=True)
        t.start()
        print('\n  P44 OCR Viewer')
        print('  Serving : {}'.format(ROOT))
        print('  URL     : {}\n'.format(url))
        webbrowser.open(url)

    print('Press Enter (or Ctrl-C) to stop the server.\n')
    try:
        input()
    except KeyboardInterrupt:
        pass
    print('Server stopped.')
    sys.exit(0)

