import * as plugins from '../plugins.js';
import * as shared from './shared/index.js';
import * as appstate from '../appstate.js';
import * as interfaces from '../../ts_interfaces/index.js';
import { BackendExecutionEnvironment } from '../environments/backend-environment.js';
import {
  DeesElement,
  customElement,
  html,
  state,
  css,
  cssManager,
  type TemplateResult,
} from '@design.estate/dees-element';

// ============================================================================
// Data transformation helpers
// Maps backend data shapes to @serve.zone/catalog component interfaces
// ============================================================================

function formatBytes(bytes: number): string {
  if (!bytes || bytes === 0) return '0 B';
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  const k = 1024;
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  const value = bytes / Math.pow(k, i);
  return `${value.toFixed(1)} ${units[i]}`;
}

function parseImageString(image: string): { repository: string; tag: string } {
  const lastColon = image.lastIndexOf(':');
  const lastSlash = image.lastIndexOf('/');
  if (lastColon > lastSlash && lastColon > 0) {
    return {
      repository: image.substring(0, lastColon),
      tag: image.substring(lastColon + 1),
    };
  }
  return { repository: image, tag: 'latest' };
}

function mapStatus(status: string): 'running' | 'stopped' | 'starting' | 'error' {
  switch (status) {
    case 'running': return 'running';
    case 'starting': return 'starting';
    case 'failed': return 'error';
    case 'stopped':
    case 'stopping':
    default: return 'stopped';
  }
}

function toServiceDetail(service: interfaces.data.IService) {
  const parsed = parseImageString(service.image);
  return {
    name: service.name,
    status: mapStatus(service.status),
    image: service.image,
    port: service.port,
    domain: service.domain || null,
    containerId: service.containerID || '',
    created: service.createdAt ? new Date(service.createdAt).toLocaleString() : '-',
    updated: service.updatedAt ? new Date(service.updatedAt).toLocaleString() : '-',
    registry: service.useOneboxRegistry ? 'Onebox Registry' : (service.registry || 'Docker Hub'),
    repository: service.registryRepository || parsed.repository,
    tag: service.registryImageTag || parsed.tag,
  };
}

function toServiceStats(stats: interfaces.data.IContainerStats) {
  return {
    cpu: stats.cpuPercent,
    memory: formatBytes(stats.memoryUsed),
    memoryLimit: formatBytes(stats.memoryLimit),
    networkIn: formatBytes(stats.networkRx),
    networkOut: formatBytes(stats.networkTx),
  };
}

function parseLogs(logs: any): Array<{ timestamp: string; message: string; level?: string }> {
  if (Array.isArray(logs)) {
    return logs.map((entry: any) => {
      const ts = entry.timestamp
        ? (typeof entry.timestamp === 'number' ? new Date(entry.timestamp).toISOString() : String(entry.timestamp))
        : new Date().toISOString();
      const message = entry.message || String(entry);
      const level = entry.level || 'info';
      return { timestamp: ts, message, level };
    });
  }
  if (typeof logs === 'string' && logs.trim()) {
    return logs.split('\n').filter((line: string) => line.trim()).map((line: string) => {
      const match = line.match(/^(\d{4}-\d{2}-\d{2}T[\d:.]+Z?)\s+(.*)/);
      const timestamp = match ? match[1] : new Date().toISOString();
      const message = match ? match[2] : line;
      const msgLower = message.toLowerCase();
      const level = msgLower.includes('error') || msgLower.includes('fatal')
        ? 'error'
        : msgLower.includes('warn')
          ? 'warn'
          : 'info';
      return { timestamp, message, level };
    });
  }
  return [];
}

const defaultStats = { cpu: 0, memory: '0 B', memoryLimit: '0 B', networkIn: '0 B', networkOut: '0 B' };

@customElement('ob-view-services')
export class ObViewServices extends DeesElement {
  @state()
  accessor servicesState: appstate.IServicesState = {
    services: [],
    currentService: null,
    currentHostedAppLifecycle: null,
    currentServiceLogs: [],
    currentServiceStats: null,
    platformServices: [],
    currentPlatformService: null,
    currentPlatformServiceStats: null,
    currentPlatformServiceLogs: [],
  };

  @state()
  accessor backupsState: appstate.IBackupsState = {
    backups: [],
    schedules: [],
  };

  @state()
  accessor currentView: 'list' | 'create' | 'detail' | 'backups' | 'platform-detail' = 'list';

  @state()
  accessor selectedServiceName: string = '';

  @state()
  accessor selectedPlatformType: string = '';

  @state()
  accessor workspaceOpen: boolean = false;

  @state()
  accessor pendingTemplate: any = null;

  @state()
  accessor appStoreState: appstate.IAppStoreState = {
    apps: [],
    upgradeableServices: [],
    upgradeOperations: [],
  };

  constructor() {
    super();

    const servicesSub = appstate.servicesStatePart
      .select((s) => s)
      .subscribe((newState) => {
        this.servicesState = newState;
      });
    this.rxSubscriptions.push(servicesSub);

    const backupsSub = appstate.backupsStatePart
      .select((s) => s)
      .subscribe((newState) => {
        this.backupsState = newState;
      });
    this.rxSubscriptions.push(backupsSub);

    const appStoreSub = appstate.appStoreStatePart
      .select((s) => s)
      .subscribe((newState) => {
        this.appStoreState = newState;
      });
    this.rxSubscriptions.push(appStoreSub);
  }

  public static styles = [
    cssManager.defaultStyles,
    shared.viewHostCss,
    css`
      .page-actions {
        display: flex;
        justify-content: flex-end;
        margin-bottom: 16px;
      }

      .deploy-button {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        padding: 10px 20px;
        background: ${cssManager.bdTheme('#18181b', '#fafafa')};
        color: ${cssManager.bdTheme('#fafafa', '#18181b')};
        border: none;
        border-radius: 6px;
        font-size: 14px;
        font-weight: 500;
        cursor: pointer;
        transition: opacity 200ms ease;
      }

      .deploy-button:hover {
        opacity: 0.9;
      }

      .deploy-button svg {
        width: 16px;
        height: 16px;
      }

      :host(.workspace-mode) {
        max-width: none;
        padding: 0;
        height: 100%;
        display: flex;
        flex-direction: column;
      }

      :host(.workspace-mode) ob-sectionheading {
        display: none;
      }
    `,
  ];

  async connectedCallback() {
    super.connectedCallback();
    await Promise.all([
      appstate.servicesStatePart.dispatchAction(appstate.fetchServicesAction, null),
      appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServicesAction, null),
      appstate.appStoreStatePart.dispatchAction(appstate.fetchUpgradeableAppStoreServicesAction, null),
      appstate.appStoreStatePart.dispatchAction(appstate.fetchAppStoreUpgradeOperationsAction, null),
    ]);

    // If a platform service was selected from the dashboard, navigate to its detail
    const state = appstate.servicesStatePart.getState();
    if (state.currentPlatformService) {
      const type = state.currentPlatformService.type;
      appstate.servicesStatePart.setState({
        ...appstate.servicesStatePart.getState(),
        currentPlatformService: null,
      });
      this.navigateToPlatformDetail(type);
    }

  }

  public render(): TemplateResult {
    switch (this.currentView) {
      case 'create':
        return this.renderCreateView();
      case 'detail':
        return this.renderDetailView();
      case 'backups':
        return this.renderBackupsView();
      case 'platform-detail':
        return this.renderPlatformDetailView();
      default:
        return this.renderListView();
    }
  }

  private renderListView(): TemplateResult {
    const mappedServices = this.servicesState.services.map((s) => ({
      name: s.name,
      image: s.image,
      domain: s.domain || null,
      status: mapStatus(s.status),
    }));
    const displayStatus = (status: string) => {
      switch (status) {
        case 'running': return 'Running';
        case 'stopped': return 'Stopped';
        case 'starting': return 'Starting...';
        case 'stopping': return 'Stopping...';
        case 'failed': return 'Failed';
        case 'not-deployed': return 'Not Deployed';
        default: return status;
      }
    };
    // Split platform services into active (running or core) and inactive (not in use)
    const activePlatformServices = this.servicesState.platformServices.filter(
      (ps) => ps.status === 'running' || ps.status === 'starting' || ps.status === 'stopping' || ps.isCore,
    );
    const inactivePlatformServices = this.servicesState.platformServices.filter(
      (ps) => !ps.isCore && (ps.status === 'not-deployed' || ps.status === 'stopped' || ps.status === 'failed'),
    );
    const mappedActivePlatformServices = activePlatformServices.map((ps) => ({
      name: ps.displayName,
      status: displayStatus(ps.status),
      running: ps.status === 'running',
      type: ps.type,
    }));
    return html`
      <ob-sectionheading>Services</ob-sectionheading>
      <div class="page-actions">
        <button class="deploy-button" @click=${() => { this.currentView = 'create'; }}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <line x1="12" y1="5" x2="12" y2="19"></line>
            <line x1="5" y1="12" x2="19" y2="12"></line>
          </svg>
          Deploy Service
        </button>
      </div>
      <sz-services-list-view
        .services=${mappedServices}
        @service-click=${(e: CustomEvent) => {
          this.selectedServiceName = e.detail.name || e.detail.service?.name;
          appstate.servicesStatePart.dispatchAction(appstate.fetchServiceAction, {
            name: this.selectedServiceName,
          });
          appstate.servicesStatePart.dispatchAction(appstate.fetchServiceLogsAction, {
            name: this.selectedServiceName,
          });
          appstate.servicesStatePart.dispatchAction(appstate.fetchServiceStatsAction, {
            name: this.selectedServiceName,
          });
          this.currentView = 'detail';
        }}
        @service-action=${(e: CustomEvent) => this.handleServiceAction(e)}
      ></sz-services-list-view>
      <ob-sectionheading style="margin-top: 32px;">Platform Services</ob-sectionheading>
      <div style="max-width: 500px;">
        ${mappedActivePlatformServices.length > 0 ? html`
          <sz-platform-services-card
            .services=${mappedActivePlatformServices}
            @service-click=${(e: CustomEvent) => {
              const type = e.detail.type || this.servicesState.platformServices.find(
                (ps) => ps.displayName === e.detail.name,
              )?.type;
              if (type) {
                this.navigateToPlatformDetail(type);
              }
            }}
          ></sz-platform-services-card>
        ` : ''}
        ${inactivePlatformServices.length > 0 ? html`
          <div style="
            background: var(--ci-shade-1, #09090b);
            border: 1px solid var(--ci-shade-2, #27272a);
            border-radius: 8px;
            padding: 20px;
            margin-top: ${mappedActivePlatformServices.length > 0 ? '12px' : '0'};
            opacity: 0.5;
          ">
            <div style="font-size: 13px; color: var(--ci-shade-4, #71717a); margin-bottom: 12px;">Available — not in use</div>
            <div style="display: flex; flex-direction: column; gap: 12px;">
              ${inactivePlatformServices.map((ps) => html`
                <div
                  style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; cursor: pointer; transition: opacity 200ms ease;"
                  @click=${() => this.navigateToPlatformDetail(ps.type)}
                >
                  <div style="display: flex; align-items: center; gap: 10px;">
                    <div style="width: 8px; height: 8px; border-radius: 50%; background: var(--ci-shade-3, #3f3f46); flex-shrink: 0;"></div>
                    <span style="font-size: 14px; font-weight: 500; color: var(--ci-shade-4, #71717a);">${ps.displayName}</span>
                  </div>
                  <span style="font-size: 13px; color: var(--ci-shade-3, #3f3f46);">${displayStatus(ps.status)}</span>
                </div>
              `)}
            </div>
          </div>
        ` : ''}
      </div>
    `;
  }

  private async deployFromTemplate(template: any): Promise<void> {
    const name = template.id || template.name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
    const envVars: Record<string, string> = {};
    if (template.envVars) {
      for (const ev of template.envVars) {
        if (ev.key && ev.value) envVars[ev.key] = ev.value;
      }
    }
    const serviceConfig: interfaces.data.IServiceCreate = {
      name,
      image: template.image,
      port: template.port || 80,
      envVars,
      enableMongoDB: template.enableMongoDB || false,
      enableS3: template.enableS3 || false,
      enableClickHouse: template.enableClickHouse || false,
      enableRedis: template.enableRedis || false,
      enableMariaDB: template.enableMariaDB || false,
    };
    await appstate.servicesStatePart.dispatchAction(appstate.createServiceAction, {
      config: serviceConfig,
    });
    this.pendingTemplate = null;
    this.currentView = 'list';
  }

  private renderCreateView(): TemplateResult {
    // If we have a pending app template from the App Store, show a quick-deploy confirmation
    if (this.pendingTemplate) {
      const t = this.pendingTemplate;
      return html`
        <ob-sectionheading>Deploy ${t.name}</ob-sectionheading>
        <div style="max-width: 600px; margin: 0 auto;">
          <div style="background: var(--ci-shade-1, #09090b); border: 1px solid var(--ci-shade-2, #27272a); border-radius: 8px; padding: 24px; margin-bottom: 16px;">
            <h3 style="margin: 0 0 8px 0; font-size: 18px;">${t.name}</h3>
            <p style="margin: 0 0 16px 0; color: var(--ci-shade-5, #a1a1aa); font-size: 14px;">${t.description}</p>
            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 13px;">
              <div><span style="color: var(--ci-shade-5, #a1a1aa);">Image:</span> <strong>${t.image}</strong></div>
              <div><span style="color: var(--ci-shade-5, #a1a1aa);">Port:</span> <strong>${t.port}</strong></div>
              <div><span style="color: var(--ci-shade-5, #a1a1aa);">Service Name:</span> <strong>${t.id}</strong></div>
              <div><span style="color: var(--ci-shade-5, #a1a1aa);">Category:</span> <strong>${t.category}</strong></div>
            </div>
            ${t.enableMongoDB || t.enableS3 || t.enableClickHouse || t.enableRedis || t.enableMariaDB ? html`
              <div style="margin-top: 12px; font-size: 13px; color: var(--ci-shade-5, #a1a1aa);">
                Platform Services:
                ${t.enableMongoDB ? html`<span style="margin-right: 8px;">MongoDB</span>` : ''}
                ${t.enableS3 ? html`<span style="margin-right: 8px;">S3</span>` : ''}
                ${t.enableClickHouse ? html`<span style="margin-right: 8px;">ClickHouse</span>` : ''}
                ${t.enableRedis ? html`<span style="margin-right: 8px;">Redis</span>` : ''}
                ${t.enableMariaDB ? html`<span style="margin-right: 8px;">MariaDB</span>` : ''}
              </div>
            ` : ''}
          </div>
          <div style="display: flex; gap: 12px; justify-content: flex-end;">
            <button class="deploy-button" style="background: transparent; border: 1px solid var(--ci-shade-2, #27272a); color: inherit;" @click=${() => { this.pendingTemplate = null; this.currentView = 'list'; }}>Cancel</button>
            <button class="deploy-button" @click=${() => this.deployFromTemplate(t)}>Deploy ${t.name}</button>
          </div>
        </div>
      `;
    }

    return html`
      <ob-sectionheading>Create Service</ob-sectionheading>
      <sz-service-create-view
        .registries=${[]}
        @create-service=${async (e: CustomEvent) => {
          const formConfig = e.detail;
          const serviceConfig: interfaces.data.IServiceCreate = {
            name: formConfig.name,
            image: formConfig.image,
            port: formConfig.ports?.[0]?.containerPort
              ? parseInt(formConfig.ports[0].containerPort, 10)
              : 80,
            envVars: formConfig.envVars?.reduce(
              (acc: Record<string, string>, ev: { key: string; value: string }) => {
                if (ev.key) acc[ev.key] = ev.value;
                return acc;
              },
              {} as Record<string, string>,
            ),
            enableMongoDB: formConfig.enableMongoDB || false,
            enableS3: formConfig.enableS3 || false,
            enableClickHouse: formConfig.enableClickHouse || false,
            enableRedis: formConfig.enableRedis || false,
            enableMariaDB: formConfig.enableMariaDB || false,
          };
          await appstate.servicesStatePart.dispatchAction(appstate.createServiceAction, {
            config: serviceConfig,
          });
          this.currentView = 'list';
        }}
        @cancel=${() => {
          this.currentView = 'list';
        }}
      ></sz-service-create-view>
    `;
  }

  private renderDetailView(): TemplateResult {
    const service = this.servicesState.currentService;
    const transformedService = service ? toServiceDetail(service) : null;
    const transformedStats = this.servicesState.currentServiceStats
      ? toServiceStats(this.servicesState.currentServiceStats)
      : defaultStats;
    const transformedLogs = parseLogs(this.servicesState.currentServiceLogs);

    // Check if this service has an available upgrade
    const upgradeInfo = service
      ? this.appStoreState.upgradeableServices.find((u) => u.serviceName === service.name)
      : null;
    const upgradeOperation = service
      ? this.appStoreState.upgradeOperations.find((operation) => {
          return operation.serviceName === service.name && operation.status === 'running';
        })
      : null;
    const latestUpgradeOperation = service
      ? this.appStoreState.upgradeOperations.find((operation) => operation.serviceName === service.name)
      : null;

    return html`
      <ob-sectionheading>Service Details</ob-sectionheading>
      ${upgradeOperation ? this.renderUpgradeOperation(upgradeOperation) : ''}
      ${!upgradeOperation && latestUpgradeOperation?.status === 'failed'
        ? this.renderUpgradeOperation(latestUpgradeOperation)
        : ''}
      ${service && this.servicesState.currentHostedAppLifecycle
        ? this.renderHostedAppLifecycleCard(service, this.servicesState.currentHostedAppLifecycle)
        : ''}
      ${upgradeInfo ? html`
        <div style="
          background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(139, 92, 246, 0.1));
          border: 1px solid rgba(59, 130, 246, 0.3);
          border-radius: 8px;
          padding: 16px;
          margin-bottom: 16px;
          display: flex;
          justify-content: space-between;
          align-items: center;
        ">
          <div>
            <div style="font-size: 14px; font-weight: 600; color: var(--ci-shade-7, #e4e4e7);">
              Update available: v${upgradeInfo.currentVersion} &rarr; v${upgradeInfo.latestVersion}
            </div>
            <div style="font-size: 12px; color: var(--ci-shade-4, #71717a); margin-top: 4px;">
              ${upgradeInfo.hasMigration ? 'Migration script available' : 'Config-only upgrade'}
            </div>
          </div>
          <button
            class="deploy-button"
            style="padding: 8px 16px; font-size: 13px;"
            ?disabled=${Boolean(upgradeOperation)}
            @click=${async () => {
              await appstate.appStoreStatePart.dispatchAction(appstate.upgradeAppStoreServiceAction, {
                serviceName: upgradeInfo.serviceName,
                targetVersion: upgradeInfo.latestVersion,
              });
            }}
          >${upgradeOperation ? 'Upgrading...' : 'Upgrade'}</button>
        </div>
      ` : ''}
      <sz-service-detail-view
        .service=${transformedService}
        .logs=${transformedLogs}
        .stats=${transformedStats}
        @back=${() => {
          this.currentView = 'list';
        }}
        @service-action=${(e: CustomEvent) => this.handleServiceAction(e)}
        @request-workspace=${async (e: CustomEvent) => {
          const name = e.detail?.service?.name || this.selectedServiceName;
          const identity = appstate.loginStatePart.getState().identity;
          if (!name || !identity) return;
          try {
            const env = new BackendExecutionEnvironment(name, identity);
            await env.init();
            const detailView = this.shadowRoot?.querySelector('sz-service-detail-view') as any;
            if (detailView) {
              detailView.workspaceEnvironment = env;
            }
            this.workspaceOpen = true;
            this.classList.add('workspace-mode');
          } catch (err) {
            console.error('Failed to open workspace:', err);
          }
        }}
        @back=${() => {
          this.workspaceOpen = false;
          this.classList.remove('workspace-mode');
          this.currentView = 'list';
        }}
      ></sz-service-detail-view>
    `;
  }

  private renderUpgradeOperation(
    operationArg: interfaces.requests.IAppStoreUpgradeOperation,
  ): TemplateResult {
    const color = operationArg.status === 'failed' ? '#f87171' : '#60a5fa';
    return html`
      <div style="
        background: var(--ci-shade-1, #09090b);
        border: 1px solid ${color};
        border-radius: 8px;
        padding: 16px;
        margin-bottom: 16px;
      ">
        <div style="display: flex; justify-content: space-between; gap: 16px; align-items: flex-start;">
          <div>
            <div style="font-size: 14px; font-weight: 600; color: var(--ci-shade-7, #e4e4e7);">
              Upgrade ${operationArg.fromVersion} &rarr; ${operationArg.targetVersion}: ${operationArg.step}
            </div>
            <div style="font-size: 12px; color: var(--ci-shade-4, #71717a); margin-top: 4px;">
              ${operationArg.status === 'running' ? 'Operation is running in the background.' : operationArg.error || 'Operation finished.'}
            </div>
          </div>
          <span style="font-size: 12px; color: ${color}; text-transform: uppercase; letter-spacing: 0.04em;">
            ${operationArg.status}
          </span>
        </div>
        <div style="
          margin-top: 12px;
          padding: 10px 12px;
          background: var(--ci-shade-0, #030305);
          border-radius: 6px;
          color: var(--ci-shade-5, #a1a1aa);
          font-family: monospace;
          font-size: 12px;
          line-height: 1.5;
          max-height: 130px;
          overflow: auto;
          white-space: pre-wrap;
        ">${operationArg.progressLines.slice(-8).join('\n')}</div>
        ${operationArg.warnings.length > 0 ? html`
          <div style="margin-top: 10px; color: #fbbf24; font-size: 12px;">
            ${operationArg.warnings.join(' | ')}
          </div>
        ` : ''}
      </div>
    `;
  }

  private renderHostedAppLifecycleCard(
    serviceArg: interfaces.data.IService,
    lifecycleArg: interfaces.data.IHostedAppLifecycleState,
  ): TemplateResult | '' {
    const action = lifecycleArg.bootstrapAction;
    const upgrade = lifecycleArg.upgradeState;
    const showUpgrade = upgrade?.status === 'available' || upgrade?.status === 'running' || upgrade?.status === 'failed';
    if (!action && !showUpgrade) return '';

    return html`
      <div style="
        background: var(--ci-shade-1, #09090b);
        border: 1px solid ${action ? '#f59e0b' : '#60a5fa'};
        border-radius: 8px;
        padding: 16px;
        margin-bottom: 16px;
      ">
        <div style="display: flex; justify-content: space-between; gap: 16px; align-items: flex-start;">
          <div>
            <div style="font-size: 14px; font-weight: 600; color: var(--ci-shade-7, #e4e4e7);">
              Hosted App Lifecycle
            </div>
            <div style="font-size: 12px; color: var(--ci-shade-4, #71717a); margin-top: 4px;">
              Runtime reports from ${lifecycleArg.appName || serviceArg.name}${lifecycleArg.publicUrl ? html` at ${lifecycleArg.publicUrl}` : ''}.
            </div>
          </div>
          <span style="font-size: 12px; color: var(--ci-shade-4, #71717a); text-transform: uppercase; letter-spacing: 0.04em;">
            ${lifecycleArg.runtimeStatus || 'unknown'}
          </span>
        </div>

        ${action ? html`
          <div style="margin-top: 14px; padding: 12px; background: var(--ci-shade-0, #030305); border-radius: 6px;">
            <div style="font-size: 13px; font-weight: 600; color: var(--ci-shade-7, #e4e4e7); margin-bottom: 8px;">
              ${action.label} ${action.status === 'completed' ? '(completed)' : ''}
            </div>
            ${action.message ? html`<div style="font-size: 12px; color: var(--ci-shade-5, #a1a1aa); margin-bottom: 8px;">${action.message}</div>` : ''}
            ${action.url ? this.renderHostedAppValueRow('Setup URL', action.url) : ''}
            ${action.username ? this.renderHostedAppValueRow('Username', action.username) : ''}
            ${action.password ? this.renderHostedAppValueRow('Password', action.password) : ''}
            ${action.expiresAt ? html`
              <div style="font-size: 12px; color: var(--ci-shade-4, #71717a); margin-top: 8px;">
                Expires ${new Date(action.expiresAt).toLocaleString()}
              </div>
            ` : ''}
            <div style="font-size: 12px; color: #fbbf24; margin-top: 10px;">
              Mark done only after you saved the information or completed the app setup. This deletes the displayed bootstrap data from Onebox.
            </div>
            <button
              class="deploy-button"
              style="margin-top: 12px; padding: 8px 14px; font-size: 12px; background: transparent; border: 1px solid #f59e0b; color: #fbbf24;"
              @click=${() => this.handleDismissHostedAppBootstrap(serviceArg.name)}
            >Mark done and delete</button>
          </div>
        ` : ''}

        ${showUpgrade ? html`
          <div style="margin-top: ${action ? '12px' : '14px'}; padding: 12px; background: var(--ci-shade-0, #030305); border-radius: 6px;">
            <div style="font-size: 13px; font-weight: 600; color: var(--ci-shade-7, #e4e4e7);">
              Managed upgrade: ${upgrade!.status}
            </div>
            <div style="font-size: 12px; color: var(--ci-shade-5, #a1a1aa); margin-top: 4px;">
              ${upgrade!.currentVersion || '-'} ${upgrade!.targetVersion || upgrade!.latestVersion ? html`&rarr; ${upgrade!.targetVersion || upgrade!.latestVersion}` : ''}
            </div>
            ${upgrade!.error ? html`<div style="font-size: 12px; color: #f87171; margin-top: 6px;">${upgrade!.error}</div>` : ''}
          </div>
        ` : ''}
      </div>
    `;
  }

  private renderHostedAppValueRow(labelArg: string, valueArg: string): TemplateResult {
    return html`
      <div style="display: grid; grid-template-columns: 110px 1fr auto; gap: 8px; align-items: center; margin-top: 6px;">
        <span style="font-size: 12px; color: var(--ci-shade-4, #71717a);">${labelArg}</span>
        <code style="font-size: 12px; color: var(--ci-shade-7, #e4e4e7); overflow-wrap: anywhere;">${valueArg}</code>
        <button
          class="deploy-button"
          style="padding: 4px 8px; font-size: 11px; background: transparent; border: 1px solid var(--ci-shade-3, #3f3f46); color: var(--ci-shade-6, #d4d4d8);"
          @click=${() => this.copyHostedAppValue(valueArg)}
        >Copy</button>
      </div>
    `;
  }

  private async copyHostedAppValue(valueArg: string): Promise<void> {
    await navigator.clipboard?.writeText(valueArg);
  }

  private async handleDismissHostedAppBootstrap(serviceNameArg: string): Promise<void> {
    if (!confirm('Delete the hosted app bootstrap information from Onebox?')) return;
    await appstate.servicesStatePart.dispatchAction(appstate.dismissHostedAppBootstrapAction, {
      name: serviceNameArg,
    });
  }

  private renderBackupsView(): TemplateResult {
    return html`
      <ob-sectionheading>Backups</ob-sectionheading>
      <sz-services-backups-view
        .schedules=${this.backupsState.schedules}
        .backups=${this.backupsState.backups}
        @create-schedule=${(e: CustomEvent) => {
          appstate.backupsStatePart.dispatchAction(appstate.createScheduleAction, {
            config: e.detail,
          });
        }}
        @run-now=${(e: CustomEvent) => {
          appstate.backupsStatePart.dispatchAction(appstate.triggerScheduleAction, {
            scheduleId: e.detail.scheduleId,
          });
        }}
        @delete-backup=${(e: CustomEvent) => {
          appstate.backupsStatePart.dispatchAction(appstate.deleteBackupAction, {
            backupId: e.detail.backupId,
          });
        }}
      ></sz-services-backups-view>
    `;
  }

  private navigateToPlatformDetail(type: string): void {
    // Reset to list first to force fresh DOM for dees-chart-log
    this.currentView = 'list';
    this.selectedPlatformType = type;

    // Clear previous stats/logs before fetching new ones
    appstate.servicesStatePart.setState({
      ...appstate.servicesStatePart.getState(),
      currentPlatformServiceStats: null,
      currentPlatformServiceLogs: [],
    });

    // Fetch stats and logs for this platform service
    const serviceType = type as interfaces.data.TPlatformServiceType;
    appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceStatsAction, { serviceType });
    appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceLogsAction, { serviceType });

    // Switch to detail view on next microtask (ensures fresh DOM)
    requestAnimationFrame(() => {
      this.currentView = 'platform-detail';
    });
  }

  private renderPlatformDetailView(): TemplateResult {
    const platformService = this.servicesState.platformServices.find(
      (ps) => ps.type === this.selectedPlatformType,
    );
    const stats = this.servicesState.currentPlatformServiceStats;
    const metrics = {
      cpu: stats ? Math.round(stats.cpuPercent) : 0,
      memory: stats ? Math.round(stats.memoryPercent) : 0,
      storage: 0,
      connections: undefined as number | undefined,
    };

    // Real service info per platform type
    const serviceInfo: Record<string, { host: string; port: number; version: string; config: Record<string, any> }> = {
      mongodb: { host: 'onebox-mongodb', port: 27017, version: '4.4', config: { engine: 'WiredTiger', authEnabled: true } },
      minio: { host: 'onebox-minio', port: 9000, version: 'latest', config: { consolePort: 9001, region: 'us-east-1' } },
      clickhouse: { host: 'onebox-clickhouse', port: 8123, version: 'latest', config: { nativePort: 9000, httpPort: 8123 } },
      smartproxy: { host: 'onebox-smartproxy', port: 80, version: 'latest', config: { httpsPort: 443, adminApi: 2019 } },
      mariadb: { host: 'onebox-mariadb', port: 3306, version: '11', config: { engine: 'InnoDB', authEnabled: true } },
      redis: { host: 'onebox-redis', port: 6379, version: '7-alpine', config: { appendonly: true, maxDatabases: 16 } },
    };
    const info = platformService
      ? serviceInfo[platformService.type] || { host: 'unknown', port: 0, version: '', config: {} }
      : { host: '', port: 0, version: '', config: {} };

    // Map backend status to catalog-compatible status
    const mapPlatformStatus = (status: string): 'running' | 'stopped' | 'error' => {
      switch (status) {
        case 'running': return 'running';
        case 'failed': return 'error';
        case 'starting':
        case 'stopping':
        case 'stopped':
        case 'not-deployed':
        default: return 'stopped';
      }
    };

    return html`
      <ob-sectionheading>Platform Service</ob-sectionheading>
      <div class="page-actions" style="justify-content: flex-start;">
        <button class="deploy-button" style="background: transparent; border: 1px solid var(--ci-shade-2, #27272a); color: inherit;" @click=${() => { this.currentView = 'list'; }}>
          &larr; Back to Services
        </button>
      </div>
      <sz-platform-service-detail-view
        .service=${platformService
          ? {
              id: platformService.type,
              name: platformService.displayName,
              type: platformService.type,
              status: mapPlatformStatus(platformService.status),
              version: info.version,
              host: info.host,
              port: info.port,
              config: info.config,
              metrics,
            }
          : null}
        .logs=${this.servicesState.currentPlatformServiceLogs.map((log) => ({
          timestamp: new Date(log.timestamp).toISOString(),
          level: log.level,
          message: log.message,
        }))}
        @back=${() => {
          this.currentView = 'list';
        }}
        @start=${async () => {
          await appstate.servicesStatePart.dispatchAction(appstate.startPlatformServiceAction, {
            serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
          });
          // Refresh stats after starting
          appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceStatsAction, {
            serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
          });
        }}
        @stop=${async () => {
          await appstate.servicesStatePart.dispatchAction(appstate.stopPlatformServiceAction, {
            serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
          });
        }}
        @restart=${async () => {
          await appstate.servicesStatePart.dispatchAction(appstate.stopPlatformServiceAction, {
            serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
          });
          await appstate.servicesStatePart.dispatchAction(appstate.startPlatformServiceAction, {
            serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
          });
          appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServiceStatsAction, {
            serviceType: this.selectedPlatformType as interfaces.data.TPlatformServiceType,
          });
        }}
      ></sz-platform-service-detail-view>
    `;
  }

  private async handleServiceAction(e: CustomEvent) {
    const action = e.detail.action;
    const name = e.detail.service?.name || e.detail.name || this.selectedServiceName;
    switch (action) {
      case 'start':
        await appstate.servicesStatePart.dispatchAction(appstate.startServiceAction, { name });
        break;
      case 'stop':
        await appstate.servicesStatePart.dispatchAction(appstate.stopServiceAction, { name });
        break;
      case 'restart':
        await appstate.servicesStatePart.dispatchAction(appstate.restartServiceAction, { name });
        break;
      case 'delete':
        await appstate.servicesStatePart.dispatchAction(appstate.deleteServiceAction, { name });
        this.currentView = 'list';
        break;
    }
  }
}
