/**
 * Shared helpers for parsing on-chain agentURI metadata.
 *
 * ERC-8004 agentURI can be:
 *   1. A JSON blob with { name, description, capabilities, type, ... }
 *   2. A URL pointing to an agent.json
 *   3. A plain string (e.g. "vaultfire-sentinel" or "vaultfire-sentinel.vns")
 *
 * Every endpoint that reads identity registry data should use these helpers
 * so the same URI produces the same parsed fields everywhere.
 */

export interface ParsedAgentURI {
  name: string;                 // Clean slug (no .vns suffix), e.g. "vaultfire-sentinel"
  description: string;          // Human-readable description, if present
  identityType?: string;        // From parsed.type (e.g. "autonomous-sentinel")
  capabilities: string[];       // From parsed.capabilities or parsed.skills
  version?: string;             // From parsed.version
  protocol?: string;            // From parsed.protocol
  raw: string;                  // Original URI for callers that need it
}

const SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;

function fallbackName(address: string): string {
  return `agent-${address.slice(2, 8).toLowerCase()}`;
}

function cleanSlug(s: string): string {
  return String(s || '').replace(/\.vns$/i, '').toLowerCase().trim();
}

function toStringArray(v: unknown): string[] {
  if (Array.isArray(v)) {
    return v.filter(x => typeof x === 'string' && x.trim().length > 0).map(x => String(x).trim());
  }
  if (typeof v === 'string' && v.trim().length > 0) {
    return v.split(/[,;\s]+/).filter(Boolean);
  }
  return [];
}

/**
 * Parse an on-chain agentURI string into structured fields.
 *
 * Never throws — always returns a usable ParsedAgentURI. If the URI is
 * malformed or empty, `name` falls back to `agent-<addr-prefix>`.
 */
export function parseAgentURI(uri: string, address: string): ParsedAgentURI {
  const raw = String(uri || '');

  // 1. JSON blob
  if (raw.startsWith('{')) {
    try {
      const parsed = JSON.parse(raw);
      const rawName = cleanSlug(parsed.name || '');
      const name = rawName && SLUG_RE.test(rawName) ? rawName : fallbackName(address);
      const capabilities = toStringArray(parsed.capabilities ?? parsed.skills);
      return {
        name,
        description: typeof parsed.description === 'string'
          ? parsed.description
          : (typeof parsed.desc === 'string' ? parsed.desc : ''),
        identityType: typeof parsed.type === 'string' ? parsed.type : undefined,
        capabilities,
        version: typeof parsed.version === 'string' ? parsed.version : undefined,
        protocol: typeof parsed.protocol === 'string' ? parsed.protocol : undefined,
        raw,
      };
    } catch { /* fall through */ }
  }

  // 2. URL
  if (raw.startsWith('http://') || raw.startsWith('https://')) {
    try {
      const url = new URL(raw);
      const parts = url.pathname.split('/').filter(Boolean);
      const last = parts[parts.length - 1] || '';
      const cleaned = cleanSlug(last.replace(/\.json$/i, ''));
      const name = cleaned && SLUG_RE.test(cleaned) ? cleaned : fallbackName(address);
      return { name, description: '', capabilities: [], raw };
    } catch { /* fall through */ }
  }

  // 3. Plain string (possibly .vns name)
  const cleaned = cleanSlug(raw);
  const name = cleaned && SLUG_RE.test(cleaned) ? cleaned : fallbackName(address);
  return { name, description: '', capabilities: [], raw };
}

/**
 * Convenience: return just the display name, with optional .vns suffix.
 * Equivalent to `${parseAgentURI(uri, address).name}.vns`.
 */
export function nameFromAgentURI(uri: string, address: string, withVnsSuffix = true): string {
  const { name } = parseAgentURI(uri, address);
  return withVnsSuffix ? `${name}.vns` : name;
}

/** Decode an even-length hex string as UTF-8. Falls back to latin-1 if the bytes are not valid UTF-8. */
export function hexToUtf8(hex: string): string {
  if (!hex) return '';
  try {
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i < hex.length; i += 2) {
      bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
    }
    return new TextDecoder('utf-8', { fatal: false }).decode(bytes);
  } catch {
    let s = '';
    for (let i = 0; i < hex.length; i += 2) {
      s += String.fromCharCode(parseInt(hex.slice(i, i + 2), 16));
    }
    return s;
  }
}

// ABI decoding helpers for getAgent(address) tuple:
//   (string agentURI, bool active, string agentType, uint256 registeredAt)
export interface DecodedGetAgent {
  agentURI: string;
  active: boolean;
  agentType: string;
  registeredAt: number;
}

/**
 * Decode the raw hex response from getAgent(address).
 * Returns null if the response is empty (address not registered).
 */
export function decodeGetAgent(hex: string): DecodedGetAgent | null {
  if (!hex || hex === '0x' || hex.length < 256 + 2) return null;
  const data = hex.startsWith('0x') ? hex.slice(2) : hex;

  try {
    const uriOffset = parseInt(data.slice(0, 64), 16) * 2;
    const active = parseInt(data.slice(64, 128), 16) === 1;
    const typeOffset = parseInt(data.slice(128, 192), 16) * 2;
    const registeredAt = parseInt(data.slice(192, 256), 16);

    const uriLen = parseInt(data.slice(uriOffset, uriOffset + 64), 16);
    const uriHex = data.slice(uriOffset + 64, uriOffset + 64 + uriLen * 2);
    const agentURI = hexToUtf8(uriHex);

    const typeLen = parseInt(data.slice(typeOffset, typeOffset + 64), 16);
    const typeHex = data.slice(typeOffset + 64, typeOffset + 64 + typeLen * 2);
    const agentType = hexToUtf8(typeHex);

    return { agentURI, active, agentType, registeredAt };
  } catch {
    return null;
  }
}
