import type { PageAst, WrnDiagnostic } from "@wrnexus/syntax";

export type PluginOrder = "pre" | "normal" | "post";
export type PluginCommand = "dev" | "build" | "test" | "cli" | "deploy";
export type ClientRuntimeType = "module" | "script";
export type ClientRuntimeLoad = "eager" | "defer" | "idle";
export type ClientRuntimeInject = "head" | "body-end";
export type PluginPermission =
  | "components"
  | "client-runtime"
  | "assets"
  | "styles"
  | "routes"
  | "middleware"
  | "migrations"
  | "config"
  | "transform"
  | "diagnostics"
  | "server"
  | "cli"
  | "directives"
  | "virtual-modules"
  | "deployment"
  | "documentation"
  | "types";

export interface PluginContext {
  root: string;
  mode: "development" | "production";
  command: PluginCommand;
  profile?: string;
  metadata: Map<string, unknown>;
  warn(message: string): void;
}

export interface TransformContext extends PluginContext {
  file: string;
}

/**
 * A browser runtime owned by a package. The framework serves/bundles it and
 * injects it only when rendered markup declares `data-wrnexus-runtime="<id>"`.
 */
export interface ClientRuntimeDefinition {
  /** Stable, package-wide runtime identifier. */
  id: string;
  /** Browser entry file. May be TypeScript; production builds bundle it. */
  entry?: string;
  /** Inline browser source. Use this instead of `entry` for generated runtimes. */
  source?: string;
  /** Development URL. Defaults to `/__wrnexus/assets/<id>.js`. */
  publicPath?: string;
  /** Module scripts are the default. Use `script` for an IIFE/classic script. */
  type?: ClientRuntimeType;
  /** Loading policy. Defaults to `defer`. */
  load?: ClientRuntimeLoad;
  /** Document injection target. Defaults to `body-end`. */
  inject?: ClientRuntimeInject;
  /** Runtime is loaded at most once per document. Defaults to true. */
  singleton?: boolean;
  /** Bundle the entry in production. Defaults to true for TS and false for JS. */
  bundle?: boolean;
  /** Optional SRI string for externally hosted runtimes. */
  integrity?: string;
  crossOrigin?: "anonymous" | "use-credentials";
  /** Additional safe script attributes. */
  attributes?: Record<string, string | boolean>;
}

/** A package-owned static/virtual asset served below `/__wrnexus/assets/`. */
export interface PackageAssetDefinition {
  id: string;
  entry?: string;
  source?: string | Uint8Array;
  publicPath?: string;
  contentType?: string;
  immutable?: boolean;
}

/** A package-owned stylesheet entry or Tailwind scan source. */
export interface PackageStyleDefinition {
  id: string;
  entry?: string;
  /** Glob/directory that application style processors should scan. */
  source?: string;
  order?: PluginOrder;
}

/** A package-owned SQL migration file, directory, or inline migration. */
export interface PackageMigrationDefinition {
  /** Stable package-wide identifier used to prevent collisions. */
  id: string;
  /** SQL file or directory containing ordered .sql migrations. */
  entry?: string;
  /** Inline SQL using the standard -- +up / -- +down markers. */
  source?: string;
  /** Named database target. Omit or use "default" for the default database. */
  database?: string;
}

export interface PackageRouteDefinition {
  kind: "page" | "api" | "realtime";
  /** Absolute after discovery; package manifests may use package-relative paths. */
  entry: string;
  /** Exact public route pattern, for example `/account` or `/api/captcha/challenge`. */
  path: string;
  name?: string;
}

export interface PluginDevToolbarPanel {
  id: string;
  title: string;
  icon?: string;
  description?: string;
  badge?: number | string;
  order?: number;
  issues?: unknown[];
  data?: unknown;
}

export interface PluginCliCommand {
  name: string;
  description?: string;
  run(args: string[], context: PluginContext): void | Promise<void>;
}

export interface PluginVirtualModule {
  id: string;
  load(context: PluginContext): string | Promise<string>;
}

export interface PluginDirective {
  name: string;
  transform?: (
    value: string,
    context: TransformContext,
  ) => { name: string; value: string } | void | Promise<{ name: string; value: string } | void>;
}

export interface PluginDeploymentAdapter {
  name: string;
  build(output: unknown, context: PluginContext): unknown | Promise<unknown>;
  deploy?(output: unknown, context: PluginContext): unknown | Promise<unknown>;
}

export interface PluginConfigSchema {
  namespace: string;
  validate(value: unknown, context: PluginContext): void | Promise<void>;
}

export interface PluginContributions {
  componentDirs: string[];
  clientRuntimes: ClientRuntimeDefinition[];
  assets: PackageAssetDefinition[];
  styles: PackageStyleDefinition[];
  routes: PackageRouteDefinition[];
  middleware: string[];
  migrations: PackageMigrationDefinition[];
  directives: PluginDirective[];
  cliCommands: PluginCliCommand[];
  virtualModules: PluginVirtualModule[];
  deploymentAdapters: PluginDeploymentAdapter[];
  configSchemas: PluginConfigSchema[];
  documentation: string[];
  typeDefinitions: string[];
}

export interface WrnexusPlugin {
  name: string;
  version?: string;
  enforce?: PluginOrder;
  /** Plugin names that must execute first. */
  after?: string[];
  /** Plugin names that must execute later. */
  before?: string[];

  /** Component directories automatically added to router discovery. */
  componentDirs?: string[] | ((context: PluginContext) => string[] | Promise<string[]>);
  /** Package browser runtimes, lazily injected from rendered runtime markers. */
  clientRuntimes?:
    | ClientRuntimeDefinition[]
    | ((context: PluginContext) => ClientRuntimeDefinition[] | Promise<ClientRuntimeDefinition[]>);
  /** Package-owned static or generated assets. */
  assets?:
    | PackageAssetDefinition[]
    | ((context: PluginContext) => PackageAssetDefinition[] | Promise<PackageAssetDefinition[]>);
  /** Stylesheet entries and processor scan sources. */
  styleSources?:
    | PackageStyleDefinition[]
    | ((context: PluginContext) => PackageStyleDefinition[] | Promise<PackageStyleDefinition[]>);
  /** Package-owned page/API/realtime routes. */
  routeEntries?:
    | PackageRouteDefinition[]
    | ((context: PluginContext) => PackageRouteDefinition[] | Promise<PackageRouteDefinition[]>);
  /** Package middleware modules, executed before application middleware. */
  middleware?: string[] | ((context: PluginContext) => string[] | Promise<string[]>);
  /** Package-owned database migrations. */
  migrations?:
    | PackageMigrationDefinition[]
    | ((
        context: PluginContext,
      ) => PackageMigrationDefinition[] | Promise<PackageMigrationDefinition[]>);
  directives?:
    | PluginDirective[]
    | ((context: PluginContext) => PluginDirective[] | Promise<PluginDirective[]>);
  cliCommands?:
    | PluginCliCommand[]
    | ((context: PluginContext) => PluginCliCommand[] | Promise<PluginCliCommand[]>);
  virtualModules?:
    | PluginVirtualModule[]
    | ((context: PluginContext) => PluginVirtualModule[] | Promise<PluginVirtualModule[]>);
  deploymentAdapters?:
    | PluginDeploymentAdapter[]
    | ((context: PluginContext) => PluginDeploymentAdapter[] | Promise<PluginDeploymentAdapter[]>);
  configSchemas?:
    | PluginConfigSchema[]
    | ((context: PluginContext) => PluginConfigSchema[] | Promise<PluginConfigSchema[]>);
  documentation?: string[] | ((context: PluginContext) => string[] | Promise<string[]>);
  typeDefinitions?: string[] | ((context: PluginContext) => string[] | Promise<string[]>);

  setup?(context: PluginContext): void | Promise<void>;
  configure?(config: Record<string, unknown>, context: PluginContext): void | Promise<void>;
  configResolved?(
    config: Readonly<Record<string, unknown>>,
    context: PluginContext,
  ): void | Promise<void>;
  transformAst?(ast: PageAst, context: TransformContext): PageAst | void | Promise<PageAst | void>;
  transformCode?(code: string, context: TransformContext): string | void | Promise<string | void>;
  diagnostics?(ast: PageAst, context: TransformContext): WrnDiagnostic[] | Promise<WrnDiagnostic[]>;
  routes?(routes: unknown[], context: PluginContext): unknown[] | void | Promise<unknown[] | void>;
  configureServer?(server: unknown, context: PluginContext): void | Promise<void>;
  buildStart?(context: PluginContext): void | Promise<void>;
  buildEnd?(result: unknown, context: PluginContext): void | Promise<void>;
  render?(html: string, context: PluginContext): string | void | Promise<string | void>;
  deploy?(result: unknown, context: PluginContext): void | Promise<void>;
  shutdown?(context: PluginContext): void | Promise<void>;
  hmrUpdate?(files: readonly string[], context: PluginContext): void | Promise<void>;
  devToolbarPanels?(
    context: PluginContext,
  ): PluginDevToolbarPanel[] | Promise<PluginDevToolbarPanel[]>;
}

export type PluginInput = WrnexusPlugin | false | null | undefined | PluginInput[];

export interface PackagePluginManifest {
  /** Plugin module relative to the package root. */
  plugin: string;
  /** Named factory/export. Defaults to `default`, then `plugin`. */
  export?: string;
  /** Invoke the resolved export with no arguments. Defaults to auto-detection. */
  factory?: boolean;
  /** Disable automatic discovery while keeping the manifest available to tools. */
  autoDiscover?: boolean;
}

export interface WrnexusPackageManifest {
  name?: string;
  version?: string;
  runtimes?: Array<"bun" | "node" | "edge" | "worker" | "service-worker" | "browser">;
  requires?: Array<
    "filesystem" | "tcp" | "process" | "websocket" | "crypto" | "streams" | "background-tasks"
  >;
  /** Framework capabilities requested by this package; applications may enforce explicit grants. */
  permissions?: PluginPermission[];
  compatibility?: {
    bunMin?: string;
    os?: string[];
  };
  plugin?: string | PackagePluginManifest;
  components?: string[];
  clientRuntimes?: ClientRuntimeDefinition[];
  assets?: PackageAssetDefinition[];
  styles?: PackageStyleDefinition[];
  routes?: PackageRouteDefinition[];
  middleware?: string[];
  migrations?: PackageMigrationDefinition[];
}

export interface PluginRunner {
  readonly plugins: readonly WrnexusPlugin[];
  configure(config: Record<string, unknown>): Promise<void>;
  configResolved(config: Readonly<Record<string, unknown>>): Promise<void>;
  transformAst(ast: PageAst, file: string): Promise<PageAst>;
  transformCode(code: string, file: string): Promise<string>;
  diagnostics(ast: PageAst, file: string): Promise<WrnDiagnostic[]>;
  contributions(): Promise<PluginContributions>;
  devToolbarPanels(): Promise<PluginDevToolbarPanel[]>;
  transformRoutes(routes: unknown[]): Promise<unknown[]>;
  render(html: string): Promise<string>;
  hook(
    name: "buildStart" | "buildEnd" | "configureServer" | "deploy" | "shutdown" | "hmrUpdate",
    value?: unknown,
  ): Promise<void>;
}
