import React from 'react';

import { useAppTranslation } from '@features/localization/renderer';
import { formatProviderBackendLabel } from '@renderer/utils/providerBackendIdentity';
import { getTeamProviderLabel as getCatalogTeamProviderLabel } from '@renderer/utils/teamModelCatalog';
import {
  isOpenCodeWindowsAccessDeniedDiagnostic,
  isOpenCodeWindowsNodeModulesSymlinkPermissionDiagnostic,
  OPENCODE_WINDOWS_ACCESS_DENIED_MESSAGE,
  OPENCODE_WINDOWS_NODE_MODULES_SYMLINK_PERMISSION_MESSAGE,
} from '@shared/utils/openCodeWindowsAccessDenied';
import { AlertTriangle, Check, CheckCircle2, Copy, Loader2, SlidersHorizontal } from 'lucide-react';

import type {
  CliProviderStatus,
  TeamProviderId,
  TeamProvisioningSupportDiagnostic,
} from '@shared/types';

type TeamTranslator = ReturnType<typeof useAppTranslation>['t'];

export type ProvisioningProviderCheckStatus = 'pending' | 'checking' | 'ready' | 'notes' | 'failed';
export type ProvisioningPrepareState = 'idle' | 'loading' | 'ready' | 'failed';

export interface ProvisioningProviderCheck {
  providerId: TeamProviderId;
  status: ProvisioningProviderCheckStatus;
  backendSummary?: string | null;
  details: string[];
  supportDiagnostics?: TeamProvisioningSupportDiagnostic[];
}

export function getProvisioningProviderLabel(providerId: TeamProviderId): string {
  return getCatalogTeamProviderLabel(providerId) ?? 'Anthropic';
}

export function createInitialProviderChecks(
  providerIds: TeamProviderId[]
): ProvisioningProviderCheck[] {
  return providerIds.map((providerId) => ({
    providerId,
    status: 'pending',
    backendSummary: null,
    details: [],
  }));
}

export function getProvisioningProviderBackendSummary(
  provider:
    | Pick<
        CliProviderStatus,
        'providerId' | 'selectedBackendId' | 'resolvedBackendId' | 'availableBackends' | 'backend'
      >
    | null
    | undefined
): string | null {
  if (!provider) {
    return null;
  }

  const options = provider.availableBackends ?? [];
  const optionById = new Map(options.map((option) => [option.id, option.label]));
  const effectiveBackendId = provider.resolvedBackendId ?? provider.selectedBackendId;
  const effectiveOption = options.find((option) => option.id === effectiveBackendId) ?? null;
  const inferredProviderId: TeamProviderId | undefined =
    provider.providerId === 'anthropic' ||
    provider.providerId === 'codex' ||
    provider.providerId === 'gemini' ||
    provider.providerId === 'opencode'
      ? provider.providerId
      : effectiveBackendId === 'codex-native' ||
          options.some((option) => option.id === 'codex-native')
        ? 'codex'
        : undefined;
  const normalizedLabel =
    formatProviderBackendLabel(inferredProviderId, effectiveBackendId ?? undefined) ?? null;

  const baseSummary = effectiveBackendId
    ? (normalizedLabel ??
      optionById.get(effectiveBackendId) ??
      provider.backend?.label ??
      effectiveBackendId)
    : (provider.backend?.label ?? null);

  if (!baseSummary) {
    return null;
  }

  const suffixes: string[] = [];
  if (effectiveOption?.audience === 'internal') {
    suffixes.push('internal');
  }
  if (effectiveOption?.state && effectiveOption.state !== 'ready') {
    switch (effectiveOption.state) {
      case 'locked':
        suffixes.push('locked');
        break;
      case 'disabled':
        suffixes.push('disabled');
        break;
      case 'authentication-required':
        suffixes.push('auth required');
        break;
      case 'runtime-missing':
        suffixes.push('runtime missing');
        break;
      case 'degraded':
        if (inferredProviderId !== 'codex') {
          suffixes.push('degraded');
        }
        break;
      default:
        break;
    }
  }

  return suffixes.length > 0 ? `${baseSummary} - ${suffixes.join(', ')}` : baseSummary;
}

export function updateProviderCheck(
  checks: ProvisioningProviderCheck[],
  providerId: TeamProviderId,
  patch: Partial<ProvisioningProviderCheck>
): ProvisioningProviderCheck[] {
  return checks.map((check) =>
    check.providerId === providerId
      ? {
          ...check,
          ...patch,
        }
      : check
  );
}

export function failIncompleteProviderChecks(
  checks: ProvisioningProviderCheck[],
  detail: string
): ProvisioningProviderCheck[] {
  return checks.map((check) =>
    check.status === 'ready' || check.status === 'notes' || check.status === 'failed'
      ? check
      : {
          ...check,
          status: 'failed',
          details: check.details.length > 0 ? check.details : [detail],
        }
  );
}

export function getProvisioningProviderProgressMessage(
  providerIds: readonly TeamProviderId[],
  totalProviderCount: number,
  t?: TeamTranslator
): string {
  if (providerIds.length === 0 || providerIds.length === totalProviderCount) {
    return t
      ? t('provisioning.providerStatus.progress.checkingSelectedProviders')
      : 'Checking selected providers in parallel...';
  }

  if (providerIds.length === 1) {
    const provider = getProvisioningProviderLabel(providerIds[0]);
    return t
      ? t('provisioning.providerStatus.progress.checkingProvider', { provider })
      : `Checking ${provider} provider...`;
  }

  const providers = providerIds.map(getProvisioningProviderLabel).join(', ');
  return t
    ? t('provisioning.providerStatus.progress.checkingProviders', { providers })
    : `Checking ${providers} providers...`;
}

type ProvisioningDetailSummary =
  | 'CLI binary missing'
  | 'OpenCode runtime missing'
  | 'OpenCode Windows access blocked'
  | 'OpenCode runtime check returned no output'
  | 'OpenCode app MCP unreachable'
  | 'Working directory missing'
  | 'CLI binary could not be started'
  | 'CLI preflight did not complete'
  | 'Authentication required'
  | 'Runtime provider is not configured'
  | 'CLI preflight failed'
  | 'Selected model compatible'
  | 'Selected model compatibility pending'
  | 'Selected model available'
  | 'Selected model verified'
  | 'Selected model unavailable'
  | 'Selected model verification timed out'
  | 'Selected model check failed'
  | 'Selected model verification deferred'
  | 'Selected model ping not confirmed'
  | 'Ready with notes'
  | 'Needs attention';

function isSelectedModelDetail(lower: string): boolean {
  return lower.includes('selected model');
}

function isOpenCodeBridgeNoOutputDiagnostic(value: string | null | undefined): boolean {
  const lower = value?.trim().toLowerCase() ?? '';
  return (
    lower.includes('opencode runtime check returned no output') ||
    lower.includes('bridge stdout was empty') ||
    lower.includes('opencode_bridge_contract_violation') ||
    (lower.includes('opencode readiness bridge failed') && lower.includes('contract_violation'))
  );
}

function isFormattedModelDetail(lower: string): boolean {
  return (
    lower.includes(' - checking...') ||
    lower.includes(' - verified') ||
    lower.includes(' - available for launch') ||
    lower.includes(' - compatible for launch') ||
    lower.includes(' - compatible, deep verification pending') ||
    lower.includes(' - unavailable') ||
    lower.includes(' - check failed') ||
    lower.includes(' - verification deferred') ||
    lower.includes(' - ping not confirmed')
  );
}

function isModelDetail(lower: string): boolean {
  return isSelectedModelDetail(lower) || isFormattedModelDetail(lower);
}

function isInternalProvisioningDetail(detail: string): boolean {
  const normalized = detail.trim().toLowerCase();
  return normalized === 'opencode_app_mcp_tool_proof_persisted_cache_hit';
}

function getPublicProvisioningDetails(details: string[]): string[] {
  return details.filter((detail) => !isInternalProvisioningDetail(detail));
}

function getStatusLabel(status: ProvisioningProviderCheckStatus): string {
  switch (status) {
    case 'checking':
      return 'checking...';
    case 'ready':
      return 'OK';
    case 'notes':
      return 'OK (notes)';
    case 'failed':
      return 'ERR';
    case 'pending':
    default:
      return 'waiting';
  }
}

function getLocalizedStatusLabel(
  status: ProvisioningProviderCheckStatus,
  t: TeamTranslator
): string {
  switch (status) {
    case 'checking':
      return t('provisioning.providerStatus.status.checking');
    case 'ready':
      return t('provisioning.providerStatus.status.ready');
    case 'notes':
      return t('provisioning.providerStatus.status.notes');
    case 'failed':
      return t('provisioning.providerStatus.status.failed');
    case 'pending':
    default:
      return t('provisioning.providerStatus.status.pending');
  }
}

function summarizeDetail(
  detail: string,
  status: ProvisioningProviderCheckStatus,
  providerId?: TeamProviderId
): ProvisioningDetailSummary | null {
  const lower = detail.toLowerCase();

  if (providerId === 'opencode' && isOpenCodeWindowsAccessDeniedDiagnostic(detail)) {
    return 'OpenCode Windows access blocked';
  }
  if (providerId === 'opencode' && isOpenCodeBridgeNoOutputDiagnostic(detail)) {
    return 'OpenCode runtime check returned no output';
  }
  if (lower.includes('spawn ') && lower.includes(' enoent')) {
    return 'CLI binary missing';
  }
  if (lower.includes('opencode runtime binary is not installed')) {
    return 'OpenCode runtime missing';
  }
  if (
    lower.includes('opencode app mcp is unreachable') ||
    (lower.includes('unable to connect') &&
      (lower.includes('/experimental/tool') || lower.includes('mcp_unavailable')))
  ) {
    return 'OpenCode app MCP unreachable';
  }
  if (lower.includes('working directory does not exist:')) {
    return 'Working directory missing';
  }
  if (
    lower.includes('eacces') ||
    lower.includes('enoexec') ||
    lower.includes('bad cpu type in executable') ||
    lower.includes('image not found')
  ) {
    return 'CLI binary could not be started';
  }
  if (lower.includes('preflight check for `') && lower.includes('-p` did not complete')) {
    return 'CLI preflight did not complete';
  }
  if (lower.includes('not authenticated') || lower.includes('not logged in')) {
    return 'Authentication required';
  }
  if (lower.includes('provider is not configured for runtime use')) {
    return 'Runtime provider is not configured';
  }
  if (lower.includes('claude cli binary failed to start')) {
    return 'CLI binary could not be started';
  }
  if (lower.includes('claude cli preflight check failed')) {
    return 'CLI preflight failed';
  }
  if (isModelDetail(lower) && lower.includes('compatible, deep verification pending')) {
    return 'Selected model compatibility pending';
  }
  if (isModelDetail(lower) && lower.includes('compatible for launch')) {
    return 'Selected model compatible';
  }
  if (isSelectedModelDetail(lower) && lower.includes('verified for launch')) {
    return 'Selected model verified';
  }
  if (isSelectedModelDetail(lower) && lower.includes('available for launch')) {
    return 'Selected model available';
  }
  if (isSelectedModelDetail(lower) && lower.includes('is unavailable')) {
    return 'Selected model unavailable';
  }
  if (
    isSelectedModelDetail(lower) &&
    lower.includes('could not be verified') &&
    lower.includes('timed out')
  ) {
    return 'Selected model verification timed out';
  }
  if (isSelectedModelDetail(lower) && lower.includes('could not be verified')) {
    return 'Selected model check failed';
  }
  if (isSelectedModelDetail(lower) && lower.includes('verification deferred')) {
    return 'Selected model verification deferred';
  }
  if (lower.includes(' - verified')) {
    return 'Selected model verified';
  }
  if (lower.includes(' - available for launch')) {
    return 'Selected model available';
  }
  if (lower.includes(' - unavailable -')) {
    return 'Selected model unavailable';
  }
  if (lower.includes(' - check failed') && lower.includes('timed out')) {
    return 'Selected model verification timed out';
  }
  if (lower.includes(' - check failed -')) {
    return 'Selected model check failed';
  }
  if (lower.includes(' - verification deferred')) {
    return 'Selected model verification deferred';
  }
  if (lower.includes(' - ping not confirmed')) {
    return 'Selected model ping not confirmed';
  }

  if (status === 'notes') {
    return 'Ready with notes';
  }
  if (status === 'failed') {
    return 'Needs attention';
  }
  return null;
}

function localizeProvisioningDetailSummary(
  summary: ProvisioningDetailSummary,
  t: TeamTranslator
): string {
  switch (summary) {
    case 'CLI binary missing':
      return t('provisioning.providerStatus.detailSummary.cliBinaryMissing');
    case 'OpenCode runtime missing':
      return t('provisioning.providerStatus.detailSummary.openCodeRuntimeMissing');
    case 'OpenCode Windows access blocked':
      return t('provisioning.providerStatus.detailSummary.openCodeWindowsAccessBlocked');
    case 'OpenCode runtime check returned no output':
      return t('provisioning.providerStatus.detailSummary.openCodeNoOutput');
    case 'OpenCode app MCP unreachable':
      return t('provisioning.providerStatus.detailSummary.openCodeMcpUnreachable');
    case 'Working directory missing':
      return t('provisioning.providerStatus.detailSummary.workingDirectoryMissing');
    case 'CLI binary could not be started':
      return t('provisioning.providerStatus.detailSummary.cliBinaryCouldNotStart');
    case 'CLI preflight did not complete':
      return t('provisioning.providerStatus.detailSummary.cliPreflightIncomplete');
    case 'Authentication required':
      return t('provisioning.providerStatus.detailSummary.authenticationRequired');
    case 'Runtime provider is not configured':
      return t('provisioning.providerStatus.detailSummary.runtimeProviderNotConfigured');
    case 'CLI preflight failed':
      return t('provisioning.providerStatus.detailSummary.cliPreflightFailed');
    case 'Selected model compatible':
      return t('provisioning.providerStatus.detailSummary.selectedModelCompatible');
    case 'Selected model compatibility pending':
      return t('provisioning.providerStatus.detailSummary.selectedModelCompatibilityPending');
    case 'Selected model available':
      return t('provisioning.providerStatus.detailSummary.selectedModelAvailable');
    case 'Selected model verified':
      return t('provisioning.providerStatus.detailSummary.selectedModelVerified');
    case 'Selected model unavailable':
      return t('provisioning.providerStatus.detailSummary.selectedModelUnavailable');
    case 'Selected model verification timed out':
      return t('provisioning.providerStatus.detailSummary.selectedModelTimedOut');
    case 'Selected model check failed':
      return t('provisioning.providerStatus.detailSummary.selectedModelCheckFailed');
    case 'Selected model verification deferred':
      return t('provisioning.providerStatus.detailSummary.selectedModelDeferred');
    case 'Selected model ping not confirmed':
      return t('provisioning.providerStatus.detailSummary.selectedModelPingNotConfirmed');
    case 'Ready with notes':
      return t('provisioning.providerStatus.detailSummary.readyWithNotes');
    case 'Needs attention':
      return t('provisioning.providerStatus.detailSummary.needsAttention');
  }
}

function getModelDetailSummary(details: string[], t?: TeamTranslator): string | null {
  let compatibilityPendingCount = 0;
  let compatibleCount = 0;
  let availableCount = 0;
  let verifiedCount = 0;
  let unavailableCount = 0;
  let timedOutCount = 0;
  let checkFailedCount = 0;
  let deferredCount = 0;
  let pingNotConfirmedCount = 0;
  let checkingCount = 0;

  for (const detail of details) {
    const lower = detail.toLowerCase();
    if (!isModelDetail(lower)) {
      continue;
    }
    if (lower.includes('compatible, deep verification pending')) {
      compatibilityPendingCount += 1;
      continue;
    }
    if (lower.includes('compatible for launch')) {
      compatibleCount += 1;
      continue;
    }
    if (
      lower.includes(' - available for launch') ||
      (isSelectedModelDetail(lower) && lower.includes('is available for launch'))
    ) {
      availableCount += 1;
      continue;
    }
    if (
      lower.includes(' - verified') ||
      (isSelectedModelDetail(lower) && lower.includes('verified for launch'))
    ) {
      verifiedCount += 1;
      continue;
    }
    if (
      lower.includes(' - unavailable -') ||
      (isSelectedModelDetail(lower) && lower.includes('is unavailable'))
    ) {
      unavailableCount += 1;
      continue;
    }
    if (
      lower.includes('timed out') &&
      (lower.includes('check failed') ||
        (isSelectedModelDetail(lower) && lower.includes('could not be verified')))
    ) {
      timedOutCount += 1;
      continue;
    }
    if (
      lower.includes(' - check failed -') ||
      (isSelectedModelDetail(lower) && lower.includes('could not be verified'))
    ) {
      checkFailedCount += 1;
      continue;
    }
    if (
      lower.includes(' - verification deferred') ||
      (isSelectedModelDetail(lower) && lower.includes('verification deferred'))
    ) {
      deferredCount += 1;
      continue;
    }
    if (lower.includes(' - ping not confirmed')) {
      pingNotConfirmedCount += 1;
      continue;
    }
    if (lower.includes(' - checking...')) {
      checkingCount += 1;
    }
  }

  const parts: string[] = [];
  if (unavailableCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.unavailable', { count: unavailableCount })
        : `${unavailableCount} model${unavailableCount === 1 ? '' : 's'} unavailable`
    );
  }
  if (checkFailedCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.checkFailed', { count: checkFailedCount })
        : `${checkFailedCount} model${checkFailedCount === 1 ? '' : 's'} check failed`
    );
  }
  if (timedOutCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.timedOut', { count: timedOutCount })
        : `${timedOutCount} model${timedOutCount === 1 ? '' : 's'} timed out`
    );
  }
  if (deferredCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.deferred', { count: deferredCount })
        : `${deferredCount} verification deferred`
    );
  }
  if (pingNotConfirmedCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.pingNotConfirmed', {
            count: pingNotConfirmedCount,
          })
        : `${pingNotConfirmedCount} ping not confirmed`
    );
  }
  if (compatibilityPendingCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.compatibilityPending', {
            count: compatibilityPendingCount,
          })
        : `${compatibilityPendingCount} compatible, deep verification pending`
    );
  }
  if (compatibleCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.compatible', { count: compatibleCount })
        : `${compatibleCount} compatible`
    );
  }
  if (checkingCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.checking', { count: checkingCount })
        : `${checkingCount} checking`
    );
  }
  if (availableCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.available', { count: availableCount })
        : `${availableCount} available`
    );
  }
  if (verifiedCount > 0) {
    parts.push(
      t
        ? t('provisioning.providerStatus.modelParts.verified', { count: verifiedCount })
        : `${verifiedCount} verified`
    );
  }

  return parts.length > 0
    ? t
      ? t('provisioning.providerStatus.modelChecksSummary', { details: parts.join(', ') })
      : `Selected model checks - ${parts.join(', ')}`
    : null;
}

function hasCompatibilityPendingDetails(checks: ProvisioningProviderCheck[]): boolean {
  return checks.some((check) =>
    check.details.some((detail) =>
      detail.toLowerCase().includes('compatible, deep verification pending')
    )
  );
}

function getDisplayStatusText(check: ProvisioningProviderCheck, t?: TeamTranslator): string {
  const publicDetails = getPublicProvisioningDetails(check.details);
  const modelSummary = getModelDetailSummary(publicDetails, t);
  if (modelSummary) {
    return modelSummary;
  }

  const summarizedDetails = publicDetails
    .map((detail) => summarizeDetail(detail, check.status, check.providerId))
    .filter((detail): detail is ProvisioningDetailSummary => Boolean(detail));

  const summary =
    check.status === 'failed'
      ? (summarizedDetails.find(
          (detail) =>
            detail === 'OpenCode Windows access blocked' ||
            detail === 'OpenCode runtime check returned no output' ||
            detail === 'OpenCode app MCP unreachable' ||
            detail === 'OpenCode runtime missing' ||
            detail === 'Selected model unavailable' ||
            detail === 'Selected model check failed' ||
            detail === 'Authentication required' ||
            detail === 'CLI preflight failed' ||
            detail === 'CLI binary could not be started'
        ) ??
        summarizedDetails[0] ??
        null)
      : (summarizedDetails[0] ?? null);
  if (summary) {
    return t ? localizeProvisioningDetailSummary(summary, t) : summary;
  }
  return t ? getLocalizedStatusLabel(check.status, t) : getStatusLabel(check.status);
}

function getDetailTone(
  detail: string,
  status: ProvisioningProviderCheckStatus,
  providerId?: TeamProviderId
): 'success' | 'failure' | 'checking' | 'neutral' {
  const summary = summarizeDetail(detail, status, providerId);
  if (
    summary === 'Selected model verified' ||
    summary === 'Selected model available' ||
    summary === 'Selected model compatible'
  ) {
    return 'success';
  }
  if (summary === 'Selected model verification timed out') {
    return 'neutral';
  }
  if (summary === 'Selected model ping not confirmed') {
    return 'neutral';
  }
  if (
    summary === 'Selected model unavailable' ||
    summary === 'Selected model check failed' ||
    summary === 'CLI binary missing' ||
    summary === 'OpenCode runtime missing' ||
    summary === 'OpenCode Windows access blocked' ||
    summary === 'OpenCode runtime check returned no output' ||
    summary === 'OpenCode app MCP unreachable' ||
    summary === 'Working directory missing' ||
    summary === 'CLI binary could not be started' ||
    summary === 'CLI preflight did not complete' ||
    summary === 'Authentication required' ||
    summary === 'Runtime provider is not configured' ||
    summary === 'CLI preflight failed' ||
    summary === 'Needs attention'
  ) {
    return 'failure';
  }
  if (detail.toLowerCase().includes(' - checking...')) {
    return 'checking';
  }
  return 'neutral';
}

function getDetailColorClass(
  detail: string,
  status: ProvisioningProviderCheckStatus,
  providerId?: TeamProviderId
): string {
  switch (getDetailTone(detail, status, providerId)) {
    case 'success':
      return 'text-emerald-400';
    case 'failure':
      return 'text-red-300';
    case 'checking':
      return 'text-[var(--color-text-secondary)]';
    case 'neutral':
    default:
      return 'text-[var(--color-text-muted)]';
  }
}

export function getPrimaryProvisioningFailureDetail(
  checks: ProvisioningProviderCheck[]
): string | null {
  for (const check of checks) {
    if (check.status !== 'failed') {
      continue;
    }

    const publicDetails = getPublicProvisioningDetails(check.details);
    const unavailableDetail = publicDetails.find((detail) =>
      detail.toLowerCase().includes('selected model') &&
      detail.toLowerCase().includes('is unavailable')
        ? true
        : detail.toLowerCase().includes(' - unavailable -')
    );
    if (unavailableDetail) {
      return unavailableDetail;
    }
  }

  for (const check of checks) {
    if (check.status !== 'failed') {
      continue;
    }

    const publicDetails = getPublicProvisioningDetails(check.details);
    const preferredFailure = publicDetails.find(
      (detail) => getDetailTone(detail, check.status, check.providerId) === 'failure'
    );
    if (preferredFailure) {
      return preferredFailure;
    }

    const nonSuccessDetail = publicDetails.find(
      (detail) => getDetailTone(detail, check.status, check.providerId) !== 'success'
    );
    if (nonSuccessDetail) {
      return nonSuccessDetail;
    }

    if (publicDetails.length > 0) {
      return publicDetails[0];
    }
  }

  return null;
}

export function deriveEffectiveProvisioningPrepareState(params: {
  state: ProvisioningPrepareState;
  message: string | null;
  warnings: string[];
  checks: ProvisioningProviderCheck[];
  t?: TeamTranslator;
}): { state: ProvisioningPrepareState; message: string | null } {
  if (params.state !== 'loading') {
    return {
      state: params.state,
      message: params.message,
    };
  }

  if (params.checks.length === 0) {
    return {
      state: params.state,
      message: params.message,
    };
  }

  const hasPendingChecks = params.checks.some(
    (check) => check.status === 'pending' || check.status === 'checking'
  );
  if (hasPendingChecks) {
    if (hasCompatibilityPendingDetails(params.checks)) {
      return {
        state: params.state,
        message:
          params.t?.('provisioning.providerStatus.deepVerificationPending') ??
          'Deep verification is still running. OpenCode free models may take around 20 seconds.',
      };
    }
    return {
      state: params.state,
      message: params.message,
    };
  }

  if (params.checks.some((check) => check.status === 'failed')) {
    return {
      state: 'failed',
      message:
        getPrimaryProvisioningFailureDetail(params.checks) ??
        params.message ??
        params.t?.('create.prepare.someProvidersNeedAttention') ??
        'Some selected providers need attention.',
    };
  }

  const hasNotes =
    params.warnings.length > 0 || params.checks.some((check) => check.status === 'notes');

  return {
    state: 'ready',
    message: hasNotes
      ? (params.t?.('create.prepare.readyWithNotes') ??
        'All selected providers are ready, with notes.')
      : (params.t?.('create.prepare.ready') ?? 'All selected providers are ready.'),
  };
}

export function shouldHideProvisioningProviderStatusList(
  checks: ProvisioningProviderCheck[],
  message: string | null | undefined
): boolean {
  const normalizedMessage = (message ?? '').trim().toLowerCase();
  if (!normalizedMessage || checks.length === 0) {
    return false;
  }

  return checks.every((check) => {
    if (check.status !== 'failed') {
      return false;
    }

    const summary = getDisplayStatusText(check).toLowerCase();
    const visibleDetails = check.details.filter(
      (detail) => detail.trim().toLowerCase() !== normalizedMessage
    );

    return summary === 'working directory missing' && visibleDetails.length === 0;
  });
}

function getStatusColor(status: ProvisioningProviderCheckStatus): string {
  switch (status) {
    case 'ready':
      return 'text-emerald-400';
    case 'notes':
      return 'text-sky-300';
    case 'failed':
      return 'text-red-300';
    case 'checking':
      return 'text-[var(--color-text-secondary)]';
    case 'pending':
    default:
      return 'text-[var(--color-text-muted)]';
  }
}

const StatusIcon = ({ status }: { status: ProvisioningProviderCheckStatus }): React.JSX.Element => {
  if (status === 'checking') {
    return <Loader2 className="size-3 animate-spin" />;
  }
  if (status === 'ready') {
    return <CheckCircle2 className="size-3" />;
  }
  if (status === 'notes' || status === 'failed') {
    return <AlertTriangle className="size-3" />;
  }
  return <span className="inline-block size-1.5 rounded-full bg-current opacity-60" />;
};

function getProvisioningProviderSettingsActionLabel(
  check: ProvisioningProviderCheck,
  t?: TeamTranslator
): string | null {
  if (check.status !== 'notes' && check.status !== 'failed') {
    return null;
  }

  const details = getPublicProvisioningDetails(check.details);
  const combined = [check.backendSummary ?? '', ...details].join('\n').toLowerCase();
  if (!combined.trim()) {
    return null;
  }

  const hasActionableProviderSetupDetail =
    combined.includes('auth required') ||
    combined.includes('authentication required') ||
    combined.includes('not authenticated') ||
    combined.includes('not logged in') ||
    combined.includes('provider is not configured for runtime use') ||
    combined.includes('connect a chatgpt account') ||
    combined.includes('connected chatgpt account') ||
    combined.includes('reconnect chatgpt') ||
    combined.includes('openai_api_key') ||
    combined.includes('codex_api_key') ||
    combined.includes('anthropic_api_key') ||
    combined.includes('gemini_api_key') ||
    combined.includes('api key mode is selected');

  return hasActionableProviderSetupDetail
    ? t
      ? t('provisioning.providerStatus.openProviderSettings', {
          provider: getProvisioningProviderLabel(check.providerId),
        })
      : `Open ${getProvisioningProviderLabel(check.providerId)} settings`
    : null;
}

function getSupportDiagnosticsPayload(check: ProvisioningProviderCheck): string | null {
  if (check.providerId !== 'opencode') {
    return null;
  }
  const payloads = (check.supportDiagnostics ?? [])
    .map((diagnostic) => diagnostic.copyText.trim())
    .filter(Boolean);
  return payloads.length > 0 ? payloads.join('\n\n---\n\n') : null;
}

export const ProvisioningProviderStatusList = ({
  checks,
  className = '',
  suppressDetailsMatching,
  onOpenProviderSettings,
}: {
  checks: ProvisioningProviderCheck[];
  className?: string;
  suppressDetailsMatching?: string | null;
  onOpenProviderSettings?: (providerId: TeamProviderId) => void;
}): React.JSX.Element | null => {
  const { t } = useAppTranslation('team');
  const [copiedDiagnosticsKey, setCopiedDiagnosticsKey] = React.useState<string | null>(null);

  if (checks.length === 0) {
    return null;
  }

  const copySupportDiagnostics = async (copyKey: string, payload: string): Promise<void> => {
    try {
      const writeText = globalThis.navigator?.clipboard?.writeText;
      if (typeof writeText !== 'function') {
        setCopiedDiagnosticsKey(null);
        return;
      }
      await writeText.call(globalThis.navigator.clipboard, payload);
      setCopiedDiagnosticsKey(copyKey);
      globalThis.setTimeout(() => {
        setCopiedDiagnosticsKey((currentKey) => (currentKey === copyKey ? null : currentKey));
      }, 1500);
    } catch {
      setCopiedDiagnosticsKey(null);
    }
  };

  return (
    <div className={`space-y-1 pl-5 ${className}`.trim()}>
      {checks.map((check) => {
        const suppressDetailsMatchingTrimmed = (suppressDetailsMatching ?? '').trim();
        const visibleDetails = getPublicProvisioningDetails(check.details).filter(
          (detail) => detail.trim() !== suppressDetailsMatchingTrimmed
        );
        const settingsActionLabel = onOpenProviderSettings
          ? getProvisioningProviderSettingsActionLabel(check, t)
          : null;
        const supportDiagnosticsPayload = getSupportDiagnosticsPayload(check);
        const supportDiagnosticsKey =
          supportDiagnosticsPayload && check.supportDiagnostics?.[0]
            ? `${check.providerId}:${check.supportDiagnostics[0].id}`
            : check.providerId;
        const copiedDiagnostics = copiedDiagnosticsKey === supportDiagnosticsKey;

        return (
          <div key={check.providerId}>
            <div
              className={`flex items-center gap-1.5 text-[11px] ${getStatusColor(check.status)}`}
            >
              <StatusIcon status={check.status} />
              <span>
                {getProvisioningProviderLabel(check.providerId)}
                {check.backendSummary ? ` (${check.backendSummary})` : ''}:{' '}
                {getDisplayStatusText(check, t)}
              </span>
            </div>
            {visibleDetails.length > 0 ? (
              <div className="mt-0.5 space-y-0.5 pl-4">
                {visibleDetails.map((detail, index) => (
                  <p
                    key={`${check.providerId}:${index}:${detail}`}
                    className={`text-[10px] ${getDetailColorClass(
                      detail,
                      check.status,
                      check.providerId
                    )}`}
                  >
                    {detail}
                  </p>
                ))}
              </div>
            ) : null}
            {settingsActionLabel ? (
              <div className="mt-1 pl-4">
                <button
                  type="button"
                  className="inline-flex items-center gap-1 rounded-md border px-2 py-1 text-[10px] font-medium transition-colors hover:bg-white/5"
                  style={{
                    borderColor: 'var(--color-border-subtle)',
                    color: 'var(--color-text-secondary)',
                  }}
                  onClick={() => onOpenProviderSettings?.(check.providerId)}
                >
                  <SlidersHorizontal className="size-3" />
                  {settingsActionLabel}
                </button>
              </div>
            ) : null}
            {supportDiagnosticsPayload ? (
              <div className="mt-1 pl-4">
                <button
                  type="button"
                  className="inline-flex items-center gap-1 rounded-md border px-2 py-1 text-[10px] font-medium transition-colors hover:bg-white/5"
                  style={{
                    borderColor: 'var(--color-border-subtle)',
                    color: 'var(--color-text-secondary)',
                  }}
                  onClick={() =>
                    void copySupportDiagnostics(supportDiagnosticsKey, supportDiagnosticsPayload)
                  }
                >
                  {copiedDiagnostics ? <Check className="size-3" /> : <Copy className="size-3" />}
                  {copiedDiagnostics
                    ? t('provisioning.providerStatus.copied')
                    : t('provisioning.providerStatus.copyDiagnostics')}
                </button>
              </div>
            ) : null}
          </div>
        );
      })}
    </div>
  );
};

export function getProvisioningFailureHint(
  message: string | null | undefined,
  checks: ProvisioningProviderCheck[],
  t?: TeamTranslator
): string {
  const failedOpenCodeChecks = checks.filter(
    (check) => check.providerId === 'opencode' && check.status === 'failed'
  );
  const hasFailedNonOpenCodeCheck = checks.some(
    (check) => check.providerId !== 'opencode' && check.status === 'failed'
  );
  const hasOpenCodeAccessDeniedDetail = failedOpenCodeChecks.some((check) =>
    check.details.some(isOpenCodeWindowsAccessDeniedDiagnostic)
  );
  const hasOpenCodeNodeModulesSymlinkPermissionDetail = failedOpenCodeChecks.some((check) =>
    check.details.some(isOpenCodeWindowsNodeModulesSymlinkPermissionDiagnostic)
  );
  const hasOpenCodeBridgeNoOutputDetail = failedOpenCodeChecks.some((check) =>
    check.details.some(isOpenCodeBridgeNoOutputDiagnostic)
  );
  const normalizedMessage = message?.trim() ?? '';
  const hasOpenCodeNodeModulesSymlinkPermissionMessage =
    failedOpenCodeChecks.length > 0 &&
    (normalizedMessage === OPENCODE_WINDOWS_NODE_MODULES_SYMLINK_PERMISSION_MESSAGE ||
      (!hasFailedNonOpenCodeCheck &&
        isOpenCodeWindowsNodeModulesSymlinkPermissionDiagnostic(normalizedMessage)));
  const hasOpenCodeAccessDeniedMessage =
    failedOpenCodeChecks.length > 0 &&
    (normalizedMessage === OPENCODE_WINDOWS_ACCESS_DENIED_MESSAGE ||
      (!hasFailedNonOpenCodeCheck && isOpenCodeWindowsAccessDeniedDiagnostic(normalizedMessage)));
  if (
    hasOpenCodeNodeModulesSymlinkPermissionDetail ||
    hasOpenCodeNodeModulesSymlinkPermissionMessage
  ) {
    return (
      t?.('provisioning.providerStatus.failureHints.openCodeNodeModulesSymlinkPermission') ??
      'Run Agent Teams AI as Administrator, then retry launch.'
    );
  }
  if (hasOpenCodeAccessDeniedDetail || hasOpenCodeAccessDeniedMessage) {
    return (
      t?.('provisioning.providerStatus.failureHints.openCodeAccessDenied') ??
      'Fix folder permissions or move the project to a user-writable folder. Running as administrator is only a temporary workaround.'
    );
  }
  const hasOpenCodeBridgeNoOutputMessage =
    failedOpenCodeChecks.length > 0 &&
    !hasFailedNonOpenCodeCheck &&
    isOpenCodeBridgeNoOutputDiagnostic(normalizedMessage);
  if (hasOpenCodeBridgeNoOutputDetail || hasOpenCodeBridgeNoOutputMessage) {
    return (
      t?.('provisioning.providerStatus.failureHints.openCodeBridgeNoOutput') ??
      'Restart the app and OpenCode runtime, then retry. If it repeats, copy diagnostics.'
    );
  }

  const combined = [message ?? '', ...checks.flatMap((check) => check.details)]
    .join('\n')
    .toLowerCase();

  if (combined.includes('working directory does not exist:')) {
    return (
      t?.('provisioning.providerStatus.failureHints.workingDirectoryMissing') ??
      'Choose an existing working directory, then reopen this dialog.'
    );
  }
  if (combined.includes('not authenticated') || combined.includes('not logged in')) {
    return (
      t?.('provisioning.providerStatus.failureHints.authenticationRequired') ??
      'Authenticate the required provider in Claude CLI, then reopen this dialog.'
    );
  }
  if (combined.includes('provider is not configured for runtime use')) {
    return (
      t?.('provisioning.providerStatus.failureHints.runtimeProviderNotConfigured') ??
      'Configure the selected provider runtime, then reopen this dialog.'
    );
  }
  if (
    combined.includes('opencode cli not detected on path') ||
    combined.includes('opencode cli not found') ||
    combined.includes('opencode runtime binary is not installed')
  ) {
    return (
      t?.('provisioning.providerStatus.failureHints.openCodeRuntimeMissing') ??
      'Install or retry OpenCode runtime from the provider status card, then reopen this dialog.'
    );
  }
  if (
    combined.includes('opencode app mcp is unreachable') ||
    (combined.includes('unable to connect') &&
      (combined.includes('/experimental/tool') || combined.includes('mcp_unavailable')))
  ) {
    return (
      t?.('provisioning.providerStatus.failureHints.openCodeAppMcpUnreachable') ??
      'Retry launch to refresh the OpenCode app MCP bridge. If it repeats, restart the app and OpenCode runtime.'
    );
  }
  if (
    combined.includes('spawn ') ||
    combined.includes(' enoent') ||
    combined.includes('eacces') ||
    combined.includes('enoexec') ||
    combined.includes('bad cpu type in executable') ||
    combined.includes('image not found')
  ) {
    return (
      t?.('provisioning.providerStatus.failureHints.cliBinaryMissing') ??
      'Make sure the local Claude CLI binary exists and can be started, then reopen this dialog.'
    );
  }

  return (
    t?.('provisioning.providerStatus.failureHints.default') ??
    'Resolve the issue above, then reopen this dialog.'
  );
}
