import secrets
from nicegui import ui
import db
import auth as auth_module
import layout


@ui.page("/tokens")
async def tokens_page():
    await auth_module.sync_session()
    if not auth_module.is_authenticated():
        ui.navigate.to("/login")
        return

    user = auth_module.current_user()

    async def load_tokens():
        return await db.fetch(
            """
            SELECT id, name, created_at, expires_at, revoked_at
            FROM   api_token
            WHERE  user_id = (SELECT id FROM user_account WHERE email = $1)
            ORDER  BY created_at DESC
            """,
            user["email"],
        )

    with layout.page("API Tokens"):
        ui.label(
            "API tokens let external systems (scripts, notebooks, third-party tools) "
            "query your data without using your email address. "
            "Each token carries the same access rights as your account."
        ).classes("text-sm text-gray-500 -mt-4")

        token_area = ui.column().classes("w-full gap-3")

        async def refresh():
            token_area.clear()
            rows = await load_tokens()
            with token_area:
                if not rows:
                    ui.label("No tokens yet.").classes("text-sm text-gray-400 italic")
                    return

                columns = [
                    {"name": "name", "label": "Name", "field": "name", "align": "left"},
                    {
                        "name": "created_at",
                        "label": "Created",
                        "field": "created_at",
                        "align": "left",
                    },
                    {
                        "name": "expires_at",
                        "label": "Expires",
                        "field": "expires_at",
                        "align": "left",
                    },
                    {
                        "name": "status",
                        "label": "Status",
                        "field": "status",
                        "align": "center",
                    },
                    {
                        "name": "actions",
                        "label": "",
                        "field": "actions",
                        "align": "right",
                    },
                ]
                row_data = [
                    {
                        "id": str(r["id"]),
                        "name": r["name"],
                        "created_at": r["created_at"].strftime("%Y-%m-%d %H:%M"),
                        "expires_at": r["expires_at"].strftime("%Y-%m-%d")
                        if r["expires_at"]
                        else "Never",
                        "status": "Revoked" if r["revoked_at"] else "Active",
                    }
                    for r in rows
                ]

                tbl = ui.table(columns=columns, rows=row_data, row_key="id").classes(
                    "w-full"
                )
                tbl.add_slot(
                    "body-cell-status",
                    """
                    <q-td :props="props">
                        <q-badge :color="props.row.status === 'Active' ? 'positive' : 'negative'">
                            {{ props.row.status }}
                        </q-badge>
                    </q-td>
                """,
                )
                tbl.add_slot(
                    "body-cell-actions",
                    """
                    <q-td :props="props">
                        <q-btn v-if="props.row.status === 'Active'"
                            flat round dense icon="block" color="negative"
                            @click="$parent.$emit('revoke', props.row)" />
                    </q-td>
                """,
                )
                tbl.on(
                    "revoke",
                    lambda e: _confirm_revoke(e.args["id"], e.args["name"], refresh),
                )

        # ── generate button ──────────────────────────────────────────────────
        with ui.row().classes("w-full items-center justify-between -mt-2"):
            ui.label("").classes("flex-1")
            ui.button(
                "Generate token",
                icon="add",
                on_click=lambda: _generate_dialog(user["email"], refresh),
            ).props("color=primary")

        await refresh()

        # ── API usage reference ───────────────────────────────────────────────
        ui.separator().classes("my-4")
        with ui.row().classes("w-full items-center justify-between"):
            ui.label("REST API").classes("text-base font-semibold text-gray-700")
            ui.link("Open interactive docs →", "/api/docs", new_tab=True).classes(
                "text-sm text-primary"
            )
        ui.label(
            "Pass your token via the Authorization header or as a query parameter."
        ).classes("text-sm text-gray-500")

        ENDPOINTS = [
            (
                "List managements",
                "GET /api/managements",
                'curl -H "Authorization: Bearer <token>" https://<host>/api/managements',
            ),
            (
                "Recent data (across all departments)",
                "GET /api/data/recent?limit=20",
                'curl -H "Authorization: Bearer <token>" "https://<host>/api/data/recent?limit=50"',
            ),
            (
                "Data by department name",
                "GET /api/data?organization=<org>&department=<dept>",
                'curl -H "Authorization: Bearer <token>" \\\n  "https://<host>/api/data?organization=WUR&department=SSB&metric=temperature&from=2024-01-01&limit=500"',
            ),
        ]

        PARAM_DOCS = (
            ("organization + department", "Text names (human-friendly scope)"),
            ("entity", "Filter to a single entity"),
            ("metric", "Filter to a single metric"),
            ("from", "Start time, inclusive (ISO 8601)"),
            ("to", "End time, exclusive (ISO 8601)"),
            ("limit", "Max rows returned (default 1000, max 10000)"),
        )

        with ui.column().classes("w-full gap-3 mt-2"):
            for title, signature, example in ENDPOINTS:
                with ui.card().classes("w-full p-4 gap-1").props("flat bordered"):
                    ui.label(title).classes("text-sm font-semibold text-gray-700")
                    ui.label(signature).classes("text-xs font-mono text-primary")
                    ui.code(example, language="bash").classes("w-full text-xs mt-1")

            ui.label("Optional query parameters for /api/data").classes(
                "text-sm font-semibold text-gray-700 mt-2"
            )
            with ui.card().classes("w-full p-4").props("flat bordered"):
                columns = [
                    {
                        "name": "param",
                        "label": "Parameter",
                        "field": "param",
                        "align": "left",
                    },
                    {
                        "name": "desc",
                        "label": "Description",
                        "field": "desc",
                        "align": "left",
                    },
                ]
                rows = [{"param": p, "desc": d} for p, d in PARAM_DOCS]
                ui.table(columns=columns, rows=rows, row_key="param").classes(
                    "w-full"
                ).props("flat dense")


def _generate_dialog(user_email: str, refresh_fn) -> None:
    with ui.dialog() as dlg, ui.card().classes("w-96 p-6 gap-4"):
        ui.label("New API token").classes("text-lg font-semibold")
        name_input = ui.input('Token name (e.g. "My R script")').classes("w-full")
        expiry_input = ui.input("Expires (YYYY-MM-DD, leave blank = never)").classes(
            "w-full"
        )
        error = ui.label("").classes("text-red-500 text-sm")

        async def create():
            error.text = ""
            name = name_input.value.strip()
            if not name:
                error.text = "Please give the token a name."
                return

            expires_at = None
            if expiry_input.value.strip():
                try:
                    from datetime import date

                    expires_at = date.fromisoformat(
                        expiry_input.value.strip()
                    ).isoformat()
                except ValueError:
                    error.text = "Invalid date format. Use YYYY-MM-DD."
                    return

            raw_token = secrets.token_hex(32)
            try:
                await db.execute(
                    """
                    INSERT INTO api_token (user_id, name, token, expires_at)
                    VALUES (
                        (SELECT id FROM user_account WHERE email = $1),
                        $2, $3, $4::timestamptz
                    )
                    """,
                    user_email,
                    name,
                    raw_token,
                    expires_at,
                )
            except Exception as e:
                error.text = f"Error: {e}"
                return

            dlg.close()
            await refresh_fn()
            _show_token(name, raw_token)

        with ui.row().classes("w-full justify-end gap-2 mt-2"):
            ui.button("Cancel", on_click=dlg.close).props("flat")
            ui.button("Generate", on_click=create).props("color=primary")

    dlg.open()


def _show_token(name: str, raw_token: str) -> None:
    """Show the token value exactly once. After closing it is gone."""
    with ui.dialog() as dlg, ui.card().classes("w-[32rem] p-6 gap-4"):
        ui.label(f"Token created: {name}").classes("text-lg font-semibold")
        ui.label("Copy this token now — it will not be shown again.").classes(
            "text-sm text-red-600 font-medium"
        )

        ui.input(value=raw_token).classes("w-full font-mono text-xs").props(
            "readonly outlined"
        )

        async def copy_to_clipboard():
            await ui.run_javascript(f'navigator.clipboard.writeText("{raw_token}")')
            ui.notify("Copied to clipboard.", color="positive")

        with ui.row().classes("w-full justify-between items-center mt-1"):
            ui.button("Copy", icon="content_copy", on_click=copy_to_clipboard).props(
                "flat color=primary"
            )
            ui.button("Close", on_click=dlg.close).props("color=primary")

    dlg.open()


def _confirm_revoke(token_id: str, name: str, refresh_fn) -> None:
    with ui.dialog() as dlg, ui.card().classes("p-6 gap-4"):
        ui.label(f'Revoke token "{name}"?').classes("text-lg font-semibold")
        ui.label(
            "Any system using this token will immediately lose access. This cannot be undone."
        ).classes("text-sm text-gray-500")

        async def confirm():
            await db.execute(
                "UPDATE api_token SET revoked_at = now() WHERE id = $1::uuid",
                token_id,
            )
            dlg.close()
            await refresh_fn()
            ui.notify(f'Token "{name}" revoked.', color="positive")

        with ui.row().classes("w-full justify-end gap-2"):
            ui.button("Cancel", on_click=dlg.close).props("flat")
            ui.button("Revoke", on_click=confirm).props("color=negative")

    dlg.open()
