import type {
  BondReadModel,
  GovernanceReadModel,
  MandateReadModel,
  PullCreditReadModel,
  SolvencyReadModel,
  X402ChallengeReadModel,
} from '../../../../../vaultfire-sdk/src/v3/types';

export const V3_PROTOCOL_VERSION = '3.0.0' as const;
export const V3_SCHEMA_VERSION = '1.0.0' as const;
export const NO_STORE_HEADERS = Object.freeze({
  'Cache-Control': 'no-store',
  'X-Robots-Tag': 'noindex',
});

export type V3ReadDisposition = 'supported' | 'degraded' | 'unavailable' | 'retired';
export type V3Readiness = 'ready' | 'deployment-unconfigured' | 'adapter-unconfigured' | 'source-only' | 'deferred' | 'retired';

/** This mapping keeps route payload names tied to the canonical SDK read models. */
export interface CanonicalV3RouteModels {
  bond: BondReadModel;
  mandate: MandateReadModel;
  /** No canonical task read model is exported by the current V3 SDK. */
  task: never;
  governance: GovernanceReadModel;
  pullCredit: PullCreditReadModel;
  solvency: SolvencyReadModel;
  x402Challenge: X402ChallengeReadModel;
}

export interface V3UnavailableEnvelope {
  schema: 'vaultfire.v3.read-result.v1';
  schemaVersion: typeof V3_SCHEMA_VERSION;
  protocolVersion: typeof V3_PROTOCOL_VERSION;
  route: string;
  resource: string;
  disposition: V3ReadDisposition;
  readiness: Exclude<V3Readiness, 'ready'>;
  data: null;
  observedAt: string;
  error: {
    code: string;
    message: string;
    retryable: boolean;
  };
  evidence: {
    deploymentVerified: false;
    adapterConfigured: false;
    rpcContacted: false;
    v2FallbackUsed: false;
  };
}

const VERSIONED_ROUTE = /^\/api\/v3(?:\/|$)/;

export function assertV3UnavailableEnvelope(value: unknown): asserts value is V3UnavailableEnvelope {
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('V3 envelope must be an object');
  const body = value as Record<string, any>;
  if (
    body.schema !== 'vaultfire.v3.read-result.v1' ||
    body.schemaVersion !== V3_SCHEMA_VERSION ||
    body.protocolVersion !== V3_PROTOCOL_VERSION ||
    typeof body.route !== 'string' ||
    !VERSIONED_ROUTE.test(body.route) ||
    typeof body.resource !== 'string' ||
    !VERSIONED_ROUTE.test(body.resource) ||
    !['supported', 'degraded', 'unavailable', 'retired'].includes(body.disposition) ||
    !['deployment-unconfigured', 'adapter-unconfigured', 'source-only', 'deferred', 'retired'].includes(body.readiness) ||
    body.data !== null ||
    typeof body.observedAt !== 'string' ||
    Number.isNaN(Date.parse(body.observedAt)) ||
    typeof body.error?.code !== 'string' ||
    typeof body.error?.message !== 'string' ||
    typeof body.error?.retryable !== 'boolean' ||
    body.evidence?.deploymentVerified !== false ||
    body.evidence?.adapterConfigured !== false ||
    body.evidence?.rpcContacted !== false ||
    body.evidence?.v2FallbackUsed !== false
  ) {
    throw new Error('invalid V3 unavailable envelope');
  }
}

export function unavailableV3Read(options: {
  route: string;
  resource?: string;
  disposition?: V3ReadDisposition;
  readiness?: Exclude<V3Readiness, 'ready'>;
  code?: string;
  message?: string;
  retryable?: boolean;
  now?: Date;
}): Response {
  const disposition = options.disposition ?? 'supported';
  const readiness = options.readiness ??
    (disposition === 'retired' ? 'retired' : disposition === 'degraded' ? 'deferred' : 'deployment-unconfigured');
  const body: V3UnavailableEnvelope = {
    schema: 'vaultfire.v3.read-result.v1',
    schemaVersion: V3_SCHEMA_VERSION,
    protocolVersion: V3_PROTOCOL_VERSION,
    route: options.route,
    resource: options.resource ?? options.route,
    disposition,
    readiness,
    data: null,
    observedAt: (options.now ?? new Date()).toISOString(),
    error: {
      code: options.code ?? `v3_${readiness.replaceAll('-', '_')}`,
      message: options.message ?? 'V3 data is unavailable until its deployment and read adapter are independently verified',
      retryable: options.retryable ?? false,
    },
    evidence: {
      deploymentVerified: false,
      adapterConfigured: false,
      rpcContacted: false,
      v2FallbackUsed: false,
    },
  };
  assertV3UnavailableEnvelope(body);
  return Response.json(body, { status: disposition === 'retired' ? 410 : 503, headers: NO_STORE_HEADERS });
}

export function invalidV3Input(code: string, message: string): Response {
  return Response.json({
    schema: 'vaultfire.v3.input-error.v1',
    schemaVersion: V3_SCHEMA_VERSION,
    protocolVersion: V3_PROTOCOL_VERSION,
    error: { code, message, retryable: false },
  }, { status: 400, headers: NO_STORE_HEADERS });
}

export function staticV3Json<T extends Record<string, unknown>>(
  body: T,
  assert: (value: unknown) => void,
  status = 200,
): Response {
  assert(body);
  return Response.json(body, { status, headers: NO_STORE_HEADERS });
}

export { V3_UINT, V3_ADDRESS, V3_BYTES32, V3_IDENTIFIER, V3_CONTRACT_NAME, strictV3Query } from './validation';

export function safePathSegment(value: string): string {
  return encodeURIComponent(value);
}
