import { mockDashboardData } from './mockData';
import type { DashboardData, HealthStatus, Severity } from './types';

const API_ORIGIN = (import.meta.env.VITE_API_BASE_URL ?? '').replace(/\/$/, '');
const API_V1 = `${API_ORIGIN}/api/v1`;
const SPEC_NAMES = [
  'locate_end_of_central_directory', 'parse_central_directory_entry', 'parse_local_file_header',
  'decode_filename', 'normalize_extraction_path', 'decompress_entry', 'validate_crc',
  'extract_entry', 'list_archive',
];

export type DataSource = 'live' | 'demo';
export interface DashboardResponse { data: DashboardData; source: DataSource; error?: string }

type JsonRecord = Record<string, unknown>;
const record = (value: unknown): JsonRecord => value && typeof value === 'object' ? value as JsonRecord : {};
const list = (value: unknown): JsonRecord[] => Array.isArray(value) ? value.map(record) : [];
const number = (value: unknown, fallback = 0): number => typeof value === 'number' && Number.isFinite(value) ? value : fallback;
const text = (value: unknown, fallback = ''): string => typeof value === 'string' ? value : fallback;
const percent = (value: unknown): number => number(value) * 100;
const variantName = (id: string): string => id.split('.').at(-1) || id;
const shortVariant = (id: string): string => {
  const name = variantName(id);
  const parts = name.split(/[_-]/).filter(Boolean);
  return parts.length > 1
    ? parts.map((part) => part[0]?.toUpperCase()).join('').slice(0, 4)
    : name.slice(0, 4).toUpperCase();
};
const friendly = (value: string): string => value.replaceAll('_', ' ').replace(/\b\w/g, (letter) => letter.toUpperCase());

export function normalizeSummary(rawSummary: unknown, rawTelemetry: unknown): DashboardData {
  const summary = record(rawSummary);
  const release = record(summary.release);
  const implementationStats = list(summary.implementation_stats);
  const distribution = list(summary.active_distribution);
  const interactions = list(summary.pairwise_interactions);
  const quarantined = list(summary.quarantined);
  const recompositions = list(summary.recompositions);
  const effects = list(summary.quarantine_effects);
  const timeline = list(summary.failure_timeline);
  const sequence = list(summary.quarantine_sequence);
  const telemetry = list(rawTelemetry);
  const implementationById = new Map(implementationStats.map((item) => [text(item.id), item]));
  const implementationCounts = Array.isArray(summary.implementations_per_function)
    ? (summary.implementations_per_function as unknown[]).map((count, index) => ({ name: friendly(SPEC_NAMES[index] ?? `spec_function_${index + 1}`), count: number(count) }))
    : [];
  const quarantinedIds = new Set(quarantined.map((item) => text(item.id)));

  const normalizedImplementations = implementationStats.map((item) => {
    const relativeRisk = number(item.relative_risk, item.infinite_relative_risk ? 9.9 : 1);
    const executions = number(item.executions);
    const rawStatus = text(item.status, 'active');
    const status: HealthStatus = rawStatus === 'quarantined' || quarantinedIds.has(text(item.id))
      ? 'quarantined'
      : relativeRisk >= 1.5 && executions >= 3 ? 'watch' : 'healthy';
    return {
      id: text(item.id), releaseId: number(item.release_id, number(summary.release_id)),
      specFunction: text(item.spec_function), variant: shortVariant(text(item.id)),
      variantName: variantName(text(item.id)), experimental: Boolean(item.experimental),
      sourceUrl: text(item.source_url) || undefined,
      clients: number(item.clients_observed), executions, failures: number(item.failures),
      failureRate: percent(item.failure_rate), relativeRisk,
      confidence: Math.min(99.9, 60 + Math.log10(Math.max(executions, 1)) * 9), status,
    };
  }).sort((left, right) =>
    right.relativeRisk - left.relativeRisk
    || right.failureRate - left.failureRate
    || right.executions - left.executions
  );

  const totalDistributionClients = distribution.reduce((sum, item) => sum + number(item.clients), 0) || 1;
  const compositions = [...distribution].sort((a, b) => number(b.clients) - number(a.clients)).slice(0, 5).map((item) => {
    const id = text(item.implementation_id);
    const implementation = implementationById.get(id);
    return {
      id, label: id, clients: number(item.clients), share: number(item.clients) / totalDistributionClients * 100,
      failureRate: percent(implementation?.failure_rate), implementations: [text(item.spec_function)],
    };
  });

  const normalizedInteractions = interactions.slice(0, 5).map((item) => {
    const observed = percent(item.failure_rate);
    const expected = Math.max(0, observed - percent(item.interaction_lift));
    return {
      first: shortVariant(text(item.implementation_a)), second: shortVariant(text(item.implementation_b)),
      clients: number(item.executions), observedFailureRate: observed, expectedFailureRate: expected,
      lift: expected > 0 ? observed / expected : observed > 0 ? 9.9 : 1,
      confidence: Math.min(99.9, 55 + Math.log10(Math.max(number(item.executions), 1)) * 10),
    };
  });

  const quarantineRows = quarantined.map((item) => {
    const id = text(item.id);
    const change = recompositions.find((candidate) => text(candidate.implementation_id) === id) ?? {};
    const effect = effects.find((candidate) => text(candidate.implementation_id) === id) ?? {};
    const implementation = implementationById.get(id) ?? {};
    const affected = number(change.affected_clients);
    return {
      implementationId: id, variant: shortVariant(id), specFunction: text(implementation.spec_function, 'spec_function'),
      quarantinedAt: text(change.changed_at, new Date().toISOString()),
      reason: text(item.status_reason, text(change.reason, 'Quarantined by policy')),
      affectedClients: affected, recomposedClients: affected,
      beforeFailureRate: percent(effect.before_failure_rate), afterFailureRate: percent(effect.after_failure_rate),
      afterExecutions: number(effect.after_executions),
    };
  });

  const normalizedTimeline = timeline.slice(-16).map((item) => ({
    label: text(item.bucket).slice(11, 16) || text(item.bucket),
    failureRate: percent(item.failure_rate), events: number(item.executions),
  }));
  const normalizedTelemetry = telemetry.map((item, index) => {
    const kind = text(item.event_type, 'spec_error');
    const severity: Severity = kind === 'panic' || kind === 'integrity_failure' ? 'critical' : 'warning';
    const calls = Array.isArray(item.recent_calls) ? item.recent_calls.map((call) => text(record(call).spec_function)).filter(Boolean) : [];
    return {
      id: text(item.id, `event-${index}`), timestamp: text(item.created_at, new Date().toISOString()), kind, severity,
      clientId: text(item.client_id).replace(/^(pc_.{4}).*(.{4})$/, '$1…$2'),
      implementationId: text(item.implementation_id), specFunction: text(item.spec_function),
      message: text(item.message, text(item.message_code, friendly(kind))), recentCalls: calls,
    };
  });

  const healthSequence = sequence.map((stage, index) => {
    const implementationId = text(stage.implementation_id);
    return {
      label: index === 0 ? 'Baseline' : `After ${shortVariant(implementationId)}`,
      implementationId: implementationId || undefined,
      healthyRate: percent(stage.healthy_rate), executions: number(stage.executions),
    };
  });
  const latestMeasuredIndex = healthSequence.reduce((found, stage, index) => stage.executions > 0 ? index : found, -1);
  const measuredStage = latestMeasuredIndex >= 0 ? healthSequence[latestMeasuredIndex] : undefined;
  const previousStage = latestMeasuredIndex > 0
    ? healthSequence.slice(0, latestMeasuredIndex).reverse().find((stage) => stage.executions > 0) ?? measuredStage
    : measuredStage;
  const failureRate = measuredStage ? 100 - measuredStage.healthyRate : percent(summary.failure_rate);
  const previousFailureRate = previousStage ? 100 - previousStage.healthyRate : failureRate;
  return {
    generatedAt: text(summary.latest_execution_at), release: {
      name: text(release.application, 'zipdiv'), version: text(release.version, 'reference'), status: 'active',
      repositoryUrl: text(release.repository_url) || undefined, artifactUrl: text(release.artifact_url) || undefined,
    },
    summary: {
      clients: number(summary.clients), activeClients: number(summary.clients), specFunctions: number(summary.spec_functions),
      implementations: implementationCounts.reduce((sum, item) => sum + item.count, 0),
      possibleCompositions: number(summary.theoretical_compositions), activeCompositions: number(summary.observed_compositions, number(summary.clients)),
      telemetryEvents: number(summary.telemetry_events), populationFailureRate: failureRate,
      previousFailureRate,
    },
    implementationsPerFunction: implementationCounts, compositions,
    implementations: normalizedImplementations, interactions: normalizedInteractions,
    quarantines: quarantineRows.reverse(), healthSequence, telemetry: normalizedTelemetry, trend: normalizedTimeline,
  };
}

export async function fetchDashboard(signal?: AbortSignal, releaseId?: number): Promise<DashboardResponse> {
  try {
    const query = releaseId ? `?release_id=${releaseId}` : '';
    const [summaryResponse, telemetryResponse] = await Promise.all([
      fetch(`${API_V1}/dashboard/summary${query}`, { signal, headers: { Accept: 'application/json' } }),
      fetch(`${API_V1}/telemetry?limit=50${releaseId ? `&release_id=${releaseId}` : ''}`, { signal, headers: { Accept: 'application/json' } }),
    ]);
    if (!summaryResponse.ok) throw new Error(`Dashboard API returned ${summaryResponse.status}`);
    const summary: unknown = await summaryResponse.json();
    const telemetry: unknown = telemetryResponse.ok ? await telemetryResponse.json() : [];
    return { data: normalizeSummary(summary, telemetry), source: 'live' };
  } catch (error) {
    if (error instanceof DOMException && error.name === 'AbortError') throw error;
    return { data: mockDashboardData, source: 'demo', error: error instanceof Error ? error.message : 'Unable to reach dashboard API' };
  }
}

export async function quarantineImplementation(id: string, releaseId: number, reason: string): Promise<void> {
  return updateImplementationStatus(id, releaseId, 'quarantined', reason);
}

export async function restoreImplementation(id: string, releaseId: number, reason: string): Promise<void> {
  return updateImplementationStatus(id, releaseId, 'active', reason);
}

async function updateImplementationStatus(id: string, releaseId: number, status: 'active' | 'quarantined', reason: string): Promise<void> {
  let token = window.sessionStorage.getItem('polyform-admin-token');
  const send = (adminToken: string | null) => fetch(`${API_V1}/implementations/${encodeURIComponent(id)}/status?release_id=${releaseId}`, {
    method: 'PUT', headers: { 'Content-Type': 'application/json', ...(adminToken ? { 'X-Polyform-Admin-Token': adminToken } : {}) },
    body: JSON.stringify({ status, reason }), credentials: 'same-origin',
  });
  let response = await send(token);
  if (response.status === 401 && !token) {
    token = window.prompt('Enter the Polyform administrator token')?.trim() || null;
    if (!token) throw new Error('Owner sign-in or administrator token required');
    window.sessionStorage.setItem('polyform-admin-token', token);
    response = await send(token);
  }
  if (response.status === 401) window.sessionStorage.removeItem('polyform-admin-token');
  if (!response.ok) throw new Error(`Implementation status update failed (${response.status})`);
}
