import type {
	ApiEnvelope,
	Deploy,
	DeployLog,
	InspectResult,
	Instance,
	Project,
	ProjectDetail,
	Registry,
	Settings,
	StageEnv,
	Volume
} from './types';

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

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

function getAuthToken(): string | null {
	if (typeof localStorage !== 'undefined') {
		return localStorage.getItem('auth_token');
	}
	return null;
}

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
	});

	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}`);
}

// ── 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;
}): 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)}`);
}

// ── 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<{ url: string }> {
	return get<{ url: string }>('/api/settings/webhook-url');
}

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

// ── 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');
}

// ── 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; mode?: string }
): Promise<Volume> {
	return post<Volume>(`/api/projects/${projectId}/volumes`, data);
}

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

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

export { ApiError };
