import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import tseslint from "typescript-eslint";
import turboPlugin from "eslint-plugin-turbo";
import boundaries from "eslint-plugin-boundaries";
import globals from "globals";
import noDirectSocketIO from "./rules/no-direct-socket-io.js";
import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js";

export default [
  { ignores: ["dist/**", "node_modules/**", ".next/**", ".turbo/**", "storybook-static/**"] },
  js.configs.recommended,
  {
    files: ["**/*.{mjs,cjs,js}", "**/*.config.{ts,tsx}"],
    languageOptions: {
      globals: { ...globals.node },
    },
  },
  ...tseslint.configs.recommended,
  eslintConfigPrettier,
  {
    plugins: { turbo: turboPlugin },
    rules: {
      "turbo/no-undeclared-env-vars": "warn",
    },
  },
  {
    rules: {
      // Honour the leading-underscore convention for intentionally-unused params/vars.
      "@typescript-eslint/no-unused-vars": [
        "error",
        {
          argsIgnorePattern: "^_",
          varsIgnorePattern: "^_",
          caughtErrorsIgnorePattern: "^_",
        },
      ],
    },
  },
  {
    plugins: { boundaries },
    settings: {
      "boundaries/elements": [
        { type: "app", pattern: "apps/*" },
        { type: "tooling", pattern: "packages/core-eslint" },
        { type: "tooling", pattern: "packages/core-typescript" },
        { type: "tooling", pattern: "packages/core-testing" },
        { type: "core-composition", pattern: "packages/core-api" },
        { type: "core-composition", pattern: "packages/core-cms" },
        // Explicit entry placed before the catch-all so its `mode: "folder"`
        // is preferred — needed for boundaries-plugin to resolve element root
        // by directory rather than by package.json name.
        { type: "core", pattern: "packages/core-realtime", mode: "folder" },
        { type: "core", pattern: "packages/core-*" },
        { type: "feature", pattern: "packages/!(core-*)" },
      ],
    },
    rules: {
      "boundaries/element-types": [
        2,
        {
          default: "disallow",
          rules: [
            { from: "app", allow: ["app", "core", "core-composition", "feature", "tooling"] },
            { from: "feature", allow: ["core", "tooling"] },
            { from: "core", allow: ["core", "tooling"] },
            { from: "core-composition", allow: ["core", "feature", "tooling"] },
            { from: "tooling", allow: ["tooling"] },
          ],
        },
      ],
    },
  },
  // R40 — block direct @sentry/* imports outside the allowlisted instrumentation paths
  {
    files: ["**/*.{ts,tsx,mjs,cjs,js}"],
    rules: {
      "no-restricted-imports": [
        "error",
        {
          patterns: [
            {
              group: ["@sentry/*"],
              message:
                "Import from @repo/core-shared/instrumentation instead — feature packages must not depend on Sentry directly (R40).",
            },
          ],
        },
      ],
    },
  },
  // R40 allowlist — the only paths permitted to import @sentry/*.
  // Patterns are double-star prefixed so they match whether eslint runs from
  // the repo root or from inside a sub-package.
  {
    files: [
      "**/instrumentation/sentry/**",
      "**/instrumentation/di/bind-sentry-instrumentation.{ts,js}",
      "**/instrumentation/di/bind-sentry-instrumentation.test.{ts,js}",
      "**/setup/no-sentry.{ts,js}",
      "**/setup/no-sentry.test.{ts,js}",
      "**/instrumentation.{ts,js,mjs}",
      "**/instrumentation-client.{ts,js,mjs}",
      "**/next.config.{mjs,ts,js}",
      "**/vite.config.{ts,mjs,js}",
      "**/sentry.*.config.{ts,mjs,js}",
    ],
    rules: {
      "no-restricted-imports": "off",
    },
  },
  // E1 — Event handlers must not be re-exported. Wire them only inside the
  // consumer feature's bind-production / bind-dev-seed (spec § 2.2 Rule E1).
  // J  — Direct `payload.jobs.*` access is forbidden outside the integration
  // layer. Use IJobQueue (from @repo/core-shared/jobs) instead.
  {
    files: ["**/*.{ts,tsx,mjs,cjs,js}"],
    rules: {
      "no-restricted-syntax": [
        "error",
        {
          selector:
            "ExportNamedDeclaration[source.value=/\\/events\\/handlers\\//]",
          message:
            "Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
        },
        {
          selector:
            "ExportAllDeclaration[source.value=/\\/events\\/handlers\\//]",
          message:
            "Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
        },
        {
          selector:
            "MemberExpression[object.type='MemberExpression'][object.object.type='Identifier'][object.object.name='payload'][object.property.type='Identifier'][object.property.name='jobs']",
          message:
            "Direct `payload.jobs.*` access is not allowed here. Use IJobQueue (from @repo/core-shared/jobs) instead. Allowed only in **/integrations/cms/jobs/** and **/core-shared/src/jobs/**.",
        },
      ],
    },
  },
  // J — `payload.jobs.*` is allowed only in the integration layer.
  // In these paths, no-restricted-syntax is narrowed to keep E1 active but
  // drop the payload.jobs check.
  // Note: "**/core-shared/src/jobs/**" does not match from within a package-local
  // ESLint run because ESLint resolves globs relative to the config file location.
  // The pattern is kept for documentation; in practice, the PayloadJobQueue class
  // uses `this.payload.jobs.*` which the selector already ignores (it only catches
  // bare `payload.jobs.*`). Any new file added there that does use bare `payload.jobs.*`
  // would need this allowlist to be expressed as "**/jobs/payload-*" or similar.
  {
    files: [
      "**/integrations/cms/jobs/**",
      "**/core-shared/src/jobs/**",
    ],
    rules: {
      "no-restricted-syntax": [
        "error",
        {
          selector:
            "ExportNamedDeclaration[source.value=/\\/events\\/handlers\\//]",
          message:
            "Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
        },
        {
          selector:
            "ExportAllDeclaration[source.value=/\\/events\\/handlers\\//]",
          message:
            "Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).",
        },
      ],
    },
  },
  // R2 — `socket.io` and `socket.io-client` must not be imported outside
  // core-realtime/src/ and apps/*/server.ts. Use @repo/core-realtime helpers.
  // R1 (ADR-016) — Realtime handlers must not be re-exported outside bind-* files.
  {
    files: ["**/*.{ts,tsx,mjs,cjs,js}"],
    plugins: {
      "repo-rules": {
        rules: {
          "no-direct-socket-io": noDirectSocketIO,
          "no-realtime-handler-reexport": noRealtimeHandlerReexport,
        },
      },
    },
    rules: {
      "repo-rules/no-direct-socket-io": "error",
      "repo-rules/no-realtime-handler-reexport": "error",
    },
  },
];
