import { randomUUID } from 'node:crypto';
import type { Command } from 'commander';
import { withAuth } from './with-auth.js';
import {
  attachInteractionSession,
  enrollInteractionSession,
  fetchInteractionHealth,
  fetchInteractionSessions,
  fetchInteractionStatus,
  fetchInteractionTree,
  recoverInteractionSession,
  sendInteractionMessage,
  stopInteractionSession,
  streamInteractionSession,
} from '../tui/gateway-api.js';

interface InteractionOptions {
  gateway: string;
  agent?: string;
  correlationId?: string;
}

function configuredAgentName(options: InteractionOptions): string {
  const name = options.agent?.trim() || process.env['MOSAIC_AGENT_NAME']?.trim();
  if (!name) throw new Error('Interaction agent name is required (--agent or MOSAIC_AGENT_NAME)');
  return name;
}

async function authenticatedGateway(options: InteractionOptions) {
  return withAuth(options.gateway);
}

async function authRequest(options: InteractionOptions) {
  const auth = await authenticatedGateway(options);
  return {
    gateway: auth.gateway,
    cookie: auth.cookie,
    agentName: configuredAgentName(options),
    correlationId: options.correlationId?.trim() || randomUUID(),
  };
}

function options(command: Command): Command {
  return command
    .option('-g, --gateway <url>', 'Gateway URL', 'http://localhost:14242')
    .option('--agent <name>', 'Configured interaction agent name')
    .option('--correlation-id <id>', 'Correlation ID for audit tracing');
}

function print(value: unknown): void {
  console.log(JSON.stringify(value, null, 2));
}

/**
 * Generic interaction command surface. The deployed instance name is data
 * (`--agent` / MOSAIC_AGENT_NAME), not a source-code command literal.
 */
export function registerInteractionCommand(program: Command): Command {
  const command = program
    .command('interaction')
    .description('Operate a configured durable interaction agent')
    .configureHelp({ sortSubcommands: true });

  options(
    command
      .command('status')
      .description('Show credential-safe effective policy and provider status'),
  ).action(async (opts: InteractionOptions) => {
    const auth = await authenticatedGateway(opts);
    print(await fetchInteractionStatus(auth.gateway, auth.cookie));
  });

  options(
    command
      .command('health')
      .description('Show readiness without configuration or credential data'),
  ).action(async (opts: InteractionOptions) => {
    print(await fetchInteractionHealth(opts.gateway));
  });

  options(
    command
      .command('sessions <provider>')
      .description('List provider sessions visible to this actor'),
  ).action(async (provider: string, opts: InteractionOptions) => {
    const request = await authRequest(opts);
    print(
      await fetchInteractionSessions(request.gateway, request.cookie, {
        ...request,
        providerId: provider,
      }),
    );
  });

  options(
    command.command('tree <provider>').description('Show the provider session hierarchy'),
  ).action(async (provider: string, opts: InteractionOptions) => {
    const request = await authRequest(opts);
    print(
      await fetchInteractionTree(request.gateway, request.cookie, {
        ...request,
        providerId: provider,
      }),
    );
  });

  options(
    command
      .command('enroll <sessionId> <provider> <runtimeSessionId>')
      .description('Bind an authorized runtime session to a durable conversation handle'),
  ).action(
    async (
      sessionId: string,
      providerId: string,
      runtimeSessionId: string,
      opts: InteractionOptions,
    ) => {
      const request = await authRequest(opts);
      print(
        await enrollInteractionSession(request.gateway, request.cookie, {
          ...request,
          sessionId,
          providerId,
          runtimeSessionId,
        }),
      );
    },
  );

  options(
    command
      .command('attach <sessionId>')
      .description('Create a scoped read or write attachment and stream the session'),
  )
    .option('--control', 'Request control mode (provider policy may deny it)')
    .action(async (sessionId: string, opts: InteractionOptions & { control?: boolean }) => {
      const request = await authRequest(opts);
      const attachment = await attachInteractionSession(request.gateway, request.cookie, {
        ...request,
        sessionId,
        mode: opts.control ? 'control' : 'read',
      });
      print(attachment);
      for await (const event of streamInteractionSession(request.gateway, request.cookie, {
        ...request,
        sessionId,
      })) {
        print(event);
      }
    });

  const send = options(
    command
      .command('send <sessionId> <message>')
      .description('Durably queue and dispatch a message'),
  )
    .option('--idempotency-key <key>', 'Stable idempotency key')
    .action(
      async (
        sessionId: string,
        message: string,
        opts: InteractionOptions & { idempotencyKey?: string },
      ) => {
        const request = await authRequest(opts);
        print(
          await sendInteractionMessage(request.gateway, request.cookie, {
            ...request,
            sessionId,
            content: message,
            idempotencyKey: opts.idempotencyKey?.trim() || randomUUID(),
          }),
        );
      },
    );
  void send;

  options(
    command
      .command('chat <sessionId> <message>')
      .description('Send a chat message through the durable interaction session'),
  )
    .option('--idempotency-key <key>', 'Stable idempotency key')
    .action(
      async (
        sessionId: string,
        message: string,
        opts: InteractionOptions & { idempotencyKey?: string },
      ) => {
        const request = await authRequest(opts);
        print(
          await sendInteractionMessage(request.gateway, request.cookie, {
            ...request,
            sessionId,
            content: message,
            idempotencyKey: opts.idempotencyKey?.trim() || randomUUID(),
          }),
        );
      },
    );

  options(
    command
      .command('stop <sessionId>')
      .description('Terminate a session using a one-time exact-action approval'),
  )
    .requiredOption('--approval <ref>', 'Durable exact-action approval reference')
    .action(async (sessionId: string, opts: InteractionOptions & { approval: string }) => {
      const request = await authRequest(opts);
      print(
        await stopInteractionSession(request.gateway, request.cookie, {
          ...request,
          sessionId,
          approvalRef: opts.approval,
        }),
      );
    });

  options(
    command
      .command('recover <sessionId>')
      .description(
        'Recover durable inbox/checkpoint state without replaying ambiguous outbox effects',
      ),
  ).action(async (sessionId: string, opts: InteractionOptions) => {
    const request = await authRequest(opts);
    print(
      await recoverInteractionSession(request.gateway, request.cookie, { ...request, sessionId }),
    );
  });

  return command;
}
