/**
 * mosaic telemetry — CU-06-02 (local) + CU-06-03 (remote)
 *
 * Local half:  mosaic telemetry local {status, tail, jaeger}
 * Remote half: mosaic telemetry {status, opt-in, opt-out, test, upload}
 *
 * Remote upload is DISABLED by default (dry-run mode).
 * Per session-1 decision: ship upload/test in dry-run-only mode until
 * the mosaicstack.dev server endpoint is live.
 *
 * Telemetry client: uses a forward-compat shim (see telemetry/client-shim.ts)
 * because @mosaicstack/telemetry-client-js is not yet published.
 */

import type { Command } from 'commander';
import { confirm, intro, outro, isCancel, cancel } from '@clack/prompts';
import { DEFAULT_MOSAIC_HOME } from '../constants.js';
import { getTelemetryClient } from '../telemetry/client-shim.js';
import { readConsent, optIn, optOut, recordUpload } from '../telemetry/consent-store.js';

// ─── helpers ─────────────────────────────────────────────────────────────────

function getMosaicHome(): string {
  return process.env['MOSAIC_HOME'] ?? DEFAULT_MOSAIC_HOME;
}

function isDryRun(): boolean {
  return process.env['MOSAIC_TELEMETRY_DRY_RUN'] === '1';
}

/** Try to open a URL — best-effort, does not fail if unsupported. */
async function tryOpenUrl(url: string): Promise<void> {
  try {
    const { spawn } = await import('node:child_process');
    // `start` is a Windows shell builtin — must be invoked via cmd /c.
    const [bin, args] =
      process.platform === 'darwin'
        ? (['open', [url]] as [string, string[]])
        : process.platform === 'win32'
          ? (['cmd', ['/c', 'start', '', url]] as [string, string[]])
          : (['xdg-open', [url]] as [string, string[]]);
    spawn(bin, args, { detached: true, stdio: 'ignore' }).unref();
  } catch {
    // Best-effort — silently skip if unavailable.
    console.log(`Open this URL in your browser: ${url}`);
  }
}

// ─── local subcommands ───────────────────────────────────────────────────────

function registerLocalCommand(parent: Command): void {
  const local = parent
    .command('local')
    .description('Inspect the local OpenTelemetry stack')
    .configureHelp({ sortSubcommands: true });

  // ── telemetry local status ──────────────────────────────────────────────

  local
    .command('status')
    .description('Report reachability of the local OTEL collector endpoint')
    .action(async () => {
      const endpoint = process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] ?? 'http://localhost:4318';
      const serviceName = process.env['OTEL_SERVICE_NAME'] ?? 'mosaic-gateway';
      const exportInterval = '15000ms'; // matches tracing.ts PeriodicExportingMetricReader

      console.log(`OTEL endpoint:     ${endpoint}`);
      console.log(`Service name:      ${serviceName}`);
      console.log(`Export interval:   ${exportInterval}`);
      console.log('');

      try {
        const response = await fetch(endpoint, {
          method: 'GET',
          signal: AbortSignal.timeout(3000),
        });
        // OTLP collector typically returns 404 for GET on the root path —
        // but a response means it's listening.
        console.log(`Status: reachable (HTTP ${String(response.status)})`);
      } catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        console.log(`Status: unreachable — ${msg}`);
        console.log('');
        console.log('Hint: start the local stack with `docker compose up -d`');
      }
    });

  // ── telemetry local tail ────────────────────────────────────────────────

  local
    .command('tail')
    .description('Explain how to view live traces from the local OTEL stack')
    .action(() => {
      const jaegerUrl = process.env['JAEGER_UI_URL'] ?? 'http://localhost:16686';

      console.log('OTLP is a push protocol — there is no log tail.');
      console.log('');
      console.log('Traces flow: your service → OTEL Collector → Jaeger');
      console.log('');
      console.log(`Jaeger UI:  ${jaegerUrl}`);
      console.log('Run `mosaic telemetry local jaeger` to print the URL (or open it).');
      console.log('');
      console.log('For raw collector output:');
      console.log('  docker compose logs -f otel-collector');
    });

  // ── telemetry local jaeger ──────────────────────────────────────────────

  local
    .command('jaeger')
    .description('Print the Jaeger UI URL (use --open to launch in browser)')
    .option('--open', 'Open the Jaeger UI in the default browser')
    .action(async (opts: { open?: boolean }) => {
      const jaegerUrl = process.env['JAEGER_UI_URL'] ?? 'http://localhost:16686';
      console.log(jaegerUrl);

      if (opts.open) {
        await tryOpenUrl(jaegerUrl);
      }
    });
}

// ─── remote subcommands ──────────────────────────────────────────────────────

function registerRemoteStatusCommand(cmd: Command): void {
  cmd
    .command('status')
    .description('Print the remote telemetry upload status and consent state')
    .action(() => {
      const mosaicHome = getMosaicHome();
      const consent = readConsent(mosaicHome);
      const remoteEndpoint = process.env['MOSAIC_TELEMETRY_ENDPOINT'] ?? '(not configured)';
      const dryRunActive = isDryRun();

      console.log('Remote telemetry status');
      console.log('─────────────────────────────────────────────');
      console.log(`  Remote upload enabled: ${String(consent.remoteEnabled)}`);
      console.log(`  Remote endpoint:       ${remoteEndpoint}`);
      if (consent.optedInAt) {
        console.log(`  Opted in:              ${consent.optedInAt}`);
      }
      if (consent.optedOutAt) {
        console.log(`  Opted out:             ${consent.optedOutAt}`);
      }
      if (consent.lastUploadAt) {
        console.log(`  Last upload:           ${consent.lastUploadAt}`);
      } else {
        console.log('  Last upload:           (never)');
      }
      if (dryRunActive) {
        console.log('');
        console.log('  [dry-run] MOSAIC_TELEMETRY_DRY_RUN=1 is set — uploads are suppressed');
      }
      console.log('');
      console.log('Local OTEL stack always active (see `mosaic telemetry local status`).');
    });
}

function registerOptInCommand(cmd: Command): void {
  cmd
    .command('opt-in')
    .description('Enable remote telemetry upload (requires explicit consent)')
    .action(async () => {
      const mosaicHome = getMosaicHome();
      const current = readConsent(mosaicHome);

      if (current.remoteEnabled) {
        console.log('Remote telemetry upload is already enabled.');
        console.log(`Opted in: ${current.optedInAt ?? '(unknown)'}`);
        return;
      }

      intro('Mosaic remote telemetry opt-in');

      console.log('');
      console.log('What gets uploaded:');
      console.log('  - CLI command names and completion status (no arguments / values)');
      console.log('  - Error types (no stack traces or user data)');
      console.log('  - Mosaic version and platform metadata');
      console.log('');
      console.log('What is NEVER uploaded:');
      console.log('  - File contents, code, or credentials');
      console.log('  - Personal information or agent conversation data');
      console.log('');
      console.log('Note: remote upload is currently in dry-run mode until');
      console.log('      the mosaicstack.dev telemetry endpoint is live.');
      console.log('');

      const confirmed = await confirm({
        message: 'Enable remote telemetry upload?',
      });

      if (isCancel(confirmed) || !confirmed) {
        cancel('Opt-in cancelled — no changes made.');
        return;
      }

      const consent = optIn(mosaicHome);
      outro(`Remote telemetry enabled. Opted in at ${consent.optedInAt ?? ''}`);
    });
}

function registerOptOutCommand(cmd: Command): void {
  cmd
    .command('opt-out')
    .description('Disable remote telemetry upload')
    .action(async () => {
      const mosaicHome = getMosaicHome();
      const current = readConsent(mosaicHome);

      if (!current.remoteEnabled) {
        console.log('Remote telemetry upload is already disabled.');
        return;
      }

      intro('Mosaic remote telemetry opt-out');
      console.log('');
      console.log('This will disable remote upload of anonymised usage data.');
      console.log('Local OTEL tracing (to Jaeger) will remain active — it is');
      console.log('independent of this consent state.');
      console.log('');

      const confirmed = await confirm({
        message: 'Disable remote telemetry upload?',
      });

      if (isCancel(confirmed) || !confirmed) {
        cancel('Opt-out cancelled — no changes made.');
        return;
      }

      const consent = optOut(mosaicHome);
      outro(`Remote telemetry disabled. Opted out at ${consent.optedOutAt ?? ''}`);
      console.log('Local OTEL stack (Jaeger) remains active.');
    });
}

function registerTestCommand(cmd: Command): void {
  cmd
    .command('test')
    .description('Synthesise a fake event and print the payload that would be sent (dry-run)')
    .option('--upload', 'Actually upload (requires consent + MOSAIC_TELEMETRY_ENDPOINT)')
    .action(async (opts: { upload?: boolean }) => {
      const mosaicHome = getMosaicHome();
      const consent = readConsent(mosaicHome);
      const dryRunActive = isDryRun() || !opts.upload;

      if (!dryRunActive && !consent.remoteEnabled) {
        console.error('Remote upload is not enabled. Run `mosaic telemetry opt-in` first.');
        process.exit(1);
      }

      const fakeEvent = {
        name: 'mosaic.cli.test',
        properties: {
          command: 'telemetry test',
          version: process.env['npm_package_version'] ?? 'unknown',
          platform: process.platform,
        },
        timestamp: new Date().toISOString(),
      };

      const endpoint = process.env['MOSAIC_TELEMETRY_ENDPOINT'];
      const client = getTelemetryClient();

      client.init({
        endpoint,
        dryRun: dryRunActive,
        labels: { source: 'mosaic-cli' },
      });

      client.captureEvent(fakeEvent);

      if (dryRunActive) {
        console.log('[dry-run] telemetry test — payload that would be sent:');
        console.log(JSON.stringify(fakeEvent, null, 2));
        console.log('');
        console.log('No network call made. Pass --upload to attempt real delivery.');
      } else {
        try {
          await client.upload();
          recordUpload(mosaicHome);
          console.log('Event delivered.');
        } catch (err) {
          // The shim throws when a real POST is attempted — make it clear nothing was sent.
          console.error(err instanceof Error ? err.message : String(err));
          process.exit(1);
        }
      }
    });
}

function registerUploadCommand(cmd: Command): void {
  cmd
    .command('upload')
    .description('Send pending telemetry events to the remote endpoint')
    .action(async () => {
      const mosaicHome = getMosaicHome();
      const consent = readConsent(mosaicHome);
      const dryRunActive = isDryRun();

      if (!consent.remoteEnabled) {
        console.log('[dry-run] telemetry upload — no network call made');
        console.log('Remote upload is disabled. Run `mosaic telemetry opt-in` to enable.');
        return;
      }

      const endpoint = process.env['MOSAIC_TELEMETRY_ENDPOINT'];

      if (dryRunActive || !endpoint) {
        console.log('[dry-run] telemetry upload — no network call made');
        if (!endpoint) {
          console.log('MOSAIC_TELEMETRY_ENDPOINT is not set — running in dry-run mode.');
        }
        if (dryRunActive) {
          console.log('MOSAIC_TELEMETRY_DRY_RUN=1 — uploads suppressed.');
        }
        console.log('');
        console.log('Dry-run is the default until the mosaicstack.dev telemetry endpoint is live.');
        return;
      }

      const client = getTelemetryClient();
      client.init({ endpoint, dryRun: false, labels: { source: 'mosaic-cli' } });

      try {
        await client.upload();
        recordUpload(mosaicHome);
        console.log('Upload complete.');
      } catch (err) {
        // The shim throws when a real POST is attempted — make it clear nothing was sent.
        console.error(err instanceof Error ? err.message : String(err));
        process.exit(1);
      }
    });
}

// ─── public registration ──────────────────────────────────────────────────────

export function registerTelemetryCommand(program: Command): void {
  const cmd = program
    .command('telemetry')
    .description('Inspect and manage telemetry (local OTEL stack + remote upload)')
    .configureHelp({ sortSubcommands: true });

  // ── local subgroup ──────────────────────────────────────────────────────
  registerLocalCommand(cmd);

  // ── remote subcommands ──────────────────────────────────────────────────
  registerRemoteStatusCommand(cmd);
  registerOptInCommand(cmd);
  registerOptOutCommand(cmd);
  registerTestCommand(cmd);
  registerUploadCommand(cmd);
}
