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


@ui.page("/admin/access-management")
async def access_management_page():
    await auth_module.sync_session()
    if not auth_module.is_authenticated():
        ui.navigate.to("/login")
        return
    if not auth_module.is_superadmin():
        ui.navigate.to("/dashboard")
        return

    rows = await db.fetch(
        """
        SELECT p.id, p.name, p.description, p.entity,
               p.time_start, p.time_end, p.created_at,
               i.name  AS organization_name,
               d.name  AS department_name,
               COUNT(up.user_id) AS n_members
        FROM management p
        JOIN organization i       ON i.id = p.organization_id
        LEFT JOIN department d ON d.id = p.department_id
        LEFT JOIN user_management up ON up.management_id = p.id
        GROUP BY p.id, i.name, d.name
        ORDER BY p.created_at DESC
        """
    )
    organizations = await db.fetch("SELECT id, name FROM organization ORDER BY name")
    departments = await db.fetch(
        "SELECT id, organization_id, name FROM department ORDER BY name"
    )
    users = await db.fetch("SELECT id, name, email FROM user_account ORDER BY name")

    with layout.page("Access Management"):
        with ui.row().classes("w-full items-center justify-between -mt-2"):
            ui.label(f"{len(rows)} access grant(s)").classes("text-sm text-gray-500")
            ui.button(
                "New access grant", icon="add", on_click=lambda: add_dialog.open()
            ).props("color=primary")

        columns = [
            {"name": "name", "label": "Name", "field": "name", "align": "left"},
            {
                "name": "organization_name",
                "label": "Organization",
                "field": "organization_name",
                "align": "left",
            },
            {
                "name": "department_name",
                "label": "Department",
                "field": "department_name",
                "align": "left",
            },
            {"name": "entity", "label": "Entity", "field": "entity", "align": "left"},
            {
                "name": "time_start",
                "label": "From",
                "field": "time_start",
                "align": "left",
            },
            {"name": "time_end", "label": "To", "field": "time_end", "align": "left"},
            {
                "name": "n_members",
                "label": "Members",
                "field": "n_members",
                "align": "center",
            },
            {"name": "actions", "label": "", "field": "actions", "align": "right"},
        ]
        row_data = [
            {
                "id": str(r["id"]),
                "name": r["name"],
                "organization_name": r["organization_name"],
                "department_name": r["department_name"] or "—",
                "entity": r["entity"] or "—",
                "time_start": r["time_start"].strftime("%Y-%m-%d")
                if r["time_start"]
                else "—",
                "time_end": r["time_end"].strftime("%Y-%m-%d")
                if r["time_end"]
                else "—",
                "n_members": r["n_members"],
            }
            for r in rows
        ]

        table = ui.table(columns=columns, rows=row_data, row_key="id").classes("w-full")
        table.add_slot(
            "body-cell-actions",
            """
            <q-td :props="props">
                <q-btn flat round dense icon="group_add" color="blue"
                    @click="$parent.$emit('addmember', props.row)" />
                <q-btn flat round dense icon="delete" color="negative"
                    @click="$parent.$emit('delete', props.row)" />
            </q-td>
        """,
        )
        table.on(
            "addmember", lambda e: _members_dialog(e.args["id"], e.args["name"], users)
        )
        table.on("delete", lambda e: _confirm_delete(e.args["id"], e.args["name"]))

        # ── add access grant dialog ─────────────────────────────────────────
        inst_options = {str(i["id"]): i["name"] for i in organizations}
        all_depts = departments  # kept for reactive filtering

        with ui.dialog() as add_dialog, ui.card().classes("w-96 p-6 gap-3"):
            ui.label("New access grant").classes("text-lg font-semibold")
            name_input = ui.input("Name").classes("w-full")
            desc_input = ui.input("Description (optional)").classes("w-full")

            inst_select = ui.select(inst_options, label="Organization").classes(
                "w-full"
            )
            dept_select = ui.select(
                {"": "— All departments"}, label="Department (optional)"
            ).classes("w-full")
            entity_input = ui.input(
                "Entity (optional — leave blank for all entities)"
            ).classes("w-full")

            ui.label("Time window (optional — leave blank for no restriction)").classes(
                "text-xs text-gray-500"
            )
            with ui.row().classes("w-full gap-2"):
                date_input = ui.input("Date range").classes("w-full")
                date_input.visible = False
                ui.date().props("range").bind_value(
                    date_input,
                    forward=lambda x: f"{x['from']} - {x['to']}" if x else None,
                    backward=lambda x: (
                        {
                            "from": x.split(" - ")[0],
                            "to": x.split(" - ")[1],
                        }
                        if " - " in (x or "")
                        else None
                    ),
                )

            error = ui.label("").classes("text-red-500 text-sm")

            def on_organization_change():
                opts = {"": "— All departments"} | {
                    str(d["id"]): d["name"]
                    for d in all_depts
                    if str(d["organization_id"]) == str(inst_select.value)
                }
                dept_select.options = opts
                dept_select.value = ""
                dept_select.update()

            inst_select.on("update:model-value", lambda e: on_organization_change())

            async def save_management():
                error.text = ""
                if not name_input.value.strip() or not inst_select.value:
                    error.text = "Name and organization are required."
                    return
                try:
                    dept_id = dept_select.value or None
                    entity_val = entity_input.value.strip() or None
                    dr = date_input.value or ""
                    if " - " in dr:
                        t_start, t_end = [p.strip() or None for p in dr.split(" - ", 1)]
                    else:
                        t_start = t_end = None
                    await db.execute(
                        """
                        INSERT INTO management
                            (organization_id, department_id, entity, time_start, time_end, name, description)
                        VALUES
                            ($1::uuid, $2::uuid, $3, $4::timestamptz, $5::timestamptz, $6, $7)
                        """,
                        inst_select.value,
                        dept_id,
                        entity_val,
                        t_start,
                        t_end,
                        name_input.value.strip(),
                        desc_input.value.strip() or None,
                    )
                    add_dialog.close()
                    ui.navigate.to("/admin/access-management")
                except Exception as e:
                    error.text = f"Error: {e}"

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


async def _members_dialog(
    management_id: str, management_name: str, users: list
) -> None:
    with ui.dialog() as dlg, ui.card().classes("w-96 p-6 gap-4"):
        ui.label(f"Members — {management_name}").classes("text-lg font-semibold")

        user_options = {str(u["id"]): f"{u['name']} ({u['email']})" for u in users}
        user_select = ui.select(user_options, label="Add user").classes("w-full")
        role_select = ui.select(
            {"viewer": "Viewer", "admin": "Admin"}, label="Role", value="viewer"
        ).classes("w-full")

        async def grant():
            if not user_select.value:
                ui.notify("Select a user.", color="warning")
                return
            try:
                await db.execute(
                    """
                    INSERT INTO user_management (user_id, management_id, role)
                    VALUES ($1::uuid, $2::uuid, $3)
                    ON CONFLICT (user_id, management_id) DO UPDATE SET role = EXCLUDED.role
                    """,
                    user_select.value,
                    management_id,
                    role_select.value,
                )
                ui.notify("Member added.", color="positive")
                await _refresh_members(management_id, members_table)
            except Exception as e:
                ui.notify(f"Error: {e}", color="negative")

        with ui.row().classes("w-full gap-2"):
            ui.button("Add member", icon="person_add", on_click=grant).props(
                "color=primary"
            )

        ui.separator()
        ui.label("Current members").classes("text-sm font-medium text-gray-600")
        members_table = ui.table(
            columns=[
                {"name": "name", "label": "Name", "field": "name", "align": "left"},
                {"name": "role", "label": "Role", "field": "role", "align": "left"},
                {"name": "rm", "label": "", "field": "rm", "align": "right"},
            ],
            rows=[],
            row_key="user_id",
        ).classes("w-full")
        members_table.add_slot(
            "body-cell-rm",
            """
            <q-td :props="props">
                <q-btn flat round dense icon="person_remove" color="negative"
                    @click="$parent.$emit('remove', props.row)" />
            </q-td>
        """,
        )
        members_table.on(
            "remove",
            lambda e: _remove_member(e.args["user_id"], management_id, members_table),
        )

        await _refresh_members(management_id, members_table)
        ui.button("Close", on_click=dlg.close).props("flat").classes("self-end")

    dlg.open()


async def _refresh_members(management_id: str, table: ui.table) -> None:
    members = await db.fetch(
        """
        SELECT up.user_id, ua.name, up.role
        FROM user_management up
        JOIN user_account ua ON ua.id = up.user_id
        WHERE up.management_id = $1::uuid
        ORDER BY ua.name
        """,
        management_id,
    )
    table.rows = [
        {"user_id": str(m["user_id"]), "name": m["name"], "role": m["role"]}
        for m in members
    ]
    table.update()


async def _remove_member(user_id: str, management_id: str, table: ui.table) -> None:
    await db.execute(
        "DELETE FROM user_management WHERE user_id = $1::uuid AND management_id = $2::uuid",
        user_id,
        management_id,
    )
    await _refresh_members(management_id, table)


def _confirm_delete(management_id: str, name: str) -> None:
    with ui.dialog() as dlg, ui.card().classes("p-6 gap-4"):
        ui.label(f'Delete "{name}"?').classes("text-lg font-semibold")
        ui.label("All member access grants for this entry will be removed.").classes(
            "text-sm text-gray-500"
        )

        async def confirm():
            try:
                await db.execute("DELETE FROM management WHERE id = $1", management_id)
                dlg.close()
                ui.navigate.to("/admin/access-management")
            except Exception as e:
                ui.notify(f"Error: {e}", color="negative")

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

    dlg.open()
