import type { RuntimeDashboardLayout } from './types';

// All API calls go through the gateway proxy
const API_GATEWAY = '';

function authHeaders(): Record<string, string> {
  if (typeof sessionStorage === 'undefined') return {};
  const token = sessionStorage.getItem('nxtgauge_admin_access_token');
  if (!token) return {};
  return { Authorization: `Bearer ${token}` };
}

function authedFetch(url: string, init: RequestInit = {}): Promise<Response> {
  return fetch(url, {
    ...init,
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
      ...authHeaders(),
      ...(init.headers as Record<string, string> | undefined),
    },
  });
}

export type RuntimeRecordType = 'role' | 'dashboard' | 'onboarding' | 'knowledge_base';
export type RuntimeRecordStatus = 'draft' | 'published';

export type RuntimeStoredRecord<T> = {
  status: RuntimeRecordStatus;
  updatedAt: string;
  payload: T;
};

export type RuntimeListItem<T> = RuntimeStoredRecord<T> & {
  key: string;
};

// Types corresponding to Rust backend models
type RoleRecord = {
  id: string;
  key: string;
  name: string;
  audience: string;
};

type DashboardConfigResponse = {
  id?: string;
  role_id?: string;
  config_json?: Record<string, unknown>;
  is_active?: boolean;
  updated_at?: string;
  created_at?: string;
};

export async function saveRuntimeConfig(type: RuntimeRecordType, key: string, payload: unknown, status: RuntimeRecordStatus) {
  const normalizedKey = key.trim().toUpperCase();
  if (!normalizedKey) return;

  try {
    // 1. Ensure the Role exists first. We lookup by key.
    // If it doesn't exist, we create it generically.
    const roleRes = await authedFetch(`${API_GATEWAY}/api/admin/roles/${normalizedKey}`);
    let role: RoleRecord;

    if (!roleRes.ok && roleRes.status === 404) {
      // Create it
      const createRes = await authedFetch(`${API_GATEWAY}/api/admin/roles`, {
        method: 'POST',
        body: JSON.stringify({
          key: normalizedKey,
          name: normalizedKey,
          audience: 'EXTERNAL',
        }),
      });
      if (!createRes.ok) throw new Error('Failed to create role');
      role = await createRes.json();
    } else {
      role = await roleRes.json();
    }

    // 2. Attach the specific config payload to the role
    const endpointMap: Record<RuntimeRecordType, string> = {
      role: '/api/runtime-config', // Storing the "role config wrapper" as runtime_config
      dashboard: '/api/admin/dashboard-config',
      onboarding: '/api/admin/onboarding-config',
      knowledge_base: '/api/admin/knowledge-base',
    };

    const bodyPayload: Record<string, unknown> = {
      role_id: role.id,
    };

    if (type === 'dashboard') {
      bodyPayload.audience = 'EXTERNAL';
      bodyPayload.config_json = payload;
    } else if (type === 'onboarding') {
      bodyPayload.schema_json = payload;
    } else if (type === 'role') {
      bodyPayload.config_json = payload;
    }

    const saveRes = await authedFetch(`${API_GATEWAY}${endpointMap[type]}`, {
      method: 'POST',
      body: JSON.stringify(bodyPayload),
    });

    if (!saveRes.ok) {
      throw new Error(`Failed to save ${type} config`);
    }

    console.log(`Saved ${type} mapping for role ${normalizedKey} successfully to Rust backend`);

  } catch (err) {
    console.error(`Error saving runtime config [${type}]:`, err);
  }
}

export async function listRuntimeConfigs<T>(type: RuntimeRecordType): Promise<Array<RuntimeListItem<T>>> {
  const endpointMap: Record<RuntimeRecordType, string> = {
    role: '/api/admin/roles',
    dashboard: '/api/admin/dashboard-config',
    onboarding: '/api/admin/onboarding-config',
    knowledge_base: '/api/admin/knowledge-base',
  };

  try {
    const res = await authedFetch(`${API_GATEWAY}${endpointMap[type]}`);
    if (!res.ok) throw new Error(`Failed to list ${type} configs`);
    const data = await res.json();

    // Map Rust backend models to UI expected format
    return data.map((item: any) => ({
      key: item.role_key || item.key,
      status: item.is_active ? 'published' : 'draft',
      updatedAt: item.updated_at || item.created_at,
      payload: item.config_json || item.schema_json || item,
      // include raw id for routing convenience
      id: item.id,
      role_id: item.role_id
    }));
  } catch (err) {
    console.error(`Error listing runtime config [${type}]:`, err);
    return [];
  }
}

export async function getRuntimeConfig<T>(type: RuntimeRecordType, roleId: string): Promise<RuntimeStoredRecord<T> | null> {
  const endpointMap: Record<RuntimeRecordType, string> = {
    role: `/api/admin/roles/${roleId}`, // Assume roleId is key if testing
    dashboard: `/api/admin/dashboard-config/${roleId}?audience=EXTERNAL`,
    onboarding: `/api/admin/onboarding-config/${roleId}`,
    knowledge_base: `/api/admin/knowledge-base/${roleId}`,
  };

  try {
    const res = await authedFetch(`${API_GATEWAY}${endpointMap[type]}`);
    if (!res.ok) return null;
    const data = await res.json();

    return {
      status: data.is_active ? 'published' : 'draft',
      updatedAt: data.updated_at || data.created_at,
      payload: data.config_json || data.schema_json || data,
    };
  } catch (err) {
    return null;
  }
}

async function resolveInternalRoleId(preferredKeys: string[] = ['ADMIN', 'SUPER_ADMIN']): Promise<string | null> {
  try {
    const res = await authedFetch(`${API_GATEWAY}/api/admin/roles?audience=INTERNAL`);
    if (!res.ok) return null;

    const data = await res.json();
    const rows = Array.isArray(data) ? data : (data.roles || []);
    if (!Array.isArray(rows) || rows.length === 0) return null;

    const normalized = rows.map((row: any) => ({
      id: String(row?.id || ''),
      key: String(row?.key || row?.role_key || '').toUpperCase(),
      audience: String(row?.audience || '').toUpperCase(),
      isActive: row?.is_active !== false,
    })).filter((row: any) => row.id);

    for (const pref of preferredKeys) {
      const hit = normalized.find((row: any) => row.key === pref && row.isActive);
      if (hit) return hit.id;
    }

    const firstInternalActive = normalized.find((row: any) => row.audience === 'INTERNAL' && row.isActive);
    if (firstInternalActive) return firstInternalActive.id;

    return normalized[0]?.id || null;
  } catch {
    return null;
  }
}

function normalizeDashboardLayout(input: any): RuntimeDashboardLayout | null {
  if (!input || typeof input !== 'object') return null;

  const rawOrder = Array.isArray(input.order)
    ? input.order.map((value: unknown) => String(value)).filter(Boolean)
    : [];

  const rawVisibility = (input.visibility && typeof input.visibility === 'object') ? input.visibility : {};
  const rawSize = (input.size && typeof input.size === 'object') ? input.size : {};

  const visibility: Record<string, boolean> = {};
  for (const [key, value] of Object.entries(rawVisibility)) {
    visibility[String(key)] = Boolean(value);
  }

  const size: Record<string, 'S' | 'M' | 'L'> = {};
  for (const [key, value] of Object.entries(rawSize)) {
    const normalized = String(value || '').toUpperCase();
    if (normalized === 'S' || normalized === 'M' || normalized === 'L') {
      size[String(key)] = normalized;
    }
  }

  return {
    order: rawOrder,
    visibility,
    size,
  };
}

async function readDashboardConfig(roleId: string, audience: 'INTERNAL' | 'EXTERNAL'): Promise<DashboardConfigResponse | null> {
  try {
    const res = await authedFetch(`${API_GATEWAY}/api/admin/dashboard-config/${roleId}?audience=${audience}`);
    if (!res.ok) return null;
    return await res.json();
  } catch {
    return null;
  }
}

export async function loadAdminDashboardLayout(): Promise<RuntimeDashboardLayout | null> {
  const roleId = await resolveInternalRoleId();
  if (!roleId) return null;

  const data = await readDashboardConfig(roleId, 'INTERNAL');
  const config = data?.config_json;
  return normalizeDashboardLayout((config as any)?.dashboard_layout);
}

export async function saveAdminDashboardLayout(layout: RuntimeDashboardLayout): Promise<boolean> {
  const roleId = await resolveInternalRoleId();
  if (!roleId) return false;

  const existing = await readDashboardConfig(roleId, 'INTERNAL');
  const existingJson = (existing?.config_json && typeof existing.config_json === 'object')
    ? existing.config_json
    : {};

  const config_json = {
    ...existingJson,
    dashboard_layout: {
      order: Array.from(new Set(layout.order.map((item) => String(item)).filter(Boolean))),
      visibility: layout.visibility,
      size: layout.size,
    },
  };

  try {
    const res = await authedFetch(`${API_GATEWAY}/api/admin/dashboard-config`, {
      method: 'POST',
      body: JSON.stringify({
        role_id: roleId,
        audience: 'INTERNAL',
        config_json,
      }),
    });

    return res.ok;
  } catch {
    return false;
  }
}

export async function deleteRuntimeConfig(type: RuntimeRecordType, key: string): Promise<boolean> {
  // Mock delete since hard deletes usually aren't ideal for configs (we just soft-disable them on new save)
  console.log(`Delete requested for ${type} config with key ${key}. In Rust, we just save a new one with is_active=true overriding the old one.`);
  return true;
}
