import { type } from "arktype";
import { describeRoute, resolver } from "hono-openapi";
import {
	COMPRESSION_MODES,
	OVERWRITE_MODES,
	REPOSITORY_BACKENDS,
	REPOSITORY_STATUS,
	repositoryConfigSchema,
	doctorResultSchema,
} from "~/schemas/restic";
import { resticSnapshotSummarySchema, resticStatsSchema } from "~/schemas/restic-dto";

export const repositorySchema = type({
	id: "string",
	shortId: "string",
	name: "string",
	type: type.valueOf(REPOSITORY_BACKENDS),
	config: repositoryConfigSchema,
	compressionMode: type.valueOf(COMPRESSION_MODES).or("null"),
	status: type.valueOf(REPOSITORY_STATUS).or("null"),
	lastChecked: "number | null",
	lastError: "string | null",
	doctorResult: doctorResultSchema.or("null"),
	createdAt: "number",
	updatedAt: "number",
});

export type RepositoryDto = typeof repositorySchema.infer;

/**
 * List all repositories
 */
export const listRepositoriesResponse = repositorySchema.array();
export type ListRepositoriesDto = typeof listRepositoriesResponse.infer;

export const listRepositoriesDto = describeRoute({
	description: "List all repositories",
	tags: ["Repositories"],
	operationId: "listRepositories",
	responses: {
		200: {
			description: "List of repositories",
			content: {
				"application/json": {
					schema: resolver(listRepositoriesResponse),
				},
			},
		},
	},
});

/**
 * Create a new repository
 */
export const createRepositoryBody = type({
	name: "string",
	compressionMode: type.valueOf(COMPRESSION_MODES).optional(),
	config: repositoryConfigSchema,
});

export type CreateRepositoryBody = typeof createRepositoryBody.infer;

export const createRepositoryResponse = type({
	message: "string",
	repository: type({
		id: "string",
		shortId: "string",
		name: "string",
	}),
});

export type CreateRepositoryDto = typeof createRepositoryResponse.infer;

export const createRepositoryDto = describeRoute({
	description: "Create a new restic repository",
	operationId: "createRepository",
	tags: ["Repositories"],
	responses: {
		201: {
			description: "Repository created successfully",
			content: {
				"application/json": {
					schema: resolver(createRepositoryResponse),
				},
			},
		},
	},
});

/**
 * Get a single repository
 */
export const getRepositoryResponse = repositorySchema;
export type GetRepositoryDto = typeof getRepositoryResponse.infer;

export const getRepositoryDto = describeRoute({
	description: "Get a single repository by ID",
	tags: ["Repositories"],
	operationId: "getRepository",
	responses: {
		200: {
			description: "Repository details",
			content: {
				"application/json": {
					schema: resolver(getRepositoryResponse),
				},
			},
		},
	},
});

/**
 * Get repository stats
 */
export const repositoryStatsSchema = resticStatsSchema;
export const getRepositoryStatsResponse = repositoryStatsSchema;
export type GetRepositoryStatsDto = typeof getRepositoryStatsResponse.infer;

export const getRepositoryStatsDto = describeRoute({
	description: "Get repository storage and compression statistics",
	tags: ["Repositories"],
	operationId: "getRepositoryStats",
	responses: {
		200: {
			description: "Repository statistics",
			content: {
				"application/json": {
					schema: resolver(getRepositoryStatsResponse),
				},
			},
		},
	},
});

/**
 * Delete a repository
 */
export const deleteRepositoryResponse = type({
	message: "string",
});

export type DeleteRepositoryDto = typeof deleteRepositoryResponse.infer;

export const deleteRepositoryDto = describeRoute({
	description: "Delete a repository",
	tags: ["Repositories"],
	operationId: "deleteRepository",
	responses: {
		200: {
			description: "Repository deleted successfully",
			content: {
				"application/json": {
					schema: resolver(deleteRepositoryResponse),
				},
			},
		},
	},
});

/**
 * Update a repository
 */
export const updateRepositoryBody = type({
	name: "string?",
	compressionMode: type.valueOf(COMPRESSION_MODES).optional(),
	config: repositoryConfigSchema.optional(),
});

export type UpdateRepositoryBody = typeof updateRepositoryBody.infer;

export const updateRepositoryResponse = repositorySchema;
export type UpdateRepositoryDto = typeof updateRepositoryResponse.infer;

export const updateRepositoryDto = describeRoute({
	description: "Update a repository's name or settings",
	tags: ["Repositories"],
	operationId: "updateRepository",
	responses: {
		200: {
			description: "Repository updated successfully",
			content: {
				"application/json": {
					schema: resolver(updateRepositoryResponse),
				},
			},
		},
		400: {
			description: "Invalid repository update payload",
		},
		404: {
			description: "Repository not found",
		},
		409: {
			description: "Repository with this name already exists",
		},
	},
});

/**
 * List snapshots in a repository
 */
export const snapshotSchema = type({
	short_id: "string",
	time: "number",
	paths: "string[]",
	size: "number",
	duration: "number",
	tags: "string[]",
	retentionCategories: "string[]",
	hostname: "string?",
	summary: resticSnapshotSummarySchema.optional(),
});

const listSnapshotsResponse = snapshotSchema.array();

export type ListSnapshotsDto = typeof listSnapshotsResponse.infer;

export const listSnapshotsFilters = type({
	backupId: "string?",
});

export const listSnapshotsDto = describeRoute({
	description: "List all snapshots in a repository",
	tags: ["Repositories"],
	operationId: "listSnapshots",
	responses: {
		200: {
			description: "List of snapshots",
			content: {
				"application/json": {
					schema: resolver(listSnapshotsResponse),
				},
			},
		},
	},
});

/**
 * Get snapshot details
 */
export const getSnapshotDetailsResponse = snapshotSchema;

export type GetSnapshotDetailsDto = typeof getSnapshotDetailsResponse.infer;

export const getSnapshotDetailsDto = describeRoute({
	description: "Get details of a specific snapshot",
	tags: ["Repositories"],
	operationId: "getSnapshotDetails",
	responses: {
		200: {
			description: "Snapshot details",
			content: {
				"application/json": {
					schema: resolver(getSnapshotDetailsResponse),
				},
			},
		},
	},
});

/**
 * List files in a snapshot
 */
export const snapshotFileNodeSchema = type({
	name: "string",
	type: "string",
	path: "string",
	uid: "number?",
	gid: "number?",
	size: "number?",
	mode: "number?",
	mtime: "string?",
	atime: "string?",
	ctime: "string?",
});

export const listSnapshotFilesResponse = type({
	snapshot: type({
		id: "string",
		short_id: "string",
		time: "string",
		hostname: "string",
		paths: "string[]",
	}),
	files: snapshotFileNodeSchema.array(),
	offset: "number",
	limit: "number",
	total: "number",
	hasMore: "boolean",
});

export type ListSnapshotFilesDto = typeof listSnapshotFilesResponse.infer;

export const listSnapshotFilesQuery = type({
	path: "string?",
	offset: "string.integer?",
	limit: "string.integer?",
});

export const listSnapshotFilesDto = describeRoute({
	description: "List files and directories in a snapshot",
	tags: ["Repositories"],
	operationId: "listSnapshotFiles",
	responses: {
		200: {
			description: "List of files and directories in the snapshot",
			content: {
				"application/json": {
					schema: resolver(listSnapshotFilesResponse),
				},
			},
		},
	},
});

const DUMP_PATH_KINDS = {
	file: "file",
	dir: "dir",
} as const;

export const dumpPathKindSchema = type.valueOf(DUMP_PATH_KINDS);
export type DumpPathKind = typeof dumpPathKindSchema.infer;

/**
 * Download snapshot paths as tar archives (folders) or raw file streams (single files)
 */
export const dumpSnapshotQuery = type({
	path: "string?",
	kind: dumpPathKindSchema.optional(),
});

export const dumpSnapshotDto = describeRoute({
	description: "Download a snapshot path as a tar archive (folders) or raw file stream (single files)",
	tags: ["Repositories"],
	operationId: "dumpSnapshot",
	responses: {
		200: {
			description: "Snapshot content stream",
			content: {
				"application/x-tar": {
					schema: { type: "string", format: "binary" },
				},
				"application/octet-stream": {
					schema: { type: "string", format: "binary" },
				},
			},
		},
	},
});

/**
 * Restore a snapshot
 */
export const overwriteModeSchema = type.valueOf(OVERWRITE_MODES);

export const restoreSnapshotBody = type({
	snapshotId: "string",
	include: "string[]?",
	exclude: "string[]?",
	excludeXattr: "string[]?",
	delete: "boolean?",
	targetPath: "string?",
	overwrite: overwriteModeSchema.optional(),
});

export type RestoreSnapshotBody = typeof restoreSnapshotBody.infer;

export const restoreSnapshotResponse = type({
	success: "boolean",
	message: "string",
	filesRestored: "number",
	filesSkipped: "number",
});

export type RestoreSnapshotDto = typeof restoreSnapshotResponse.infer;

export const restoreSnapshotDto = describeRoute({
	description: "Restore a snapshot to a target path on the filesystem",
	tags: ["Repositories"],
	operationId: "restoreSnapshot",
	responses: {
		200: {
			description: "Snapshot restored successfully",
			content: {
				"application/json": {
					schema: resolver(restoreSnapshotResponse),
				},
			},
		},
	},
});

/**
 * Start doctor operation
 */
export const startDoctorResponse = type({
	message: "string",
	repositoryId: "string",
});

export type StartDoctorDto = typeof startDoctorResponse.infer;

export const startDoctorDto = describeRoute({
	description:
		"Start an asynchronous doctor operation on a repository to fix common issues (unlock, check, repair index). The operation runs in the background and sends results via SSE events.",
	tags: ["Repositories"],
	operationId: "startDoctor",
	responses: {
		202: {
			description: "Doctor operation started",
			content: {
				"application/json": {
					schema: resolver(startDoctorResponse),
				},
			},
		},
		409: {
			description: "Doctor operation already in progress",
		},
	},
});

/**
 * Cancel running doctor operation
 */
export const cancelDoctorResponse = type({
	message: "string",
});

export type CancelDoctorDto = typeof cancelDoctorResponse.infer;

export const cancelDoctorDto = describeRoute({
	description: "Cancel a running doctor operation on a repository",
	tags: ["Repositories"],
	operationId: "cancelDoctor",
	responses: {
		200: {
			description: "Doctor operation cancelled",
			content: {
				"application/json": {
					schema: resolver(cancelDoctorResponse),
				},
			},
		},
		409: {
			description: "No doctor operation is currently running",
		},
	},
});

/**
 * List rclone available remotes
 */
const rcloneRemoteSchema = type({
	name: "string",
	type: "string",
});

const listRcloneRemotesResponse = rcloneRemoteSchema.array();

export const listRcloneRemotesDto = describeRoute({
	description: "List all configured rclone remotes on the host system",
	tags: ["Rclone"],
	operationId: "listRcloneRemotes",
	responses: {
		200: {
			description: "List of rclone remotes",
			content: {
				"application/json": {
					schema: resolver(listRcloneRemotesResponse),
				},
			},
		},
	},
});

/**
 * Delete a snapshot
 */
export const deleteSnapshotResponse = type({
	message: "string",
});

export type DeleteSnapshotDto = typeof deleteSnapshotResponse.infer;

export const deleteSnapshotDto = describeRoute({
	description: "Delete a specific snapshot from a repository",
	tags: ["Repositories"],
	operationId: "deleteSnapshot",
	responses: {
		200: {
			description: "Snapshot deleted successfully",
			content: {
				"application/json": {
					schema: resolver(deleteSnapshotResponse),
				},
			},
		},
	},
});

/**
 * Delete multiple snapshots
 */
export const deleteSnapshotsBody = type({
	snapshotIds: "string[]>=1",
});

export const deleteSnapshotsResponse = type({
	message: "string",
});

export type DeleteSnapshotsResponseDto = typeof deleteSnapshotsResponse.infer;

export const deleteSnapshotsDto = describeRoute({
	description: "Delete multiple snapshots from a repository",
	tags: ["Repositories"],
	operationId: "deleteSnapshots",
	responses: {
		200: {
			description: "Snapshots deleted successfully",
			content: {
				"application/json": {
					schema: resolver(deleteSnapshotsResponse),
				},
			},
		},
	},
});

/**
 * Tag multiple snapshots
 */
export const tagSnapshotsBody = type({
	snapshotIds: "string[]>=1",
	add: "string[]?",
	remove: "string[]?",
	set: "string[]?",
});

export const tagSnapshotsResponse = type({
	message: "string",
});

export type TagSnapshotsResponseDto = typeof tagSnapshotsResponse.infer;

export const tagSnapshotsDto = describeRoute({
	description: "Tag multiple snapshots in a repository",
	tags: ["Repositories"],
	operationId: "tagSnapshots",
	responses: {
		200: {
			description: "Snapshots tagged successfully",
			content: {
				"application/json": {
					schema: resolver(tagSnapshotsResponse),
				},
			},
		},
	},
});

/**
 * Refresh snapshots cache
 */
export const refreshSnapshotsResponse = type({
	message: "string",
	count: "number",
});

export type RefreshSnapshotsDto = typeof refreshSnapshotsResponse.infer;

export const refreshSnapshotsDto = describeRoute({
	description: "Clear snapshot cache and force refresh from repository",
	tags: ["Repositories"],
	operationId: "refreshSnapshots",
	responses: {
		200: {
			description: "Snapshot cache cleared and refreshed",
			content: {
				"application/json": {
					schema: resolver(refreshSnapshotsResponse),
				},
			},
		},
	},
});

export const devPanelExecBody = type({
	command: "string",
	args: "string[]?",
});

export type DevPanelExecBody = typeof devPanelExecBody.infer;

export const devPanelExecDto = describeRoute({
	description: "Execute a restic command against a repository (dev panel only)",
	tags: ["Repositories"],
	operationId: "devPanelExec",
	responses: {
		200: {
			description: "Command output stream (SSE)",
			content: {
				"text/event-stream": {
					schema: { type: "string" },
				},
			},
		},
		403: {
			description: "Dev panel not enabled",
		},
	},
});

/**
 * Unlock repository
 */
export const unlockRepositoryResponse = type({
	success: "boolean",
	message: "string",
});

export type UnlockRepositoryDto = typeof unlockRepositoryResponse.infer;

export const unlockRepositoryDto = describeRoute({
	description: "Unlock a repository by removing all stale locks",
	tags: ["Repositories"],
	operationId: "unlockRepository",
	responses: {
		200: {
			description: "Repository unlocked successfully",
			content: {
				"application/json": {
					schema: resolver(unlockRepositoryResponse),
				},
			},
		},
	},
});
