import type {
	ApiEnvelope,
	ContainerStats,
	Deploy,
	DeployLog,
	DockerHealth,
	ProxyHealth,
	EventLogEntry,
	EventLogStats,
	InspectResult,
	Instance,
	NpmCertificate,
	ProxyRoute,
	Project,
	ProjectDetail,
	Registry,
	RegistryImage,
	Settings,
	StaleContainer,
	Stage,
	StageEnv,
	Volume,
	VolumeScopeInfo,
	BrowseResult,
	DnsZone,
	DnsRecordView,
	BackupInfo
} from './types';

// ── Helpers ─────────────────────────────────────────────────────────

class ApiError extends Error {
	constructor(
		message: string,
		public readonly status: number
	) {
		super(message);
		this.name = 'ApiError';
	}
}

import { getAuthToken, clearAuth } from './auth';

async function request<T>(path: string, init?: RequestInit): Promise<T> {
	const token = getAuthToken();
	const headers: Record<string, string> = {
		'Content-Type': 'application/json',
		...(init?.headers as Record<string, string>)
	};
	if (token) {
		headers['Authorization'] = `Bearer ${token}`;
	}

	const res = await fetch(path, {
		...init,
		headers
	});

	// Redirect to login on 401 (expired/missing token).
	if (res.status === 401 && typeof window !== 'undefined' && !path.includes('/auth/')) {
		clearAuth();
		window.location.href = '/login';
		throw new ApiError('Authentication required', 401);
	}

	let envelope: ApiEnvelope<T>;
	try {
		envelope = await res.json();
	} catch {
		throw new ApiError(
			`Server returned non-JSON response (HTTP ${res.status})`,
			res.status
		);
	}

	if (!envelope.success) {
		throw new ApiError(envelope.error ?? 'Unknown API error', res.status);
	}

	return envelope.data as T;
}

function get<T>(path: string): Promise<T> {
	return request<T>(path);
}

function post<T>(path: string, body?: unknown): Promise<T> {
	return request<T>(path, {
		method: 'POST',
		body: body !== undefined ? JSON.stringify(body) : undefined
	});
}

function put<T>(path: string, body: unknown): Promise<T> {
	return request<T>(path, {
		method: 'PUT',
		body: JSON.stringify(body)
	});
}

function del<T>(path: string): Promise<T> {
	return request<T>(path, { method: 'DELETE' });
}

// ── Projects ────────────────────────────────────────────────────────

export function listProjects(): Promise<Project[]> {
	return get<Project[]>('/api/projects');
}

export function getProject(id: string): Promise<ProjectDetail> {
	return get<ProjectDetail>(`/api/projects/${id}`);
}

export function createProject(data: Partial<Project>): Promise<Project> {
	return post<Project>('/api/projects', data);
}

export function updateProject(id: string, data: Partial<Project>): Promise<Project> {
	return put<Project>(`/api/projects/${id}`, data);
}

export function deleteProject(id: string): Promise<{ deleted: string }> {
	return del<{ deleted: string }>(`/api/projects/${id}`);
}

// ── Stages ─────────────────────────────────────────────────────────

export function createStage(projectId: string, data: Partial<Stage>): Promise<Stage> {
	return post<Stage>(`/api/projects/${projectId}/stages`, data);
}

export function updateStage(projectId: string, stageId: string, data: Partial<Stage>): Promise<Stage> {
	return put<Stage>(`/api/projects/${projectId}/stages/${stageId}`, data);
}

export function deleteStage(projectId: string, stageId: string): Promise<void> {
	return del<void>(`/api/projects/${projectId}/stages/${stageId}`);
}

// ── Instances ───────────────────────────────────────────────────────

export function listInstances(projectId: string, stageId: string): Promise<Instance[]> {
	return get<Instance[]>(`/api/projects/${projectId}/stages/${stageId}/instances`);
}

export function deployInstance(
	projectId: string,
	stageId: string,
	imageTag: string
): Promise<{ status: string }> {
	return post<{ status: string }>(`/api/projects/${projectId}/stages/${stageId}/instances`, {
		image_tag: imageTag
	});
}

export function removeInstance(
	projectId: string,
	stageId: string,
	instanceId: string
): Promise<{ deleted: string }> {
	return del<{ deleted: string }>(
		`/api/projects/${projectId}/stages/${stageId}/instances/${instanceId}`
	);
}

export function stopInstance(
	projectId: string,
	stageId: string,
	instanceId: string
): Promise<{ status: string }> {
	return post<{ status: string }>(
		`/api/projects/${projectId}/stages/${stageId}/instances/${instanceId}/stop`
	);
}

export function startInstance(
	projectId: string,
	stageId: string,
	instanceId: string
): Promise<{ status: string }> {
	return post<{ status: string }>(
		`/api/projects/${projectId}/stages/${stageId}/instances/${instanceId}/start`
	);
}

export function restartInstance(
	projectId: string,
	stageId: string,
	instanceId: string
): Promise<{ status: string }> {
	return post<{ status: string }>(
		`/api/projects/${projectId}/stages/${stageId}/instances/${instanceId}/restart`
	);
}

// ── Deploys ─────────────────────────────────────────────────────────

export function listDeploys(limit = 50): Promise<Deploy[]> {
	return get<Deploy[]>(`/api/deploys?limit=${limit}`);
}

export function getDeployLogs(deployId: string): Promise<DeployLog[]> {
	return get<DeployLog[]>(`/api/deploys/${deployId}/logs`);
}

export function inspectImage(image: string): Promise<InspectResult> {
	return post<InspectResult>('/api/deploy/inspect', { image });
}

export function quickDeploy(data: {
	name?: string;
	image: string;
	tag?: string;
	registry?: string;
	port?: number;
	force?: boolean;
}): Promise<{ project: Project; status: string }> {
	return post<{ project: Project; status: string }>('/api/deploy/quick', data);
}

// ── Registries ──────────────────────────────────────────────────────

export function listRegistries(): Promise<Registry[]> {
	return get<Registry[]>('/api/registries');
}

export function createRegistry(data: Partial<Registry>): Promise<Registry> {
	return post<Registry>('/api/registries', data);
}

export function updateRegistry(id: string, data: Partial<Registry>): Promise<Registry> {
	return put<Registry>(`/api/registries/${id}`, data);
}

export function deleteRegistry(id: string): Promise<{ deleted: string }> {
	return del<{ deleted: string }>(`/api/registries/${id}`);
}

export function testRegistry(id: string): Promise<{ status: string }> {
	return post<{ status: string }>(`/api/registries/${id}/test`);
}

export function listRegistryTags(registryId: string, image: string): Promise<string[]> {
	return get<string[]>(`/api/registries/${registryId}/tags/${encodeURIComponent(image)}`);
}

export function listRegistryImages(registryId: string): Promise<RegistryImage[]> {
	return get<RegistryImage[]>(`/api/registries/${registryId}/images`);
}

// ── Settings ────────────────────────────────────────────────────────

export function getSettings(): Promise<Settings> {
	return get<Settings>('/api/settings');
}

export function updateSettings(data: Partial<Settings>): Promise<Settings> {
	return put<Settings>('/api/settings', data);
}

export function getWebhookUrl(): Promise<{ webhook_url: string }> {
	return get<{ webhook_url: string }>('/api/settings/webhook-url');
}

export function regenerateWebhookUrl(): Promise<{ webhook_url: string }> {
	return post<{ webhook_url: string }>('/api/settings/webhook-url/regenerate');
}

// ── Proxy Routes ───────────────────────────────────────────────────

export function listProxyRoutes(): Promise<ProxyRoute[]> {
	return get<ProxyRoute[]>('/api/proxies');
}

export function testNpmConnection(data: { npm_url?: string; npm_email?: string; npm_password?: string }): Promise<{ status: string }> {
	return post<{ status: string }>('/api/settings/npm/test', data);
}

export function listNpmCertificates(): Promise<NpmCertificate[]> {
	return get<NpmCertificate[]>('/api/settings/npm-certificates');
}

// ── DNS ────────────────────────────────────────────────────────────

export function testDnsConnection(provider: string, token: string, zoneId: string): Promise<{ success: boolean; error?: string }> {
	return post<{ success: boolean; error?: string }>('/api/settings/dns/test', { provider, token, zone_id: zoneId });
}

export function listDnsZones(token?: string): Promise<DnsZone[]> {
	return post<DnsZone[]>('/api/settings/dns/zones', { token: token ?? '' });
}

export function getDnsRecords(): Promise<DnsRecordView[]> {
	return get<DnsRecordView[]>('/api/dns/records');
}

export function syncDnsRecords(): Promise<{ created: number; deleted: number; already_synced: number }> {
	return post<{ created: number; deleted: number; already_synced: number }>('/api/dns/sync');
}

export function deleteDnsRecord(fqdn: string): Promise<void> {
	return del<void>(`/api/dns/records/${encodeURIComponent(fqdn)}`);
}

// ── Backups ────────────────────────────────────────────────────────

export function listBackups(): Promise<BackupInfo[]> {
	return get<BackupInfo[]>('/api/backups');
}

export function triggerBackup(): Promise<BackupInfo> {
	return post<BackupInfo>('/api/backups');
}

export function deleteBackup(id: string): Promise<void> {
	return del<void>(`/api/backups/${id}`);
}

export function restoreBackup(id: string): Promise<{ status: string; message: string }> {
	return post<{ status: string; message: string }>(`/api/backups/${id}/restore`);
}

export function backupDownloadUrl(id: string): string {
	return `/api/backups/${id}/download`;
}

// ── Health ──────────────────────────────────────────────────────────

export function getHealth(): Promise<{ docker: DockerHealth; proxy?: ProxyHealth }> {
	return get<{ docker: DockerHealth; proxy?: ProxyHealth }>('/api/health');
}

// ── Auth ─────────────────────────────────────────────────────────────

export function login(username: string, password: string): Promise<{ token: string; expires_at: string }> {
	return post<{ token: string; expires_at: string }>('/api/auth/login', { username, password });
}

export function getCurrentUser(): Promise<{ id: string; username: string; email: string; role: string }> {
	return get<{ id: string; username: string; email: string; role: string }>('/api/auth/me');
}

// Auth settings
export async function getAuthSettings(): Promise<any> {
	return request<any>('/api/auth/settings');
}

export async function updateAuthSettings(settings: any): Promise<any> {
	return request<any>('/api/auth/settings', {
		method: 'PUT',
		body: JSON.stringify(settings)
	});
}

export async function listUsers(): Promise<any[]> {
	return request<any[]>('/api/auth/users');
}

export async function createUser(data: { username: string; password: string; email?: string; role?: string }): Promise<any> {
	return request<any>('/api/auth/users', {
		method: 'POST',
		body: JSON.stringify(data)
	});
}

export async function updateUser(uid: string, data: { email?: string; role?: string }): Promise<any> {
	return request<any>(`/api/auth/users/${uid}`, {
		method: 'PUT',
		body: JSON.stringify(data)
	});
}

export async function changeUserPassword(uid: string, password: string): Promise<any> {
	return request<any>(`/api/auth/users/${uid}/password`, {
		method: 'PUT',
		body: JSON.stringify({ password })
	});
}

export async function deleteUser(uid: string): Promise<any> {
	return request<any>(`/api/auth/users/${uid}`, { method: 'DELETE' });
}

export async function logout(): Promise<void> {
	await request<any>('/api/auth/logout', { method: 'POST' });
}

// ── Config Export ────────────────────────────────────────────────────

export function exportConfigUrl(): string {
	return '/api/config/export';
}

// ── Stage Env Overrides ──────────────────────────────────────────────

export function listStageEnv(projectId: string, stageId: string): Promise<StageEnv[]> {
	return get<StageEnv[]>(`/api/projects/${projectId}/stages/${stageId}/env`);
}

export function createStageEnv(
	projectId: string,
	stageId: string,
	data: { key: string; value: string; encrypted?: boolean }
): Promise<StageEnv> {
	return post<StageEnv>(`/api/projects/${projectId}/stages/${stageId}/env`, data);
}

export function updateStageEnv(
	projectId: string,
	stageId: string,
	envId: string,
	data: { key?: string; value?: string; encrypted?: boolean }
): Promise<StageEnv> {
	return put<StageEnv>(`/api/projects/${projectId}/stages/${stageId}/env/${envId}`, data);
}

export function deleteStageEnv(
	projectId: string,
	stageId: string,
	envId: string
): Promise<{ deleted: string }> {
	return del<{ deleted: string }>(`/api/projects/${projectId}/stages/${stageId}/env/${envId}`);
}

// ── Volumes ──────────────────────────────────────────────────────────

export function listVolumes(projectId: string): Promise<Volume[]> {
	return get<Volume[]>(`/api/projects/${projectId}/volumes`);
}

export function createVolume(
	projectId: string,
	data: { source: string; target: string; scope: string; name?: string; mode?: string }
): Promise<Volume> {
	return post<Volume>(`/api/projects/${projectId}/volumes`, data);
}

export function updateVolume(
	projectId: string,
	volId: string,
	data: { source?: string; target?: string; scope?: string; name?: string; mode?: string }
): Promise<Volume> {
	return put<Volume>(`/api/projects/${projectId}/volumes/${volId}`, data);
}

export function listVolumeScopes(): Promise<VolumeScopeInfo[]> {
	return get<VolumeScopeInfo[]>('/api/volumes/scopes');
}

export function deleteVolume(
	projectId: string,
	volId: string
): Promise<{ deleted: string }> {
	return del<{ deleted: string }>(`/api/projects/${projectId}/volumes/${volId}`);
}

export function browseVolume(
	projectId: string,
	volId: string,
	params?: { path?: string; stage?: string; tag?: string }
): Promise<BrowseResult> {
	const query = new URLSearchParams();
	if (params?.path) query.set('path', params.path);
	if (params?.stage) query.set('stage', params.stage);
	if (params?.tag) query.set('tag', params.tag);
	const qs = query.toString();
	return get<BrowseResult>(`/api/projects/${projectId}/volumes/${volId}/browse${qs ? `?${qs}` : ''}`);
}

export function volumeDownloadUrl(
	projectId: string,
	volId: string,
	params?: { path?: string; stage?: string; tag?: string }
): string {
	const query = new URLSearchParams();
	if (params?.path) query.set('path', params.path);
	if (params?.stage) query.set('stage', params.stage);
	if (params?.tag) query.set('tag', params.tag);
	const token = typeof localStorage !== 'undefined' ? localStorage.getItem('auth_token') : null;
	if (token) query.set('token', token);
	const qs = query.toString();
	return `/api/projects/${projectId}/volumes/${volId}/download${qs ? `?${qs}` : ''}`;
}

export async function uploadToVolume(
	projectId: string,
	volId: string,
	files: FileList,
	params?: { path?: string; stage?: string; tag?: string }
): Promise<{ uploaded: string[]; count: number }> {
	const query = new URLSearchParams();
	if (params?.path) query.set('path', params.path);
	if (params?.stage) query.set('stage', params.stage);
	if (params?.tag) query.set('tag', params.tag);
	const qs = query.toString();

	const formData = new FormData();
	for (let i = 0; i < files.length; i++) {
		formData.append('files', files[i]);
	}

	const token = typeof localStorage !== 'undefined' ? localStorage.getItem('auth_token') : null;
	const headers: Record<string, string> = {};
	if (token) headers['Authorization'] = `Bearer ${token}`;

	const res = await fetch(`/api/projects/${projectId}/volumes/${volId}/upload${qs ? `?${qs}` : ''}`, {
		method: 'POST',
		headers,
		body: formData,
	});

	const envelope = await res.json();
	if (!envelope.success) throw new Error(envelope.error ?? 'Upload failed');
	return envelope.data;
}

// ── Event Log ───────────────────────────────────────────────────────

export function fetchEventLog(params?: {
	severity?: string;
	source?: string;
	since?: string;
	until?: string;
	limit?: number;
	offset?: number;
}): Promise<EventLogEntry[]> {
	const query = new URLSearchParams();
	if (params?.severity) query.set('severity', params.severity);
	if (params?.source) query.set('source', params.source);
	if (params?.since) query.set('since', params.since);
	if (params?.until) query.set('until', params.until);
	if (params?.limit) query.set('limit', String(params.limit));
	if (params?.offset) query.set('offset', String(params.offset));
	const qs = query.toString();
	return get<EventLogEntry[]>(`/api/events/log${qs ? `?${qs}` : ''}`);
}

export function fetchEventLogStats(): Promise<EventLogStats> {
	return get<EventLogStats>('/api/events/log/stats');
}

// ── Stale Containers ────────────────────────────────────────────────

export function fetchStaleContainers(): Promise<StaleContainer[]> {
	return get<StaleContainer[]>('/api/containers/stale');
}

export function cleanupStaleContainer(id: string): Promise<{ deleted: string }> {
	return post<{ deleted: string }>(`/api/containers/stale/${id}/cleanup`);
}

export function bulkCleanupStaleContainers(): Promise<{ deleted: number }> {
	return post<{ deleted: number }>('/api/containers/stale/cleanup');
}

// ── Container Stats ────────────────────────────────────────────────

export function fetchContainerStats(
	projectId: string,
	stageId: string,
	instanceId: string
): Promise<ContainerStats> {
	return get<ContainerStats>(
		`/api/projects/${projectId}/stages/${stageId}/instances/${instanceId}/stats`
	);
}

export { ApiError };
