import type { CliDeps } from "../cli/deps.js";
import { info, success } from "../globals.js";
import type { RuntimeEnv } from "../runtime.js";
import type { Provider } from "../utils.js";
import { sendViaIpc } from "../web/ipc.js";

export async function sendCommand(
  opts: {
    to: string;
    message: string;
    wait: string;
    poll: string;
    provider: Provider;
    json?: boolean;
    dryRun?: boolean;
    media?: string;
    serveMedia?: boolean;
  },
  deps: CliDeps,
  runtime: RuntimeEnv,
) {
  deps.assertProvider(opts.provider);
  const waitSeconds = Number.parseInt(opts.wait, 10);
  const pollSeconds = Number.parseInt(opts.poll, 10);

  if (Number.isNaN(waitSeconds) || waitSeconds < 0) {
    throw new Error("Wait must be >= 0 seconds");
  }
  if (Number.isNaN(pollSeconds) || pollSeconds <= 0) {
    throw new Error("Poll must be > 0 seconds");
  }

  if (opts.provider === "web") {
    if (opts.dryRun) {
      runtime.log(
        `[dry-run] would send via web -> ${opts.to}: ${opts.message}${opts.media ? ` (media ${opts.media})` : ""}`,
      );
      return;
    }
    if (waitSeconds !== 0) {
      runtime.log(info("Wait/poll are Twilio-only; ignored for provider=web."));
    }

    // Try to send via IPC to running relay first (avoids Signal session corruption)
    const ipcResult = await sendViaIpc(opts.to, opts.message, opts.media);
    if (ipcResult) {
      if (ipcResult.success) {
        runtime.log(
          success(`✅ Sent via relay IPC. Message ID: ${ipcResult.messageId}`),
        );
        if (opts.json) {
          runtime.log(
            JSON.stringify(
              {
                provider: "web",
                via: "ipc",
                to: opts.to,
                messageId: ipcResult.messageId,
                mediaUrl: opts.media ?? null,
              },
              null,
              2,
            ),
          );
        }
        return;
      }
      // IPC failed but relay is running - warn and fall back
      runtime.log(
        info(
          `IPC send failed (${ipcResult.error}), falling back to direct connection`,
        ),
      );
    }

    // Fall back to direct connection (creates new Baileys socket)
    const res = await deps
      .sendMessageWeb(opts.to, opts.message, {
        verbose: false,
        mediaUrl: opts.media,
      })
      .catch((err) => {
        runtime.error(`❌ Web send failed: ${String(err)}`);
        throw err;
      });
    if (opts.json) {
      runtime.log(
        JSON.stringify(
          {
            provider: "web",
            via: "direct",
            to: opts.to,
            messageId: res.messageId,
            mediaUrl: opts.media ?? null,
          },
          null,
          2,
        ),
      );
    }
    return;
  }

  if (opts.dryRun) {
    runtime.log(
      `[dry-run] would send via twilio -> ${opts.to}: ${opts.message}${opts.media ? ` (media ${opts.media})` : ""}`,
    );
    return;
  }

  let mediaUrl: string | undefined;
  if (opts.media) {
    mediaUrl = await deps.resolveTwilioMediaUrl(opts.media, {
      serveMedia: Boolean(opts.serveMedia),
      runtime,
    });
  }

  const result = await deps.sendMessage(
    opts.to,
    opts.message,
    { mediaUrl },
    runtime,
  );
  if (opts.json) {
    runtime.log(
      JSON.stringify(
        {
          provider: "twilio",
          to: opts.to,
          sid: result?.sid ?? null,
          mediaUrl: mediaUrl ?? null,
        },
        null,
        2,
      ),
    );
  }
  if (!result) return;
  if (waitSeconds === 0) return;
  await deps.waitForFinalStatus(
    result.client,
    result.sid,
    waitSeconds,
    pollSeconds,
    runtime,
  );
}
