#!/usr/bin/env python3
"""
PocketBase v0.23+ Collection Schema Setup
Complete Firebase,PocketBase migration: define all fields for every collection,
then apply userId-based multi-tenancy rules.

The two-step process (schema first with empty rules, then rules) is required
because PocketBase validates rule expressions against existing fields.

IMPORTANT: This list MUST include EVERY field the web app writes.
PocketBase silently drops fields not in the schema (HTTP 200, no error).
Compare against: grep -rn '\.write\|formData\.\|newRO\.\|updateData\.\|financial\b' *.js
to find all written fields before deployment.
"""

import urllib.request, json, ssl, sys

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

BASE = "https://grajmedia.duckdns.org:3447"  # change to your domain
ADMIN_EMAIL = "admin@example.com"
ADMIN_PASS = "your-password"

def api(method, path, data=None, token=None):
    headers = {"Content-Type": "application/json"}
    if token:
        headers["Authorization"] = token
    req = urllib.request.Request(
        f"{BASE}{path}",
        data=json.dumps(data).encode() if data else None,
        headers=headers,
        method=method
    )
    try:
        resp = urllib.request.urlopen(req, context=ctx)
        if resp.status == 204:
            return {}
        return json.loads(resp.read())
    except urllib.error.HTTPError as e:
        body = e.read().decode()
        print(f"  ERROR {e.code}: {body[:200]}")
        return None

# --- Auth ---
auth = api("POST", "/api/collections/_superusers/auth-with-password",
    {"identity": ADMIN_EMAIL, "password": ADMIN_PASS})
token = auth["token"]

# --- Collection Schemas ---
# Note: v0.23+ uses 'fields' key (not 'schema' from v0.22)
# Include the system 'id' field when PATCHing -- PocketBase requires it
ID_FIELD = {
    "autogeneratePattern": "[a-z0-9]{15}",
    "name": "id", "type": "text", "system": True, "required": True,
    "max": 15, "min": 15, "pattern": "^[a-z0-9]+$",
    "presentable": False, "primaryKey": True
}

# IMPORTANT: Every field the web app's JavaScript writes MUST be in this list.
# Missing fields are silently dropped by PocketBase on write.
NEW_FIELDS = {
    "repairOrders": [
        {"name": "userId", "type": "text", "required": True},
        {"name": "customerName", "type": "text"},
        {"name": "customerEmail", "type": "text"},
        {"name": "customerPhone", "type": "text"},
        {"name": "deviceModel", "type": "text"},
        {"name": "deviceType", "type": "text"},
        {"name": "issue", "type": "text"},
        {"name": "status", "type": "text"},                     # waiter / drop-off
        {"name": "workStatus", "type": "text"},                 # in-progress / waiting-for-parts / completed / waiting-for-pickup
        {"name": "roNumber", "type": "text"},                   # RO-XXXXXXX
        {"name": "vehicleInfo", "type": "text"},                # "2020 Honda Accord"
        {"name": "vin", "type": "text"},                        # 17-char VIN
        {"name": "mileage", "type": "text"},
        {"name": "services", "type": "text"},                   # comma-separated service names
        {"name": "estimatedTime", "type": "text"},              # hours as string
        {"name": "promisedTime", "type": "text"},               # ISO datetime string
        {"name": "lastModified", "type": "text"},               # ISO datetime string
        {"name": "completedTime", "type": "text"},              # ISO datetime string
        {"name": "financial", "type": "json"},                  # {cpTotal,cpCost,whTotal,whCost,grossProfit,...}
        {"name": "priority", "type": "text"},
        {"name": "createdAt", "type": "text"},
        {"name": "updatedAt", "type": "text"},
        {"name": "lastActivity", "type": "text"},
        {"name": "technician", "type": "text"},
        {"name": "notes", "type": "text"},
        {"name": "price", "type": "number"},
        {"name": "customerId", "type": "text"},
        {"name": "quoteId", "type": "text"},
        {"name": "invoiceId", "type": "text"},
        {"name": "writeupTime", "type": "text"},
    ],
    "appointments": [
        {"name": "userId", "type": "text", "required": True},
        {"name": "customerName", "type": "text"},
        {"name": "customerEmail", "type": "text"},
        {"name": "customerPhone", "type": "text"},
        {"name": "appointmentDateTime", "type": "text"},
        {"name": "vehicleInfo", "type": "text"},
        {"name": "serviceType", "type": "text"},
        {"name": "lastModified", "type": "text"},
        {"name": "status", "type": "text"},
        {"name": "type", "type": "text"},
        {"name": "notes", "type": "text"},
        {"name": "createdAt", "type": "text"},
        {"name": "updatedAt", "type": "text"},
        {"name": "repairOrderId", "type": "text"},
        {"name": "duration", "type": "number"},
    ],
    "quotes": [
        {"name": "userId", "type": "text", "required": True},
        {"name": "customerName", "type": "text"},
        {"name": "customerEmail", "type": "text"},
        {"name": "customerPhone", "type": "text"},
        {"name": "deviceModel", "type": "text"},
        {"name": "deviceType", "type": "text"},
        {"name": "issue", "type": "text"},
        {"name": "status", "type": "text"},
        {"name": "createdAt", "type": "text"},
        {"name": "updatedAt", "type": "text"},
        {"name": "subtotal", "type": "number"},
        {"name": "tax", "type": "number"},
        {"name": "total", "type": "number"},
        {"name": "notes", "type": "text"},
        {"name": "customerId", "type": "text"},
        {"name": "repairOrderId", "type": "text"},
    ],
    "customers": [
        {"name": "userId", "type": "text", "required": True},
        {"name": "name", "type": "text"},
        {"name": "email", "type": "text"},
        {"name": "phone", "type": "text"},
        {"name": "address", "type": "text"},
        {"name": "vehicleInfo", "type": "text"},
        {"name": "vin", "type": "text"},
        {"name": "lastModified", "type": "text"},
        {"name": "lastService", "type": "text"},
        {"name": "preferredContact", "type": "text"},           # phone / email / text
        {"name": "notes", "type": "text"},
        {"name": "createdAt", "type": "text"},
        {"name": "updatedAt", "type": "text"},
        {"name": "lastVisit", "type": "text"},
        {"name": "totalVisits", "type": "number"},
    ],
    "services": [
        {"name": "userId", "type": "text", "required": True},
        {"name": "name", "type": "text", "required": True},
        {"name": "description", "type": "text"},
        {"name": "price", "type": "number"},
        {"name": "category", "type": "text"},
        {"name": "duration", "type": "number"},
        {"name": "createdAt", "type": "text"},
        {"name": "updatedAt", "type": "text"},
    ],
    "tasks": [
        {"name": "userId", "type": "text", "required": True},
        {"name": "title", "type": "text", "required": True},
        {"name": "description", "type": "text"},
        {"name": "status", "type": "text"},
        {"name": "priority", "type": "text"},
        {"name": "dueDate", "type": "text"},
        {"name": "createdAt", "type": "text"},
        {"name": "updatedAt", "type": "text"},
        {"name": "assignedTo", "type": "text"},
        {"name": "repairOrderId", "type": "text"},
    ],
    "settings": [
        {"name": "userId", "type": "text", "required": True},
        {"name": "name", "type": "text", "required": True},
    ],
}

RULES = {
    "listRule": "userId = @request.auth.id",
    "viewRule": "userId = @request.auth.id",
    "createRule": "@request.auth.id != ''",
    "updateRule": "userId = @request.auth.id",
    "deleteRule": "userId = @request.auth.id",
}

# --- Apply schemas ---
cols = api("GET", "/api/collections", token=token)
col_map = {c["name"]: c["id"] for c in cols.get("items", []) if c["type"] == "base"}

for name, new_fields in NEW_FIELDS.items():
    print(f"\n=== {name} ===")
    col_id = col_map.get(name)
    if not col_id:
        print(f"  Collection not found -- create it first")
        continue

    # Get existing fields (especially the system 'id' field)
    full = api("GET", f"/api/collections/{col_id}", token=token)
    if not full:
        continue
    existing = full.get("fields", [])
    existing_names = {f["name"] for f in existing}

    # Merge: keep all existing fields, add new ones
    merged = existing.copy()
    added = 0
    for nf in new_fields:
        if nf["name"] not in existing_names:
            if "required" not in nf:
                nf["required"] = False
            merged.append(nf)
            added += 1

    if added == 0:
        print(f"  All fields already exist -- skipping")
        continue

    # Step 1: Set fields with empty rules
    result = api("PATCH", f"/api/collections/{col_id}", {
        "fields": merged,
        "listRule": "", "viewRule": "", "createRule": "",
        "updateRule": "", "deleteRule": ""
    }, token=token)

    if result:
        fields_out = [f["name"] for f in result.get("fields", [])]
        print(f"  ✓ Added {added} fields , {fields_out}")

        # Step 2: Apply rules now that fields exist
        result2 = api("PATCH", f"/api/collections/{col_id}", RULES, token=token)
        if result2:
            print(f"  ✓ Rules applied")
        else:
            print(f"  ✗ Rules failed")
    else:
        print(f"  ✗ Schema update failed")

print("\nDone!")
