import { normalizeAttachmentPaths } from '../../shared/workspace/utils';
import { normalizeDashboardAttachmentPath } from '../../shared/workspace/workspaceMarkdown';
import type {
  CronJob,
  MCPConfigResponse,
  MCPServerDraft,
} from './types';

const COMPOSER_DRAFT_STORAGE_PREFIX = 'nanobot-dashboard-composer-draft:v1:';

export function stateLabel(s?: string) {
  return (s || 'IDLE').toUpperCase();
}

export function normalizeRuntimeState(s?: string) {
  const raw = stateLabel(s);
  if (raw.includes('ERROR') || raw.includes('FAIL')) return 'ERROR';
  if (raw.includes('TOOL') || raw.includes('EXEC') || raw.includes('ACTION')) return 'TOOL_CALL';
  if (raw.includes('THINK') || raw.includes('PLAN') || raw.includes('REASON') || raw === 'RUNNING') return 'THINKING';
  if (raw.includes('SUCCESS') || raw.includes('DONE') || raw.includes('COMPLETE')) return 'SUCCESS';
  if (raw.includes('IDLE') || raw.includes('STOP')) return 'IDLE';
  return raw;
}

export interface ComposerDraftStorage {
  command: string;
  attachments: string[];
  updated_at_ms: number;
}

export function getComposerDraftStorageKey(botId: string): string {
  return `${COMPOSER_DRAFT_STORAGE_PREFIX}${String(botId || '').trim()}`;
}

export function loadComposerDraft(botId: string): ComposerDraftStorage | null {
  const id = String(botId || '').trim();
  if (!id || typeof window === 'undefined') return null;
  try {
    const raw = window.localStorage.getItem(getComposerDraftStorageKey(id));
    if (!raw) return null;
    const parsed = JSON.parse(raw) as Partial<ComposerDraftStorage> | null;
    const command = String(parsed?.command || '');
    const attachments = normalizeAttachmentPaths(parsed?.attachments)
      .map(normalizeDashboardAttachmentPath)
      .filter(Boolean);
    return {
      command,
      attachments,
      updated_at_ms: Number(parsed?.updated_at_ms || Date.now()),
    };
  } catch {
    return null;
  }
}

export function persistComposerDraft(botId: string, commandRaw: string, attachmentsRaw: string[]): void {
  const id = String(botId || '').trim();
  if (!id || typeof window === 'undefined') return;
  const command = String(commandRaw || '');
  const attachments = normalizeAttachmentPaths(attachmentsRaw)
    .map(normalizeDashboardAttachmentPath)
    .filter(Boolean);
  const key = getComposerDraftStorageKey(id);
  try {
    if (!command.trim() && attachments.length === 0) {
      window.localStorage.removeItem(key);
      return;
    }
    const payload: ComposerDraftStorage = {
      command,
      attachments,
      updated_at_ms: Date.now(),
    };
    window.localStorage.setItem(key, JSON.stringify(payload));
  } catch {
    // ignore localStorage write failures
  }
}


export function clampTemperature(value: number) {
  if (Number.isNaN(value)) return 0.2;
  return Math.min(1, Math.max(0, value));
}

export function clampMaxTokens(value: number) {
  if (Number.isNaN(value)) return 8192;
  return Math.min(32768, Math.max(256, Math.round(value)));
}

export function clampCpuCores(value: number) {
  if (Number.isNaN(value)) return 1;
  if (value === 0) return 0;
  return Math.min(16, Math.max(0.1, Math.round(value * 10) / 10));
}

export function clampMemoryMb(value: number) {
  if (Number.isNaN(value)) return 1024;
  if (value === 0) return 0;
  return Math.min(65536, Math.max(256, Math.round(value)));
}

export function clampStorageGb(value: number) {
  if (Number.isNaN(value)) return 10;
  if (value === 0) return 0;
  return Math.min(1024, Math.max(1, Math.round(value)));
}

export function formatBytes(bytes: number): string {
  const value = Number(bytes || 0);
  if (!Number.isFinite(value) || value <= 0) return '0 B';
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  const idx = Math.min(units.length - 1, Math.floor(Math.log(value) / Math.log(1024)));
  const scaled = value / Math.pow(1024, idx);
  return `${scaled >= 10 ? scaled.toFixed(1) : scaled.toFixed(2)} ${units[idx]}`;
}

export function formatPercent(value: number): string {
  const n = Number(value || 0);
  if (!Number.isFinite(n)) return '0.00%';
  return `${Math.max(0, n).toFixed(2)}%`;
}

export function formatWorkspaceTime(raw: string | undefined, isZh: boolean): string {
  const text = String(raw || '').trim();
  if (!text) return '-';
  const dt = new Date(text);
  if (Number.isNaN(dt.getTime())) return '-';
  try {
    return dt.toLocaleString(isZh ? 'zh-CN' : 'en-US', {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      weekday: 'long',
      hour: '2-digit',
      minute: '2-digit',
      hour12: false,
    });
  } catch {
    return dt.toLocaleString();
  }
}

export function formatCronSchedule(job: CronJob, isZh: boolean) {
  const s = job.schedule || {};
  if (s.kind === 'every' && Number(s.everyMs) > 0) {
    const sec = Math.round(Number(s.everyMs) / 1000);
    return isZh ? `每 ${sec}s` : `every ${sec}s`;
  }
  if (s.kind === 'cron') {
    if (s.tz) return `${s.expr || '-'} (${s.tz})`;
    return s.expr || '-';
  }
  if (s.kind === 'at' && Number(s.atMs) > 0) {
    return new Date(Number(s.atMs)).toLocaleString();
  }
  return '-';
}

export function mapMcpResponseToDrafts(payload?: MCPConfigResponse | null): MCPServerDraft[] {
  const rows = payload?.mcp_servers && typeof payload.mcp_servers === 'object' ? payload.mcp_servers : {};
  return Object.entries(rows).map(([name, cfg]) => {
    const rawHeaders = cfg?.headers && typeof cfg.headers === 'object' ? cfg.headers : {};
    const headers: Record<string, string> = {};
    Object.entries(rawHeaders).forEach(([k, v]) => {
      const key = String(k || '').trim();
      if (!key) return;
      headers[key] = String(v ?? '').trim();
    });
    const headerEntries = Object.entries(headers);
    const botIdHeader = headerEntries.find(([k]) => String(k || '').trim().toLowerCase() === 'x-bot-id');
    const botSecretHeader = headerEntries.find(([k]) => String(k || '').trim().toLowerCase() === 'x-bot-secret');
    const type: MCPServerDraft['type'] = String(cfg?.type || 'streamableHttp') === 'sse' ? 'sse' : 'streamableHttp';
    return {
      name: String(name || '').trim(),
      type,
      url: String(cfg?.url || '').trim(),
      botId: String(botIdHeader?.[1] || '').trim(),
      botSecret: String(botSecretHeader?.[1] || '').trim(),
      toolTimeout: String(Number(cfg?.toolTimeout || 60) || 60),
      headers,
      locked: Boolean(cfg?.locked),
      originName: String(name || '').trim(),
    };
  });
}
