import {
  DeesElement,
  html,
  customElement,
  type TemplateResult,
  css,
  state,
  cssManager,
} from '@design.estate/dees-element';
import * as plugins from '../../plugins.js';
import * as appstate from '../../appstate.js';
import * as interfaces from '../../../dist_ts_interfaces/index.js';
import { viewHostCss } from '../shared/css.js';
import { type IStatsTile } from '@design.estate/dees-catalog';

/**
 * Toggle form field visibility based on checkbox states.
 * Used in Create and Edit VPN client dialogs.
 */
function setupFormVisibility(formEl: any) {
  const show = 'flex'; // match dees-form's flex layout
  const updateVisibility = async () => {
    const data = await formEl.collectFormData();
    const contentEl = formEl.closest('.content') || formEl.parentElement;
    if (!contentEl) return;
    const hostIpGroup = contentEl.querySelector('.hostIpGroup') as HTMLElement;
    const hostIpDetails = contentEl.querySelector('.hostIpDetails') as HTMLElement;
    const staticIpGroup = contentEl.querySelector('.staticIpGroup') as HTMLElement;
    const vlanIdGroup = contentEl.querySelector('.vlanIdGroup') as HTMLElement;
    const aclGroup = contentEl.querySelector('.aclGroup') as HTMLElement;
    if (hostIpGroup) hostIpGroup.style.display = show;
    if (hostIpDetails) hostIpDetails.style.display = data.useHostIp ? show : 'none';
    if (staticIpGroup) staticIpGroup.style.display = data.useDhcp ? 'none' : show;
    if (vlanIdGroup) vlanIdGroup.style.display = data.forceVlan ? show : 'none';
    if (aclGroup) aclGroup.style.display = data.allowAdditionalAcls ? show : 'none';
  };
  formEl.changeSubject.subscribe(() => updateVisibility());
  updateVisibility();
}

declare global {
  interface HTMLElementTagNameMap {
    'ops-view-vpn': OpsViewVpn;
  }
}

@customElement('ops-view-vpn')
export class OpsViewVpn extends DeesElement {
  @state()
  accessor vpnState: appstate.IVpnState = appstate.vpnStatePart.getState()!;

  @state()
  accessor targetProfilesState: appstate.ITargetProfilesState = appstate.targetProfilesStatePart.getState()!;

  constructor() {
    super();
    const sub = appstate.vpnStatePart.select().subscribe((newState) => {
      this.vpnState = newState;
    });
    this.rxSubscriptions.push(sub);

    const targetProfilesSub = appstate.targetProfilesStatePart.select().subscribe((newState) => {
      this.targetProfilesState = newState;
    });
    this.rxSubscriptions.push(targetProfilesSub);
  }

  async connectedCallback() {
    await super.connectedCallback();
    await Promise.all([
      appstate.vpnStatePart.dispatchAction(appstate.fetchVpnAction, null),
      appstate.targetProfilesStatePart.dispatchAction(appstate.fetchTargetProfilesAction, null),
    ]);
  }

  public static styles = [
    cssManager.defaultStyles,
    viewHostCss,
    css`
      .vpnContainer {
        display: flex;
        flex-direction: column;
        gap: 24px;
      }

      .statusBadge {
        display: inline-flex;
        align-items: center;
        padding: 3px 10px;
        border-radius: 12px;
        font-size: 12px;
        font-weight: 600;
        letter-spacing: 0.02em;
        text-transform: uppercase;
      }

      .statusBadge.enabled {
        background: ${cssManager.bdTheme('#dcfce7', '#14532d')};
        color: ${cssManager.bdTheme('#166534', '#4ade80')};
      }

      .statusBadge.disabled {
        background: ${cssManager.bdTheme('#fef2f2', '#450a0a')};
        color: ${cssManager.bdTheme('#991b1b', '#f87171')};
      }

      .configDialog {
        padding: 16px;
        background: ${cssManager.bdTheme('#fffbeb', '#1c1917')};
        border: 1px solid ${cssManager.bdTheme('#fbbf24', '#92400e')};
        border-radius: 8px;
        margin-bottom: 16px;
      }

      .configDialog pre {
        display: block;
        padding: 12px;
        background: ${cssManager.bdTheme('#1f2937', '#111827')};
        color: #10b981;
        border-radius: 4px;
        font-family: monospace;
        font-size: 12px;
        white-space: pre-wrap;
        word-break: break-all;
        margin: 8px 0;
        user-select: all;
        max-height: 300px;
        overflow-y: auto;
      }

      .configDialog .warning {
        font-size: 12px;
        color: ${cssManager.bdTheme('#92400e', '#fbbf24')};
        margin-top: 8px;
      }

      .tagBadge {
        display: inline-flex;
        padding: 2px 8px;
        border-radius: 4px;
        font-size: 12px;
        font-weight: 500;
        background: ${cssManager.bdTheme('#eff6ff', '#172554')};
        color: ${cssManager.bdTheme('#1e40af', '#60a5fa')};
        margin-right: 4px;
      }

      .serverInfo {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
        gap: 12px;
        padding: 16px;
        background: ${cssManager.bdTheme('#f9fafb', '#111827')};
        border-radius: 8px;
        border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#1f2937')};
      }

      .serverInfo .infoItem {
        display: flex;
        flex-direction: column;
        gap: 4px;
      }

      .serverInfo .infoLabel {
        font-size: 11px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};
      }

      .serverInfo .infoValue {
        font-size: 14px;
        font-family: monospace;
        color: ${cssManager.bdTheme('#111827', '#f9fafb')};
      }
    `,
  ];

  /** Look up connected client info by clientId or assignedIp */
  private getConnectedInfo(client: interfaces.data.IVpnClient): interfaces.data.IVpnConnectedClient | undefined {
    return this.vpnState.connectedClients?.find(
      c => c.clientId === client.clientId || (client.assignedIp && c.assignedIp === client.assignedIp)
    );
  }

  render(): TemplateResult {
    const status = this.vpnState.status;
    const clients = this.vpnState.clients;
    const connectedClients = this.vpnState.connectedClients || [];
    const connectedCount = connectedClients.length;
    const totalClients = clients.length;
    const enabledClients = clients.filter(c => c.enabled).length;

    const statsTiles: IStatsTile[] = [
      {
        id: 'totalClients',
        title: 'Total Clients',
        type: 'number',
        value: totalClients,
        icon: 'lucide:users',
        description: 'Registered VPN clients',
        color: '#3b82f6',
      },
      {
        id: 'connectedClients',
        title: 'Connected',
        type: 'number',
        value: connectedCount,
        icon: 'lucide:link',
        description: 'Currently connected',
        color: '#10b981',
      },
      {
        id: 'enabledClients',
        title: 'Enabled',
        type: 'number',
        value: enabledClients,
        icon: 'lucide:shieldCheck',
        description: 'Active client registrations',
        color: '#8b5cf6',
      },
      {
        id: 'serverStatus',
        title: 'Server',
        type: 'text',
        value: status?.running ? 'Running' : 'Stopped',
        icon: 'lucide:server',
        description: status?.running ? 'Active' : 'VPN server not running',
        color: status?.running ? '#10b981' : '#ef4444',
      },
    ];

    return html`
      <dees-heading level="3">VPN</dees-heading>
      <div class="vpnContainer">

      ${this.vpnState.newClientConfig ? html`
        <div class="configDialog">
          <strong>Client created successfully!</strong>
          <div class="warning">Copy the WireGuard config now. It contains private keys that won't be shown again.</div>
          <pre>${this.vpnState.newClientConfig}</pre>
          <dees-button
            @click=${async () => {
              if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
                await navigator.clipboard.writeText(this.vpnState.newClientConfig!);
              }
              const { DeesToast } = await import('@design.estate/dees-catalog');
              DeesToast.createAndShow({ message: 'Config copied to clipboard', type: 'success', duration: 3000 });
            }}
          >Copy to Clipboard</dees-button>
          <dees-button
            @click=${() => {
              const blob = new Blob([this.vpnState.newClientConfig!], { type: 'text/plain' });
              const url = URL.createObjectURL(blob);
              const a = document.createElement('a');
              a.href = url;
              a.download = 'wireguard.conf';
              a.click();
              URL.revokeObjectURL(url);
            }}
          >Download .conf</dees-button>
          <dees-button
            @click=${async () => {
              const dataUrl = await plugins.qrcode.toDataURL(
                this.vpnState.newClientConfig!,
                { width: 400, margin: 2 }
              );
              const { DeesModal } = await import('@design.estate/dees-catalog');
              DeesModal.createAndShow({
                heading: 'WireGuard QR Code',
                content: html`
                  <div style="text-align: center; padding: 16px;">
                    <img src="${dataUrl}" style="max-width: 100%; image-rendering: pixelated;" />
                    <p style="margin-top: 12px; font-size: 13px; color: #9ca3af;">
                      Scan with the WireGuard app on your phone
                    </p>
                  </div>
                `,
                menuOptions: [
                  { name: 'Close', iconName: 'lucide:x', action: async (modalArg: any) => await modalArg.destroy() },
                ],
              });
            }}
          >Show QR Code</dees-button>
          <dees-button
            @click=${() => appstate.vpnStatePart.dispatchAction(appstate.clearNewClientConfigAction, null)}
          >Dismiss</dees-button>
        </div>
      ` : ''}

      <dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>

      ${status ? html`
        <div class="serverInfo">
          <div class="infoItem">
            <span class="infoLabel">Subnet</span>
            <span class="infoValue">${status.subnet}</span>
          </div>
          <div class="infoItem">
            <span class="infoLabel">WireGuard Port</span>
            <span class="infoValue">${status.wgListenPort}</span>
          </div>
          ${status.serverPublicKeys ? html`
            <div class="infoItem">
              <span class="infoLabel">WG Public Key</span>
              <span class="infoValue" style="font-size: 11px; word-break: break-all;">${status.serverPublicKeys.wgPublicKey}</span>
            </div>
          ` : ''}
        </div>
      ` : ''}

      <dees-table
        .heading1=${'VPN Clients'}
        .heading2=${'Manage WireGuard and SmartVPN client registrations'}
        .data=${clients}
        .rowKey=${'clientId'}
        .highlightUpdates=${'flash'}
        .showColumnFilters=${true}
        .displayFunction=${(client: interfaces.data.IVpnClient) => {
          const conn = this.getConnectedInfo(client);
          let statusHtml;
          if (!client.enabled) {
            statusHtml = html`<span class="statusBadge disabled">disabled</span>`;
          } else if (conn) {
            const since = new Date(conn.connectedSince).toLocaleString();
            statusHtml = html`<span class="statusBadge enabled" title="Since ${since}">connected</span>`;
          } else {
            statusHtml = html`<span class="statusBadge enabled" style="background: ${cssManager.bdTheme('#eff6ff', '#172554')}; color: ${cssManager.bdTheme('#1e40af', '#60a5fa')};">offline</span>`;
          }
          let routingHtml;
          if (client.useHostIp) {
            routingHtml = html`<span class="statusBadge" style="background: ${cssManager.bdTheme('#f3e8ff', '#3b0764')}; color: ${cssManager.bdTheme('#7c3aed', '#c084fc')};">Host IP</span>`;
          } else {
            routingHtml = html`<span class="statusBadge" style="background: ${cssManager.bdTheme('#eff6ff', '#172554')}; color: ${cssManager.bdTheme('#1e40af', '#60a5fa')};">Direct</span>`;
          }
          return {
            'Client ID': client.clientId,
            'Status': statusHtml,
            'Routing': routingHtml,
            'VPN IP': client.assignedIp || '-',
            'Source IP': conn?.sourceIp || '-',
            'Target Profiles': this.renderTargetProfileBadges(client.targetProfileIds),
            'Description': client.description || '-',
            'Created': new Date(client.createdAt).toLocaleDateString(),
          };
        }}
        .dataActions=${[
          {
            name: 'Create Client',
            iconName: 'lucide:plus',
            type: ['header'],
            actionFunc: async () => {
              await this.ensureTargetProfilesLoaded();
              const { DeesModal } = await import('@design.estate/dees-catalog');
              const profileCandidates = this.getTargetProfileCandidates();
              const createModal = await DeesModal.createAndShow({
                heading: 'Create VPN Client',
                content: html`
                  <dees-form>
                    <dees-input-text .key=${'clientId'} .label=${'Client ID'} .required=${true}></dees-input-text>
                    <dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
                    <dees-input-list .key=${'targetProfileNames'} .label=${'Target Profiles'} .placeholder=${'Type to search profiles...'} .candidates=${profileCandidates} .allowFreeform=${false}></dees-input-list>
                    <div class="hostIpGroup" style="display: flex; flex-direction: column; gap: 16px;">
                      <dees-input-checkbox .key=${'useHostIp'} .label=${'Get Host IP'} .value=${false}></dees-input-checkbox>
                      <div class="hostIpDetails" style="display: none; flex-direction: column; gap: 16px;">
                        <dees-input-checkbox .key=${'useDhcp'} .label=${'Get IP through DHCP'} .value=${false}></dees-input-checkbox>
                        <div class="staticIpGroup" style="display: flex; flex-direction: column; gap: 16px;">
                          <dees-input-text .key=${'staticIp'} .label=${'Static IP'}></dees-input-text>
                        </div>
                        <dees-input-checkbox .key=${'forceVlan'} .label=${'Force VLAN'} .value=${false}></dees-input-checkbox>
                        <div class="vlanIdGroup" style="display: none; flex-direction: column; gap: 16px;">
                          <dees-input-text .key=${'vlanId'} .label=${'VLAN ID'}></dees-input-text>
                        </div>
                      </div>
                    </div>
                    <dees-input-checkbox .key=${'allowAdditionalAcls'} .label=${'Allow additional ACLs'} .value=${false}></dees-input-checkbox>
                    <div class="aclGroup" style="display: none; flex-direction: column; gap: 16px;">
                      <dees-input-text .key=${'destinationAllowList'} .label=${'Destination Allow List'} .description=${'Comma-separated IPs or CIDRs'}></dees-input-text>
                      <dees-input-text .key=${'destinationBlockList'} .label=${'Destination Block List'} .description=${'Comma-separated IPs or CIDRs'}></dees-input-text>
                    </div>
                  </dees-form>
                `,
                menuOptions: [
                  {
                    name: 'Cancel',
                    iconName: 'lucide:x',
                    action: async (modalArg: any) => await modalArg.destroy(),
                  },
                  {
                    name: 'Create',
                    iconName: 'lucide:plus',
                    action: async (modalArg: any) => {
                      const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
                      if (!form) return;
                      const data = await form.collectFormData();
                      if (!data.clientId) return;
                      const targetProfileIds = this.resolveProfileLabelsToIds(
                        Array.isArray(data.targetProfileNames) ? data.targetProfileNames : [],
                      );

                      // Apply conditional logic based on checkbox states
                      const useHostIp = data.useHostIp ?? false;
                      const useDhcp = useHostIp && (data.useDhcp ?? false);
                      const staticIp = useHostIp && !useDhcp && data.staticIp ? data.staticIp : undefined;
                      const forceVlan = useHostIp && (data.forceVlan ?? false);
                      const vlanId = forceVlan && data.vlanId ? parseInt(data.vlanId, 10) : undefined;

                      const allowAcls = data.allowAdditionalAcls ?? false;
                      const destinationAllowList = allowAcls && data.destinationAllowList
                        ? data.destinationAllowList.split(',').map((s: string) => s.trim()).filter(Boolean)
                        : undefined;
                      const destinationBlockList = allowAcls && data.destinationBlockList
                        ? data.destinationBlockList.split(',').map((s: string) => s.trim()).filter(Boolean)
                        : undefined;

                      await appstate.vpnStatePart.dispatchAction(appstate.createVpnClientAction, {
                        clientId: data.clientId,
                        description: data.description || undefined,
                        targetProfileIds,

                        useHostIp,
                        useDhcp,
                        staticIp,
                        forceVlan,
                        vlanId,
                        destinationAllowList,
                        destinationBlockList,
                      });
                      await modalArg.destroy();
                    },
                  },
                ],
              });
              // Setup conditional form visibility after modal renders
              const createForm = createModal?.shadowRoot?.querySelector('.content')?.querySelector('dees-form') as any;
              if (createForm) {
                await createForm.updateComplete;
                setupFormVisibility(createForm);
              }
            },
          },
          {
            name: 'Detail',
            iconName: 'lucide:info',
            type: ['doubleClick'],
            actionFunc: async (actionData: any) => {
              const client = actionData.item as interfaces.data.IVpnClient;
              const conn = this.getConnectedInfo(client);
              const { DeesModal } = await import('@design.estate/dees-catalog');

              // Fetch telemetry on-demand
              let telemetryHtml = html`<p style="color: #9ca3af;">Loading telemetry...</p>`;
              try {
                const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
                  interfaces.requests.IReq_GetVpnClientTelemetry
                >('/typedrequest', 'getVpnClientTelemetry');
                const response = await request.fire({
                  identity: appstate.loginStatePart.getState()!.identity!,
                  clientId: client.clientId,
                });
                const t = response.telemetry;
                if (t) {
                  const formatBytes = (b: number) => b > 1048576 ? `${(b / 1048576).toFixed(1)} MB` : b > 1024 ? `${(b / 1024).toFixed(1)} KB` : `${b} B`;
                  telemetryHtml = html`
                    <div class="serverInfo" style="margin-top: 12px;">
                      <div class="infoItem"><span class="infoLabel">Bytes Sent</span><span class="infoValue">${formatBytes(t.bytesSent)}</span></div>
                      <div class="infoItem"><span class="infoLabel">Bytes Received</span><span class="infoValue">${formatBytes(t.bytesReceived)}</span></div>
                      <div class="infoItem"><span class="infoLabel">Keepalives</span><span class="infoValue">${t.keepalivesReceived}</span></div>
                      <div class="infoItem"><span class="infoLabel">Last Keepalive</span><span class="infoValue">${t.lastKeepaliveAt ? new Date(t.lastKeepaliveAt).toLocaleString() : '-'}</span></div>
                      <div class="infoItem"><span class="infoLabel">Packets Dropped</span><span class="infoValue">${t.packetsDropped}</span></div>
                    </div>
                  `;
                } else {
                  telemetryHtml = html`<p style="color: #9ca3af;">No telemetry available (client not connected)</p>`;
                }
              } catch {
                telemetryHtml = html`<p style="color: #9ca3af;">Telemetry unavailable</p>`;
              }

              DeesModal.createAndShow({
                heading: `Client: ${client.clientId}`,
                content: html`
                  <div class="serverInfo">
                    <div class="infoItem"><span class="infoLabel">Client ID</span><span class="infoValue">${client.clientId}</span></div>
                    <div class="infoItem"><span class="infoLabel">VPN IP</span><span class="infoValue">${client.assignedIp || '-'}</span></div>
                    <div class="infoItem"><span class="infoLabel">Status</span><span class="infoValue">${!client.enabled ? 'Disabled' : conn ? 'Connected' : 'Offline'}</span></div>
                    ${conn ? html`
                      <div class="infoItem"><span class="infoLabel">Connected Since</span><span class="infoValue">${new Date(conn.connectedSince).toLocaleString()}</span></div>
                      <div class="infoItem"><span class="infoLabel">Transport</span><span class="infoValue">${conn.transport}</span></div>
                      <div class="infoItem"><span class="infoLabel">Source IP</span><span class="infoValue">${conn.sourceIp || '-'}</span></div>
                    ` : ''}
                    <div class="infoItem"><span class="infoLabel">Description</span><span class="infoValue">${client.description || '-'}</span></div>
                    <div class="infoItem"><span class="infoLabel">Target Profiles</span><span class="infoValue">${this.resolveProfileIdsToLabels(client.targetProfileIds)?.join(', ') || '-'}</span></div>
                    <div class="infoItem"><span class="infoLabel">Routing</span><span class="infoValue">${client.useHostIp ? 'Host IP' : 'SmartProxy'}</span></div>
                    ${client.useHostIp ? html`
                      <div class="infoItem"><span class="infoLabel">Host IP</span><span class="infoValue">${client.useDhcp ? 'DHCP' : client.staticIp ? `Static: ${client.staticIp}` : 'Not configured'}</span></div>
                      <div class="infoItem"><span class="infoLabel">VLAN</span><span class="infoValue">${client.forceVlan && client.vlanId != null ? `VLAN ${client.vlanId}` : 'No VLAN'}</span></div>
                    ` : ''}
                    <div class="infoItem"><span class="infoLabel">Allow List</span><span class="infoValue">${client.destinationAllowList?.length ? client.destinationAllowList.join(', ') : 'None'}</span></div>
                    <div class="infoItem"><span class="infoLabel">Block List</span><span class="infoValue">${client.destinationBlockList?.length ? client.destinationBlockList.join(', ') : 'None'}</span></div>
                    <div class="infoItem"><span class="infoLabel">Created</span><span class="infoValue">${new Date(client.createdAt).toLocaleString()}</span></div>
                    <div class="infoItem"><span class="infoLabel">Updated</span><span class="infoValue">${new Date(client.updatedAt).toLocaleString()}</span></div>
                  </div>
                  <h3 style="margin: 16px 0 4px; font-size: 14px;">Telemetry</h3>
                  ${telemetryHtml}
                `,
                menuOptions: [
                  { name: 'Close', iconName: 'lucide:x', action: async (m: any) => await m.destroy() },
                ],
              });
            },
          },
          {
            name: 'Enable',
            iconName: 'lucide:power',
            type: ['contextmenu', 'inRow'],
            actionRelevancyCheckFunc: (actionData: any) => !actionData.item.enabled,
            actionFunc: async (actionData: any) => {
              const client = actionData.item as interfaces.data.IVpnClient;
              await appstate.vpnStatePart.dispatchAction(appstate.toggleVpnClientAction, {
                clientId: client.clientId,
                enabled: true,
              });
            },
          },
          {
            name: 'Disable',
            iconName: 'lucide:power',
            type: ['contextmenu', 'inRow'],
            actionRelevancyCheckFunc: (actionData: any) => actionData.item.enabled,
            actionFunc: async (actionData: any) => {
              const client = actionData.item as interfaces.data.IVpnClient;
              await appstate.vpnStatePart.dispatchAction(appstate.toggleVpnClientAction, {
                clientId: client.clientId,
                enabled: false,
              });
            },
          },
          {
            name: 'Export Config',
            iconName: 'lucide:download',
            type: ['contextmenu', 'inRow'],
            actionFunc: async (actionData: any) => {
              const client = actionData.item as interfaces.data.IVpnClient;
              const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');

              const exportConfig = async (format: 'wireguard' | 'smartvpn') => {
                try {
                  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
                    interfaces.requests.IReq_ExportVpnClientConfig
                  >('/typedrequest', 'exportVpnClientConfig');
                  const response = await request.fire({
                    identity: appstate.loginStatePart.getState()!.identity!,
                    clientId: client.clientId,
                    format,
                  });
                  if (response.success && response.config) {
                    const ext = format === 'wireguard' ? 'conf' : 'json';
                    const blob = new Blob([response.config], { type: 'text/plain' });
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = `${client.clientId}.${ext}`;
                    a.click();
                    URL.revokeObjectURL(url);
                    DeesToast.createAndShow({ message: `${format} config downloaded`, type: 'success', duration: 3000 });
                  } else {
                    DeesToast.createAndShow({ message: response.message || 'Export failed', type: 'error', duration: 5000 });
                  }
                } catch (err: any) {
                  DeesToast.createAndShow({ message: err.message || 'Export failed', type: 'error', duration: 5000 });
                }
              };

              const showQrCode = async () => {
                try {
                  const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
                    interfaces.requests.IReq_ExportVpnClientConfig
                  >('/typedrequest', 'exportVpnClientConfig');
                  const response = await request.fire({
                    identity: appstate.loginStatePart.getState()!.identity!,
                    clientId: client.clientId,
                    format: 'wireguard',
                  });
                  if (response.success && response.config) {
                    const dataUrl = await plugins.qrcode.toDataURL(
                      response.config,
                      { width: 400, margin: 2 }
                    );
                    DeesModal.createAndShow({
                      heading: `QR Code: ${client.clientId}`,
                      content: html`
                        <div style="text-align: center; padding: 16px;">
                          <img src="${dataUrl}" style="max-width: 100%; image-rendering: pixelated;" />
                          <p style="margin-top: 12px; font-size: 13px; color: #9ca3af;">
                            Scan with the WireGuard app on your phone
                          </p>
                        </div>
                      `,
                      menuOptions: [
                        { name: 'Close', iconName: 'lucide:x', action: async (m: any) => await m.destroy() },
                      ],
                    });
                  } else {
                    DeesToast.createAndShow({ message: response.message || 'Export failed', type: 'error', duration: 5000 });
                  }
                } catch (err: any) {
                  DeesToast.createAndShow({ message: err.message || 'QR generation failed', type: 'error', duration: 5000 });
                }
              };

              DeesModal.createAndShow({
                heading: `Export Config: ${client.clientId}`,
                content: html`<p>Choose a config format to download.</p>`,
                menuOptions: [
                  {
                    name: 'WireGuard (.conf)',
                    iconName: 'lucide:shield',
                    action: async (modalArg: any) => {
                      await modalArg.destroy();
                      await exportConfig('wireguard');
                    },
                  },
                  {
                    name: 'SmartVPN (.json)',
                    iconName: 'lucide:braces',
                    action: async (modalArg: any) => {
                      await modalArg.destroy();
                      await exportConfig('smartvpn');
                    },
                  },
                  {
                    name: 'QR Code (WireGuard)',
                    iconName: 'lucide:qr-code',
                    action: async (modalArg: any) => {
                      await modalArg.destroy();
                      await showQrCode();
                    },
                  },
                  {
                    name: 'Cancel',
                    iconName: 'lucide:x',
                    action: async (modalArg: any) => await modalArg.destroy(),
                  },
                ],
              });
            },
          },
          {
            name: 'Edit',
            iconName: 'lucide:pencil',
            type: ['contextmenu', 'inRow'],
            actionFunc: async (actionData: any) => {
              const client = actionData.item as interfaces.data.IVpnClient;
              await this.ensureTargetProfilesLoaded();
              const { DeesModal } = await import('@design.estate/dees-catalog');
              const currentDescription = client.description ?? '';
              const currentTargetProfileNames = this.resolveProfileIdsToLabels(client.targetProfileIds) || [];
              const profileCandidates = this.getTargetProfileCandidates();
              const currentUseHostIp = client.useHostIp ?? false;
              const currentUseDhcp = client.useDhcp ?? false;
              const currentStaticIp = client.staticIp ?? '';
              const currentForceVlan = client.forceVlan ?? false;
              const currentVlanId = client.vlanId != null ? String(client.vlanId) : '';
              const currentAllowList = client.destinationAllowList?.join(', ') ?? '';
              const currentBlockList = client.destinationBlockList?.join(', ') ?? '';
              const currentAllowAcls = (client.destinationAllowList?.length ?? 0) > 0
                || (client.destinationBlockList?.length ?? 0) > 0;
              const editModal = await DeesModal.createAndShow({
                heading: `Edit: ${client.clientId}`,
                content: html`
                  <dees-form>
                    <dees-input-text .key=${'description'} .label=${'Description'} .value=${currentDescription}></dees-input-text>
                    <dees-input-list .key=${'targetProfileNames'} .label=${'Target Profiles'} .placeholder=${'Type to search profiles...'} .candidates=${profileCandidates} .allowFreeform=${false} .value=${currentTargetProfileNames}></dees-input-list>
                    <div class="hostIpGroup" style="display: flex; flex-direction: column; gap: 16px;">
                      <dees-input-checkbox .key=${'useHostIp'} .label=${'Get Host IP'} .value=${currentUseHostIp}></dees-input-checkbox>
                      <div class="hostIpDetails" style="display: ${currentUseHostIp ? 'flex' : 'none'}; flex-direction: column; gap: 16px;">
                        <dees-input-checkbox .key=${'useDhcp'} .label=${'Get IP through DHCP'} .value=${currentUseDhcp}></dees-input-checkbox>
                        <div class="staticIpGroup" style="display: ${currentUseDhcp ? 'none' : 'flex'}; flex-direction: column; gap: 16px;">
                          <dees-input-text .key=${'staticIp'} .label=${'Static IP'} .value=${currentStaticIp}></dees-input-text>
                        </div>
                        <dees-input-checkbox .key=${'forceVlan'} .label=${'Force VLAN'} .value=${currentForceVlan}></dees-input-checkbox>
                        <div class="vlanIdGroup" style="display: ${currentForceVlan ? 'flex' : 'none'}; flex-direction: column; gap: 16px;">
                          <dees-input-text .key=${'vlanId'} .label=${'VLAN ID'} .value=${currentVlanId}></dees-input-text>
                        </div>
                      </div>
                    </div>
                    <dees-input-checkbox .key=${'allowAdditionalAcls'} .label=${'Allow additional ACLs'} .value=${currentAllowAcls}></dees-input-checkbox>
                    <div class="aclGroup" style="display: ${currentAllowAcls ? 'flex' : 'none'}; flex-direction: column; gap: 16px;">
                      <dees-input-text .key=${'destinationAllowList'} .label=${'Destination Allow List'} .description=${'Comma-separated IPs or CIDRs'} .value=${currentAllowList}></dees-input-text>
                      <dees-input-text .key=${'destinationBlockList'} .label=${'Destination Block List'} .description=${'Comma-separated IPs or CIDRs'} .value=${currentBlockList}></dees-input-text>
                    </div>
                  </dees-form>
                `,
                menuOptions: [
                  { name: 'Cancel', iconName: 'lucide:x', action: async (modalArg: any) => await modalArg.destroy() },
                  {
                    name: 'Save',
                    iconName: 'lucide:check',
                    action: async (modalArg: any) => {
                      const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
                      if (!form) return;
                      const data = await form.collectFormData();
                      const targetProfileIds = this.resolveProfileLabelsToIds(
                        Array.isArray(data.targetProfileNames) ? data.targetProfileNames : [],
                      );

                      // Apply conditional logic based on checkbox states
                      const useHostIp = data.useHostIp ?? false;
                      const useDhcp = useHostIp && (data.useDhcp ?? false);
                      const staticIp = useHostIp && !useDhcp && data.staticIp ? data.staticIp : undefined;
                      const forceVlan = useHostIp && (data.forceVlan ?? false);
                      const vlanId = forceVlan && data.vlanId ? parseInt(data.vlanId, 10) : undefined;

                      const allowAcls = data.allowAdditionalAcls ?? false;
                      const destinationAllowList = allowAcls && data.destinationAllowList
                        ? data.destinationAllowList.split(',').map((s: string) => s.trim()).filter(Boolean)
                        : [];
                      const destinationBlockList = allowAcls && data.destinationBlockList
                        ? data.destinationBlockList.split(',').map((s: string) => s.trim()).filter(Boolean)
                        : [];

                      await appstate.vpnStatePart.dispatchAction(appstate.updateVpnClientAction, {
                        clientId: client.clientId,
                        description: data.description || undefined,
                        targetProfileIds,

                        useHostIp,
                        useDhcp,
                        staticIp,
                        forceVlan,
                        vlanId,
                        destinationAllowList,
                        destinationBlockList,
                      });
                      await modalArg.destroy();
                    },
                  },
                ],
              });
              // Setup conditional form visibility for edit dialog
              const editForm = editModal?.shadowRoot?.querySelector('.content')?.querySelector('dees-form') as any;
              if (editForm) {
                await editForm.updateComplete;
                setupFormVisibility(editForm);
              }
            },
          },
          {
            name: 'Rotate Keys',
            iconName: 'lucide:rotate-cw',
            type: ['contextmenu'],
            actionFunc: async (actionData: any) => {
              const client = actionData.item as interfaces.data.IVpnClient;
              const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
              DeesModal.createAndShow({
                heading: 'Rotate Client Keys',
                content: html`<p>Generate new keys for "${client.clientId}"? The old keys will be invalidated and the client will need the new config to reconnect.</p>`,
                menuOptions: [
                  { name: 'Cancel', iconName: 'lucide:x', action: async (modalArg: any) => await modalArg.destroy() },
                  {
                    name: 'Rotate',
                    iconName: 'lucide:rotate-cw',
                    action: async (modalArg: any) => {
                      try {
                        const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
                          interfaces.requests.IReq_RotateVpnClientKey
                        >('/typedrequest', 'rotateVpnClientKey');
                        const response = await request.fire({
                          identity: appstate.loginStatePart.getState()!.identity!,
                          clientId: client.clientId,
                        });
                        if (response.success && response.wireguardConfig) {
                          appstate.vpnStatePart.setState({
                            ...appstate.vpnStatePart.getState()!,
                            newClientConfig: response.wireguardConfig,
                          });
                        }
                        await modalArg.destroy();
                      } catch (err: any) {
                        DeesToast.createAndShow({ message: err.message || 'Rotate failed', type: 'error', duration: 5000 });
                      }
                    },
                  },
                ],
              });
            },
          },
          {
            name: 'Delete',
            iconName: 'lucide:trash2',
            type: ['contextmenu'],
            actionFunc: async (actionData: any) => {
              const client = actionData.item as interfaces.data.IVpnClient;
              const { DeesModal } = await import('@design.estate/dees-catalog');
              DeesModal.createAndShow({
                heading: 'Delete VPN Client',
                content: html`<p>Are you sure you want to delete client "${client.clientId}"?</p>`,
                menuOptions: [
                  { name: 'Cancel', iconName: 'lucide:x', action: async (modalArg: any) => await modalArg.destroy() },
                  {
                    name: 'Delete',
                    iconName: 'lucide:trash2',
                    action: async (modalArg: any) => {
                      await appstate.vpnStatePart.dispatchAction(appstate.deleteVpnClientAction, client.clientId);
                      await modalArg.destroy();
                    },
                  },
                ],
              });
            },
          },
        ]}
      ></dees-table>
      </div>
    `;
  }

  private async ensureTargetProfilesLoaded(): Promise<void> {
    await appstate.targetProfilesStatePart.dispatchAction(appstate.fetchTargetProfilesAction, null);
  }

  private renderTargetProfileBadges(ids?: string[]): TemplateResult | string {
    const labels = this.resolveProfileIdsToLabels(ids, {
      pendingLabel: 'Loading profile...',
      missingLabel: (id) => `Unknown profile (${id})`,
    });

    if (!labels?.length) {
      return '-';
    }

    return html`${labels.map((label) => html`<span class="tagBadge">${label}</span>`)}`;
  }

  /**
   * Build stable profile labels for list inputs.
   */
  private getTargetProfileChoices() {
    const profiles = this.targetProfilesState.profiles || [];
    const nameCounts = new Map<string, number>();

    for (const profile of profiles) {
      nameCounts.set(profile.name, (nameCounts.get(profile.name) || 0) + 1);
    }

    return profiles.map((profile) => ({
      id: profile.id,
      label: (nameCounts.get(profile.name) || 0) > 1
        ? `${profile.name} (${profile.id})`
        : profile.name,
    }));
  }

  private getTargetProfileCandidates() {
    return this.getTargetProfileChoices().map((profile) => ({ viewKey: profile.label }));
  }

  /**
   * Convert profile IDs to form labels (for populating edit form values).
   */
  private resolveProfileIdsToLabels(
    ids?: string[],
    options: {
      pendingLabel?: string;
      missingLabel?: (id: string) => string;
    } = {},
  ): string[] | undefined {
    if (!ids?.length) return undefined;
    const choices = this.getTargetProfileChoices();
    const labelsById = new Map(choices.map((profile) => [profile.id, profile.label]));
    return ids.map((id) => {
      const label = labelsById.get(id);
      if (label) {
        return label;
      }

      if (this.targetProfilesState.lastUpdated === 0 && !this.targetProfilesState.error) {
        return options.pendingLabel || 'Loading profile...';
      }

      return options.missingLabel?.(id) || id;
    });
  }

  /**
   * Convert profile form labels back to IDs.
   */
  private resolveProfileLabelsToIds(labels: string[]): string[] {
    if (!labels.length) return [];

    const labelsToIds = new Map(
      this.getTargetProfileChoices().map((profile) => [profile.label, profile.id]),
    );
    return labels
      .map((label) => labelsToIds.get(label))
      .filter((id): id is string => !!id);
  }
}
