import { MODAL_TONES } from './modal-theme.ts';
import { infoRow } from './modal-surface-helpers.ts';
import type {
  ConfigModalActionContext,
  ConfigModalRow,
  ConfigModalSurface,
  ConfigModalView,
} from '../../input/config-modal-types.ts';
import { encodeConnectionPayload, generateQrMatrix, renderQrToString } from '@pellux/goodvibes-sdk/platform/pairing';

// ---------------------------------------------------------------------------
// QR Code → 'pairing' config-modal surface (W6.1 group-B port). Connection
// URL/token/username(/password) plus a scannable QR block — read/navigate only.
// Token regeneration is a destructive mutation (it invalidates any live
// companion session) that the panel gated behind an in-panel confirm; per
// charter that confirm is never folded into a modal, so regenerate routes to a
// command instead. `getConnectionInfo` is a lazy thunk (loaded once on first
// buildView, cached) so a missing daemon home degrades honestly rather than
// throwing at modal-registration time.
//
// KNOWN GAP (flagged in the report): src/input/commands/qrcode-runtime.ts only
// registers `/qrcode` (open); it has no `regenerate` subcommand yet, and this
// surface cannot observe a regenerated token without being re-opened.
// ---------------------------------------------------------------------------

export interface PairingModalConnectionInfo {
  readonly url: string;
  readonly token: string;
  readonly username: string;
  readonly password?: string;
  readonly version?: string;
  readonly surface?: string;
}
export interface PairingModalControlPlaneSnapshot { readonly activeClientIds: readonly string[]; }
export interface PairingModalReadModel<T> { getSnapshot(): T; }

export interface PairingModalDeps {
  /** Lazy connection-info provider — returns null when the daemon/companion
   *  token cannot be resolved (honest degraded state instead of a throw). */
  readonly getConnectionInfo: () => PairingModalConnectionInfo | null;
  readonly controlPlaneReadModel?: PairingModalReadModel<PairingModalControlPlaneSnapshot>;
  readonly copyToClipboard?: (text: string) => void;
}

// Fixed-width placeholder — deliberately NOT derived from the real secret's
// length (mirrors QrPanel.SECRET_MASK), so masking doesn't leak length.
const SECRET_MASK = '••••••••••••';

class PairingModalSurface implements ConfigModalSurface {
  readonly name = 'pairing-modal';
  readonly title = 'Companion Pairing';
  private info: PairingModalConnectionInfo | null | undefined = undefined;
  private revealed = false;
  private requestRender: () => void = () => {};

  constructor(private readonly deps: PairingModalDeps) {}

  readonly actions = [
    { key: 'v', id: 'toggleReveal', label: 'reveal token' },
    { key: 'c', id: 'copyToken', label: 'copy token', enabledFor: () => Boolean(this.deps.copyToClipboard) },
    { key: 'r', id: 'regenerate', label: 'regenerate token' },
  ];

  onOpen(requestRender: () => void): void { this.requestRender = requestRender; }

  private ensureInfo(): PairingModalConnectionInfo | null {
    if (this.info === undefined) {
      try { this.info = this.deps.getConnectionInfo(); } catch { this.info = null; }
    }
    return this.info;
  }

  buildView(): ConfigModalView {
    const info = this.ensureInfo();
    if (!info) {
      return { title: 'Companion Pairing', degraded: 'Companion pairing is unavailable — the daemon connection info could not be resolved for this session.', tabs: [{ id: 'pairing', label: 'Pairing', rows: [] }] };
    }
    const { url, token, username, password } = info;
    const displayToken = this.revealed ? token : SECRET_MASK;
    const displayPassword = password !== undefined ? (this.revealed ? password : SECRET_MASK) : undefined;

    const rows: ConfigModalRow[] = [];
    rows.push(infoRow('intro', 'Scan with the GoodVibes companion app to pair this session.', { dim: true }));
    rows.push(infoRow('url', `URL       ${url}`));
    rows.push(infoRow('token', `Token     ${displayToken}`, { fg: MODAL_TONES.reasoning }));
    rows.push(infoRow('username', `Username  ${username}`));
    if (displayPassword !== undefined) rows.push(infoRow('password', `Password  ${displayPassword}`));
    if (this.deps.controlPlaneReadModel) {
      const connected = this.deps.controlPlaneReadModel.getSnapshot().activeClientIds.length;
      rows.push(infoRow('companions', `Companions connected: ${connected}`, connected > 0 ? { fg: MODAL_TONES.good } : { dim: true }));
    }

    // ASCII QR block — same payload shape as QrPanel.render(); the multi-line
    // preformatted string is split into one non-selectable row per block row.
    const payload = encodeConnectionPayload({
      url, token, username,
      ...(password !== undefined ? { password } : {}),
      version: info.version ?? '0.0.0',
      surface: info.surface ?? 'tui',
    });
    const qr = renderQrToString(generateQrMatrix(payload));
    qr.split('\n').forEach((line, i) => rows.push(infoRow(`qr:${i}`, line, { fg: MODAL_TONES.qrDark, bg: MODAL_TONES.qrLight })));

    return {
      title: 'Companion Pairing',
      tabs: [{ id: 'pairing', label: 'Pairing', rows, hints: [this.revealed ? 'v hide token' : 'v reveal token'] }],
    };
  }

  onAction(id: string, ctx: ConfigModalActionContext): void {
    const info = this.ensureInfo();
    if (id === 'toggleReveal') { this.revealed = !this.revealed; this.requestRender(); return; }
    if (id === 'copyToken') {
      if (!this.deps.copyToClipboard || !info) { ctx.print('Clipboard not available.'); return; }
      this.deps.copyToClipboard(info.token);
      ctx.print('Token copied to clipboard.');
      return;
    }
    if (id === 'regenerate') {
      void ctx.executeCommand?.('qrcode', ['regenerate']);
      ctx.setStatus('Dispatched /qrcode regenerate (reopen to see the new token).');
    }
  }
}

export function createPairingModalSurface(deps: PairingModalDeps): ConfigModalSurface {
  return new PairingModalSurface(deps);
}

/**
 * Deterministic golden fixture: a frozen connectionInfo literal (no real
 * token/QR generation call — encodeConnectionPayload/generateQrMatrix are pure
 * functions of this literal) and a frozen control-plane snapshot.
 */
export function pairingModalGoldenSurface(): ConfigModalSurface {
  const connectionInfo: PairingModalConnectionInfo = {
    url: 'http://192.168.1.50:3141', token: 'golden-token-0123456789abcdef', username: 'golden-user', password: 'golden-pass', version: '0.0.0', surface: 'tui',
  };
  return createPairingModalSurface({
    getConnectionInfo: () => connectionInfo,
    controlPlaneReadModel: { getSnapshot: () => ({ activeClientIds: ['golden-client-1'] }) },
    copyToClipboard: () => {},
  });
}
