"""One-shot web server for collecting secrets from the user.

Replaces all platform-specific dialog code (osascript, zenity, kdialog,
PowerShell, tkinter) with a single HTML form served on localhost.

Security:
  - Binds to 127.0.0.1 only (not reachable from network)
  - Port 0 lets the OS pick a free port (no collisions)
  - One-time nonce in the URL prevents other localhost processes from submitting
  - Server accepts exactly one POST, then shuts down
  - Total lifetime: ~2-5 seconds (however long the user takes to type)

The AI agent never sees the secret — it travels from the browser form
to a localhost POST to this module's return value. Never logged, never
in stdout, never in MCP tool results.
"""

import secrets
import threading
import webbrowser
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import parse_qs

# HTML template — single page, auto-focuses the input, submits and closes
_HTML = """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{title}</title>
<style>
  * {{ margin: 0; padding: 0; box-sizing: border-box; }}
  body {{
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: #0f172a;
    color: #e2e8f0;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
  }}
  .card {{
    background: #1e293b;
    border: 1px solid #334155;
    border-radius: 12px;
    padding: 32px;
    width: 420px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.5);
  }}
  h1 {{
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 8px;
    color: #f8fafc;
  }}
  .subtitle {{
    font-size: 13px;
    color: #94a3b8;
    margin-bottom: 24px;
    line-height: 1.5;
  }}
  label {{
    font-size: 13px;
    font-weight: 500;
    color: #cbd5e1;
    display: block;
    margin-bottom: 6px;
  }}
  input {{
    width: 100%;
    padding: 10px 12px;
    background: #0f172a;
    border: 1px solid #475569;
    border-radius: 6px;
    color: #f1f5f9;
    font-size: 15px;
    font-family: inherit;
    outline: none;
    transition: border-color 0.15s;
  }}
  input:focus {{
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59,130,246,0.15);
  }}
  button {{
    width: 100%;
    margin-top: 20px;
    padding: 10px;
    background: #3b82f6;
    color: white;
    border: none;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s;
  }}
  button:hover {{ background: #2563eb; }}
  .lock {{
    font-size: 24px;
    margin-bottom: 12px;
  }}
  .done {{
    text-align: center;
    padding: 48px 32px;
  }}
  .done h1 {{
    color: #4ade80;
  }}
</style>
</head>
<body>
<div class="card" id="form-card">
  <div class="lock">&#128274;</div>
  <h1>{title}</h1>
  <div class="subtitle">{message}</div>
  <form method="POST" action="/{nonce}" id="pin-form">
    <label for="secret">{label}</label>
    <input type="{input_type}" id="secret" name="secret" autocomplete="off" autofocus required>
    <button type="submit">Submit</button>
  </form>
</div>
<div class="card done" id="done-card" style="display:none">
  <h1>Done</h1>
  <div class="subtitle">You can close this tab.</div>
</div>
<script>
document.getElementById('pin-form').addEventListener('submit', function(e) {{
  e.preventDefault();
  var fd = new FormData(this);
  fetch('/{nonce}', {{ method: 'POST', body: new URLSearchParams(fd) }})
    .then(function() {{
      document.getElementById('form-card').style.display = 'none';
      document.getElementById('done-card').style.display = 'block';
      setTimeout(function() {{ window.close(); }}, 500);
    }});
}});
</script>
</body>
</html>"""


def prompt_secret(title: str, message: str, hidden: bool = True, label: str = "") -> str:
    """Open a browser tab with a form, collect one secret, shut down.

    Args:
        title: Page/card title (e.g. "clawdflare authorization").
        message: Explanatory text shown above the input.
        hidden: If True, input is masked (password field).
        label: Input field label. Defaults to title if empty.

    Returns:
        The value the user entered.

    Raises:
        RuntimeError: If the user closes the tab without submitting,
                      or the server times out (60s).
    """
    nonce = secrets.token_urlsafe(32)
    result = {}

    if not label:
        label = title

    html = _HTML.format(
        title=title,
        message=message.replace("\\n", "<br>"),
        nonce=nonce,
        input_type="password" if hidden else "text",
        label=label,
    )

    class Handler(BaseHTTPRequestHandler):
        def do_GET(self):
            if self.path != f"/{nonce}":
                self.send_error(404)
                return
            self.send_response(200)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.end_headers()
            self.wfile.write(html.encode())

        def do_POST(self):
            if self.path != f"/{nonce}":
                self.send_error(404)
                return
            length = int(self.headers.get("Content-Length", 0))
            body = self.rfile.read(length).decode()
            params = parse_qs(body)
            value = params.get("secret", [""])[0]
            if value:
                result["value"] = value

            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.end_headers()
            self.wfile.write(b"ok")

            # Shut down after this response
            threading.Thread(target=self.server.shutdown, daemon=True).start()

        def log_message(self, format, *args):
            # Suppress all request logging — secrets must not appear in stdout
            pass

    server = HTTPServer(("127.0.0.1", 0), Handler)
    port = server.server_address[1]

    server_thread = threading.Thread(target=server.serve_forever, daemon=True)
    server_thread.start()

    webbrowser.open(f"http://127.0.0.1:{port}/{nonce}")

    # Block until the user submits (or 120s timeout)
    server_thread.join(timeout=120)
    server.server_close()

    if "value" not in result:
        raise RuntimeError("No input received — dialog was closed or timed out.")

    return result["value"]


def prompt_setup_wizard(accounts_exist: bool = False) -> dict:
    """Open a browser-based setup wizard that collects all setup fields at once.

    Returns a dict with keys:
        account, read_token_name, read_token, write_token_name, write_token, pin

    Raises:
        RuntimeError: On timeout, cancellation, or validation failure.
    """
    nonce = secrets.token_urlsafe(32)
    result = {}

    html = _WIZARD_HTML.format(nonce=nonce, accounts_exist="true" if accounts_exist else "false")

    class Handler(BaseHTTPRequestHandler):
        def do_GET(self):
            if self.path != f"/{nonce}":
                self.send_error(404)
                return
            self.send_response(200)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.end_headers()
            self.wfile.write(html.encode())

        def do_POST(self):
            if self.path != f"/{nonce}":
                self.send_error(404)
                return
            length = int(self.headers.get("Content-Length", 0))
            body = self.rfile.read(length).decode()
            params = parse_qs(body)

            for key in ("account", "read_token_name", "read_token",
                        "write_token_name", "write_token", "pin"):
                result[key] = params.get(key, [""])[0]

            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.end_headers()
            self.wfile.write(b"ok")
            threading.Thread(target=self.server.shutdown, daemon=True).start()

        def log_message(self, format, *args):
            pass

    server = HTTPServer(("127.0.0.1", 0), Handler)
    port = server.server_address[1]

    server_thread = threading.Thread(target=server.serve_forever, daemon=True)
    server_thread.start()

    webbrowser.open(f"http://127.0.0.1:{port}/{nonce}")

    server_thread.join(timeout=300)  # setup takes longer
    server.server_close()

    if "account" not in result or not result.get("pin"):
        raise RuntimeError("Setup was not completed — wizard was closed or timed out.")

    return result


_WIZARD_HTML = """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>clawdflare setup</title>
<style>
  * {{ margin: 0; padding: 0; box-sizing: border-box; }}
  body {{
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: #0f172a;
    color: #e2e8f0;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
  }}
  .card {{
    background: #1e293b;
    border: 1px solid #334155;
    border-radius: 12px;
    padding: 32px;
    width: 480px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.5);
  }}
  h1 {{
    font-size: 20px;
    font-weight: 600;
    margin-bottom: 4px;
    color: #f8fafc;
  }}
  .tagline {{
    font-size: 13px;
    color: #64748b;
    margin-bottom: 24px;
  }}
  .section {{
    margin-bottom: 20px;
  }}
  .section-title {{
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #64748b;
    margin-bottom: 12px;
    padding-bottom: 6px;
    border-bottom: 1px solid #334155;
  }}
  .field {{
    margin-bottom: 14px;
  }}
  label {{
    font-size: 13px;
    font-weight: 500;
    color: #cbd5e1;
    display: block;
    margin-bottom: 4px;
  }}
  .hint {{
    font-size: 11px;
    color: #64748b;
    margin-bottom: 4px;
  }}
  input {{
    width: 100%;
    padding: 9px 12px;
    background: #0f172a;
    border: 1px solid #475569;
    border-radius: 6px;
    color: #f1f5f9;
    font-size: 14px;
    font-family: inherit;
    outline: none;
    transition: border-color 0.15s;
  }}
  input:focus {{
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59,130,246,0.15);
  }}
  input.error {{
    border-color: #ef4444;
    box-shadow: 0 0 0 3px rgba(239,68,68,0.15);
  }}
  .error-msg {{
    color: #ef4444;
    font-size: 12px;
    margin-top: 4px;
    display: none;
  }}
  button {{
    width: 100%;
    margin-top: 8px;
    padding: 10px;
    background: #3b82f6;
    color: white;
    border: none;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s;
  }}
  button:hover {{ background: #2563eb; }}
  button:disabled {{
    background: #475569;
    cursor: not-allowed;
  }}
  .done {{
    text-align: center;
    padding: 48px 32px;
  }}
  .done h1 {{ color: #4ade80; }}
</style>
</head>
<body>
<div class="card" id="wizard">
  <h1>&#128274; clawdflare setup</h1>
  <div class="tagline">All fields stay on your machine. The AI never sees them.</div>

  <form id="setup-form">
    <div class="section">
      <div class="section-title">Account</div>
      <div class="field">
        <label for="account">Account name</label>
        <div class="hint">A label for this Cloudflare account (e.g. "personal", "work")</div>
        <input type="text" id="account" name="account" value="default" required autofocus>
      </div>
    </div>

    <div class="section">
      <div class="section-title">Read Token (read-only access)</div>
      <div class="field">
        <label for="read_token_name">Token name in Cloudflare</label>
        <div class="hint">The name you gave it in the dashboard — for your reference when rotating</div>
        <input type="text" id="read_token_name" name="read_token_name" placeholder="e.g. clawdflare-read" required>
      </div>
      <div class="field">
        <label for="read_token">API token</label>
        <div class="hint">Zone:Read, Zone Settings:Read permissions</div>
        <input type="password" id="read_token" name="read_token" required>
      </div>
    </div>

    <div class="section">
      <div class="section-title">Write Token (PIN-protected)</div>
      <div class="field">
        <label for="write_token_name">Token name in Cloudflare</label>
        <input type="text" id="write_token_name" name="write_token_name" placeholder="e.g. clawdflare-write" required>
      </div>
      <div class="field">
        <label for="write_token">API token</label>
        <div class="hint">Zone Settings:Edit permission — will be encrypted</div>
        <input type="password" id="write_token" name="write_token" required>
      </div>
    </div>

    <div class="section">
      <div class="section-title">PIN</div>
      <div class="field">
        <label for="pin">Choose a PIN (min 6 characters)</label>
        <div class="hint">You'll type this each time you authorize a write operation</div>
        <input type="password" id="pin" name="pin" minlength="6" required>
      </div>
      <div class="field">
        <label for="pin_confirm">Confirm PIN</label>
        <input type="password" id="pin_confirm" minlength="6" required>
        <div class="error-msg" id="pin-error">PINs don't match</div>
      </div>
    </div>

    <button type="submit" id="submit-btn">Save &amp; Encrypt</button>
  </form>
</div>

<div class="card done" id="done-card" style="display:none">
  <h1>Setup complete</h1>
  <div class="tagline">Read token saved. Write token encrypted. You can close this tab.</div>
</div>

<script>
document.getElementById('setup-form').addEventListener('submit', function(e) {{
  e.preventDefault();

  var pin = document.getElementById('pin').value;
  var confirm = document.getElementById('pin_confirm').value;
  var errMsg = document.getElementById('pin-error');

  if (pin !== confirm) {{
    document.getElementById('pin_confirm').classList.add('error');
    errMsg.style.display = 'block';
    return;
  }}
  errMsg.style.display = 'none';
  document.getElementById('pin_confirm').classList.remove('error');

  var btn = document.getElementById('submit-btn');
  btn.disabled = true;
  btn.textContent = 'Encrypting...';

  var fd = new FormData(this);
  fetch('/{nonce}', {{ method: 'POST', body: new URLSearchParams(fd) }})
    .then(function() {{
      document.getElementById('wizard').style.display = 'none';
      document.getElementById('done-card').style.display = 'block';
      setTimeout(function() {{ window.close(); }}, 1000);
    }});
}});
</script>
</body>
</html>"""
