#!/usr/bin/env python3
"""Generate the role×role integration matrix.

Source of truth for the integration-matrix markdown emitted alongside this
script. Regenerate it from the repository root and commit the regenerated file.

Axes are the entity names of every web-app-* and web-svc-* role.
A cell [row][col] marks whether the ROW role ships an addon/plugin that wires
in the COLUMN role:
  - check  -> a real upstream plugin exists (cell links to its page)
  - coin   -> integration exists but is gated behind a commercial/paid tier
  - cross  -> no known plugin
  - dash   -> diagonal (same role)
"""

# nocheck: file-size  curated EDGES data table plus matrix renderers; the data and the renderer read together, splitting them only adds indirection.

import re

from utils.cache.files import read_text
from utils.roles.mapping import ROLE_FILE_META_SERVICES

from . import PROJECT_ROOT

ROLES_DIR = PROJECT_ROOT / "roles"
REPORT = PROJECT_ROOT / "docs" / "requirements" / "027-integration-matrix.md"

FRAMEWORK = {
    "sso": "keycloak",
    "matomo": "matomo",
    "prometheus": "prometheus",
    "email": "mailu",
    "dashboard": "dashboard",
    "css": "css",
    "logout": "logout",
    "cdn": "cdn",
    "coturn": "coturn",
    "collabora": "collabora",
    "onlyoffice": "onlyoffice",
    "libretranslate": "libretranslate",
    "litellm": "litellm",
}

WEB_APP = [
    "akaunting",
    "baserow",
    "bigbluebutton",
    "bluesky",
    "bookwyrm",
    "bridgy-fed",
    "chess",
    "confluence",
    "dashboard",
    "decidim",
    "discourse",
    "erpnext",
    "espocrm",
    "fediwall",
    "fider",
    "flowise",
    "friendica",
    "funkwhale",
    "fusiondirectory",
    "gitea",
    "gitlab",
    "hugo",
    "jenkins",
    "jira",
    "jitsi",
    "joomla",
    "keycloak",
    "kix",
    "lam",
    "listmonk",
    "litellm",
    "littlejs",
    "magento",
    "mailu",
    "mastodon",
    "matomo",
    "matrix",
    "mattermost",
    "mediawiki",
    "mig",
    "minio",
    "mini-qr",
    "mobilizon",
    "moodle",
    "n8n",
    "navigator",
    "nextcloud",
    "odoo",
    "opencloud",
    "openproject",
    "opentalk",
    "openwebui",
    "peertube",
    "pgadmin",
    "phpldapadmin",
    "phpmyadmin",
    "pixelfed",
    "postmarks",
    "pretix",
    "prometheus",
    "roulette-wheel",
    "shopware",
    "snipe-it",
    "socialhome",
    "sphinx",
    "suitecrm",
    "taiga",
    "wordpress",
    "xwiki",
    "yourls",
    "zammad",
]
WEB_SVC = [
    "asset",
    "cdn",
    "collabora",
    "coturn",
    "css",
    "file",
    "html",
    "legal",
    "libretranslate",
    "logout",
    "onlyoffice",
    "simpleicons",
    "xmpp",
]
ENTITIES = WEB_APP + WEB_SVC

EDGES = [
    (
        "nextcloud",
        "openproject",
        "https://github.com/nextcloud/integration_openproject",
        "check",
    ),
    ("nextcloud", "gitlab", "https://github.com/nextcloud/integration_gitlab", "check"),
    (
        "nextcloud",
        "discourse",
        "https://github.com/nextcloud/integration_discourse",
        "check",
    ),
    (
        "nextcloud",
        "mattermost",
        "https://github.com/nextcloud/integration_mattermost",
        "check",
    ),
    ("nextcloud", "matrix", "https://github.com/nextcloud/integration_matrix", "check"),
    ("nextcloud", "jira", "https://github.com/nextcloud/integration_jira", "check"),
    ("nextcloud", "zammad", "https://github.com/nextcloud/integration_zammad", "check"),
    (
        "nextcloud",
        "openwebui",
        "https://github.com/nextcloud/integration_openai",
        "check",
    ),
    (
        "nextcloud",
        "flowise",
        "https://github.com/nextcloud/integration_openai",
        "check",
    ),
    (
        "nextcloud",
        "mastodon",
        "https://github.com/nextcloud/integration_mastodon",
        "check",
    ),
    (
        "nextcloud",
        "peertube",
        "https://github.com/nextcloud/integration_peertube",
        "check",
    ),
    ("nextcloud", "moodle", "https://github.com/nextcloud/integration_moodle", "check"),
    (
        "nextcloud",
        "suitecrm",
        "https://github.com/eneiluj/integration_suitecrm",
        "check",
    ),
    ("nextcloud", "bigbluebutton", "https://apps.nextcloud.com/apps/bbb", "check"),
    ("nextcloud", "xwiki", "https://apps.nextcloud.com/apps/xwiki", "check"),
    ("nextcloud", "collabora", "https://github.com/nextcloud/richdocuments", "check"),
    (
        "nextcloud",
        "onlyoffice",
        "https://github.com/ONLYOFFICE/onlyoffice-nextcloud",
        "check",
    ),
    ("nextcloud", "keycloak", "https://github.com/nextcloud/user_oidc", "check"),
    ("nextcloud", "matomo", "https://apps.nextcloud.com/apps/matomo", "check"),
    (
        "mattermost",
        "gitlab",
        "https://github.com/mattermost/mattermost-plugin-gitlab",
        "check",
    ),
    (
        "mattermost",
        "jira",
        "https://github.com/mattermost/mattermost-plugin-jira",
        "check",
    ),
    (
        "mattermost",
        "jenkins",
        "https://github.com/mattermost/mattermost-plugin-jenkins",
        "check",
    ),
    (
        "mattermost",
        "jitsi",
        "https://github.com/mattermost-community/mattermost-plugin-jitsi",
        "check",
    ),
    (
        "mattermost",
        "openwebui",
        "https://github.com/mattermost/mattermost-plugin-agents",
        "check",
    ),
    (
        "mattermost",
        "prometheus",
        "https://github.com/cpanato/mattermost-plugin-alertmanager",
        "check",
    ),
    (
        "mattermost",
        "keycloak",
        "https://docs.mattermost.com/administration-guide/onboard/sso-saml.html",
        "coin",
    ),
    (
        "gitlab",
        "mattermost",
        "https://docs.gitlab.com/user/project/integrations/mattermost/",
        "check",
    ),
    ("gitlab", "jira", "https://docs.gitlab.com/integration/jira/", "check"),
    ("gitlab", "jenkins", "https://docs.gitlab.com/integration/jenkins/", "check"),
    (
        "gitlab",
        "matrix",
        "https://docs.gitlab.com/user/project/integrations/matrix/",
        "check",
    ),
    (
        "gitlab",
        "confluence",
        "https://docs.gitlab.com/user/project/integrations/confluence/",
        "check",
    ),
    ("gitlab", "keycloak", "https://docs.gitlab.com/integration/saml/", "check"),
    (
        "discourse",
        "mattermost",
        "https://github.com/discourse/discourse-chat-integration",
        "check",
    ),
    (
        "discourse",
        "matrix",
        "https://github.com/discourse/discourse-chat-integration",
        "check",
    ),
    (
        "discourse",
        "prometheus",
        "https://github.com/discourse/discourse-prometheus",
        "check",
    ),
    (
        "discourse",
        "bigbluebutton",
        "https://github.com/discourse/discourse-bbb",
        "check",
    ),
    ("discourse", "jitsi", "https://github.com/discourse/discourse-jitsi", "check"),
    ("discourse", "openwebui", "https://github.com/discourse/discourse-ai", "check"),
    (
        "discourse",
        "matomo",
        "https://github.com/discourse/discourse-matomo-analytics",
        "check",
    ),
    (
        "discourse",
        "keycloak",
        "https://github.com/discourse/discourse-openid-connect",
        "check",
    ),
    (
        "discourse",
        "mastodon",
        "https://github.com/discourse/discourse-activity-pub",
        "check",
    ),
    ("matrix", "gitlab", "https://github.com/matrix-org/matrix-hookshot", "check"),
    ("matrix", "jira", "https://github.com/matrix-org/matrix-hookshot", "check"),
    (
        "matrix",
        "keycloak",
        "https://matrix-org.github.io/synapse/latest/openid.html",
        "check",
    ),
    (
        "matrix",
        "prometheus",
        "https://matrix-org.github.io/synapse/latest/metrics-howto.html",
        "check",
    ),
    (
        "matrix",
        "jitsi",
        "https://github.com/element-hq/element-web/blob/develop/docs/jitsi.md",
        "check",
    ),
    (
        "moodle",
        "bigbluebutton",
        "https://moodle.org/plugins/mod_bigbluebuttonbn",
        "check",
    ),
    ("moodle", "nextcloud", "https://moodle.org/plugins/repository_owncloud", "check"),
    ("moodle", "matrix", "https://docs.moodle.org/en/Matrix", "check"),
    ("moodle", "keycloak", "https://moodle.org/plugins/auth_oidc", "check"),
    ("moodle", "jitsi", "https://moodle.org/plugins/mod_jitsi", "check"),
    ("moodle", "matomo", "https://moodle.org/plugins/tool_webanalytics", "check"),
    ("moodle", "openwebui", "https://moodle.org/plugins/aiprovider_openwebui", "check"),
    ("jenkins", "gitlab", "https://plugins.jenkins.io/gitlab-plugin", "check"),
    ("jenkins", "gitea", "https://plugins.jenkins.io/gitea/", "check"),
    ("jenkins", "mattermost", "https://plugins.jenkins.io/mattermost/", "check"),
    ("jenkins", "prometheus", "https://plugins.jenkins.io/prometheus", "check"),
    ("jenkins", "keycloak", "https://plugins.jenkins.io/oic-auth/", "check"),
    (
        "openproject",
        "nextcloud",
        "https://www.openproject.org/docs/system-admin-guide/integrations/nextcloud/",
        "check",
    ),
    (
        "openproject",
        "gitlab",
        "https://www.openproject.org/docs/system-admin-guide/integrations/gitlab-integration/",
        "check",
    ),
    (
        "openproject",
        "keycloak",
        "https://www.openproject.org/docs/system-admin-guide/authentication/openid-providers/",
        "coin",
    ),
    ("wordpress", "matomo", "https://wordpress.org/plugins/matomo/", "check"),
    ("wordpress", "mastodon", "https://wordpress.org/plugins/activitypub/", "check"),
    ("wordpress", "discourse", "https://wordpress.org/plugins/wp-discourse/", "check"),
    (
        "wordpress",
        "keycloak",
        "https://wordpress.org/plugins/daggerhart-openid-connect-generic/",
        "check",
    ),
    (
        "wordpress",
        "listmonk",
        "https://wordpress.org/plugins/integration-for-listmonk-mailing-list-and-newsletter-manager/",
        "check",
    ),
    (
        "wordpress",
        "bigbluebutton",
        "https://wordpress.org/plugins/video-conferencing-with-bbb/",
        "check",
    ),
    (
        "wordpress",
        "peertube",
        "https://wordpress.org/plugins/video-manager-for-peertube/",
        "check",
    ),
    (
        "mediawiki",
        "keycloak",
        "https://www.mediawiki.org/wiki/Extension:OpenID_Connect",
        "check",
    ),
    ("mediawiki", "matomo", "https://www.mediawiki.org/wiki/Extension:Matomo", "check"),
    (
        "mediawiki",
        "discourse",
        "https://www.mediawiki.org/wiki/Extension:DiscourseSsoConsumer",
        "check",
    ),
    (
        "mediawiki",
        "peertube",
        "https://www.mediawiki.org/wiki/Extension:PeerTubeEmbed",
        "check",
    ),
    (
        "mediawiki",
        "libretranslate",
        "https://www.mediawiki.org/wiki/Extension:MachineTranslation",
        "check",
    ),
    ("mediawiki", "minio", "https://www.mediawiki.org/wiki/Extension:AWS", "check"),
    (
        "mediawiki",
        "openwebui",
        "https://www.mediawiki.org/wiki/Extension:AIEditingAssistant",
        "check",
    ),
    (
        "xwiki",
        "keycloak",
        "https://extensions.xwiki.org/xwiki/bin/view/Extension/OpenID%20Connect/",
        "check",
    ),
    (
        "xwiki",
        "matomo",
        "https://extensions.xwiki.org/xwiki/bin/view/Extension/Piwiki%20Integration",
        "check",
    ),
    (
        "joomla",
        "keycloak",
        "../../roles/web-app-joomla/files/joomla-oidc-plugin/",
        "check",
    ),
    (
        "joomla",
        "matomo",
        "https://extensions.joomla.org/extension/itcs-matomo/",
        "check",
    ),
    ("friendica", "matomo", "https://github.com/friendica/friendica-addons", "check"),
    (
        "pretix",
        "keycloak",
        "https://docs.pretix.eu/en/latest/admin/installation/index.html",
        "check",
    ),
    (
        "peertube",
        "keycloak",
        "https://www.npmjs.com/package/peertube-plugin-auth-openid-connect",
        "check",
    ),
    (
        "bigbluebutton",
        "keycloak",
        "https://docs.bigbluebutton.org/greenlight/v3/external-authentication/",
        "check",
    ),
    (
        "zammad",
        "keycloak",
        "https://admin-docs.zammad.org/en/latest/settings/security/third-party/saml.html",
        "check",
    ),
    (
        "zammad",
        "mailu",
        "https://admin-docs.zammad.org/en/latest/channels/email/index.html",
        "check",
    ),
    ("espocrm", "keycloak", "https://docs.espocrm.com/administration/oidc/", "check"),
    (
        "espocrm",
        "mailu",
        "https://docs.espocrm.com/user-guide/imap-smtp-configuration/",
        "check",
    ),
    (
        "suitecrm",
        "keycloak",
        "https://docs.suitecrm.com/8.x/admin/configuration/saml/8.7.0-saml-configuration/",
        "check",
    ),
    (
        "suitecrm",
        "mailu",
        "https://github.com/SuiteCRM/SuiteCRM",
        "check",
    ),
    (
        "odoo",
        "keycloak",
        "https://github.com/OCA/server-auth/tree/16.0/auth_oidc",
        "check",
    ),
    (
        "odoo",
        "mailu",
        "https://www.odoo.com/documentation/latest/applications/general/email_communication.html",
        "check",
    ),
    (
        "odoo",
        "nextcloud",
        "https://apps.odoo.com/apps/modules/19.0/nextcloud_odoo_integration",
        "coin",
    ),
    (
        "erpnext",
        "keycloak",
        "https://docs.frappe.io/framework/user/en/guides/integration/openid_connect_and_frappe_social_login",
        "check",
    ),
    (
        "erpnext",
        "mailu",
        "https://docs.frappe.io/erpnext/user/manual/en/setting-up-email-account",
        "check",
    ),
    (
        "baserow",
        "keycloak",
        "https://baserow.io/user-docs/configure-openid-connect-for-oauth-2-sso",
        "coin",
    ),
    (
        "taiga",
        "keycloak",
        "https://github.com/taigaio/taiga-contrib-oidc-auth",
        "check",
    ),
]

CHECK = "✅"
WIRED = "☑️"
COIN = "🪙"
CROSS = "❌"
DASH = "—"


def scan_framework_edges():
    """Derive infinito-native integration edges from each role's meta/services.yml."""
    edges = []
    for prefix in ("web-app-", "web-svc-"):
        for path in sorted(ROLES_DIR.glob(f"{prefix}*/{ROLE_FILE_META_SERVICES}")):
            role = path.parent.parent.name
            entity = role[len(prefix) :]
            lines = read_text(str(path)).splitlines()
            if not lines:
                continue
            seen = set()
            for line in lines:
                m = re.match(r"^([a-z0-9_]+):", line)
                if not m:
                    continue
                key = m.group(1)
                target = FRAMEWORK.get(key)
                if target and target != entity and target not in seen:
                    seen.add(target)
                    rel = f"../../roles/{role}/{ROLE_FILE_META_SERVICES}"
                    edges.append((entity, target, rel, "wired"))
    return edges


def build():
    by_pair = {(s, d): (url, kind) for (s, d, url, kind) in scan_framework_edges()}
    by_pair.update({(s, d): (url, kind) for (s, d, url, kind) in EDGES})
    corner = "↓ row wires → col"

    def render_row(label, cells):
        out = [label]
        for j, cell in enumerate(cells):
            out.append(cell)
            if (j + 1) % 10 == 0 and (j + 1) < len(cells):
                out.append(label)
        return "| " + " | ".join(out) + " |"

    header = render_row(corner, ENTITIES)
    ncols = header.count("|") - 1
    sep = "|" + "---|" * ncols
    rows = [header, sep]
    for i, src in enumerate(ENTITIES):
        cells = []
        for dst in ENTITIES:
            if src == dst:
                cells.append(DASH)
            elif (src, dst) in by_pair:
                url, kind = by_pair[(src, dst)]
                sym = {"coin": COIN, "wired": WIRED}.get(kind, CHECK)
                cells.append(f"[{sym}]({url})")
            else:
                cells.append(CROSS)
        rows.append(render_row(f"**{src}**", cells))
        if (i + 1) % 10 == 0 and (i + 1) < len(ENTITIES):
            rows.append(header)
    return "\n".join(rows)


HEADER = """# 027 - Integration Matrix

> Generated by `make integration-matrix`. Do not edit by hand, edit the generator's `EDGES` map and re-run.

Companion artifact for [026-unified-addon-syntax.md](026-unified-addon-syntax.md).
Axes are the **entity names** of every `web-app-*` and `web-svc-*` role.
A cell marks whether the **row** role ships an addon/plugin that wires in the **column** role.

## Legend

| Symbol | Meaning |
|---|---|
| {wired} | Already wired via an infinito-native service flag in the row role's `meta/services.yml` (group-gated). Links to that declaration. |
| {check} | A verified upstream app↔app plugin exists but is not yet declared as an addon. Links to the plugin page. This is the backlog. |
| {coin} | Integration exists but is gated behind a commercial / paid tier. |
| {cross} | No known integration to wire these two. |
| {dash} | Same role (diagonal). |

Notes:

- The matrix is **directional**: the row hosts the plugin/flag. Bidirectional pairs (e.g. `nextcloud`↔`openproject`) carry a symbol in both cells, each linking to that side.
- {wired} edges are derived automatically by scanning every role's `meta/services.yml` for integration service keys (`sso`→keycloak, `matomo`→matomo, `prometheus`→prometheus, `email`→mailu, `dashboard`, `css`, `logout`, `cdn`, `coturn`, `collabora`, `onlyoffice`, `libretranslate`, `litellm`). `ldap`/`redis`/`mariadb` map to `svc-db-*` roles that are off these axes and are not shown. The `litellm` column is the AI gateway surface of [036](036-native-ai-gateway-integration.md): the service key resolves to `svc-ai-litellm`, which is off-axis, so the edge is drawn to the `litellm` entity that fronts it.
- `→ keycloak` {wired} cells are the central `sso` service; a {check}/{coin} on `→ keycloak` instead means a role-local OIDC/SAML addon path beyond the central service.
- Native ActivityPub federation between fediverse roles (`mastodon`, `peertube`, `pixelfed`, `funkwhale`, `mobilizon`, `bookwyrm`, `socialhome`) needs **no plugin** and is therefore not a {check} edge unless an installable connector exists.

## Matrix

{matrix}

## Maintenance

- [ ] Regenerate this matrix after adding or removing any integration edge.
"""


def main():
    fw = scan_framework_edges()
    out = HEADER.format(
        check=CHECK, wired=WIRED, coin=COIN, cross=CROSS, dash=DASH, matrix=build()
    )
    path = REPORT
    path.write_text(out, encoding="utf-8")
    print(
        f"wrote {path} ({len(ENTITIES)}x{len(ENTITIES)} matrix, "
        f"{len(fw)} wired + {len(EDGES)} upstream edges)"
    )


if __name__ == "__main__":
    main()
