import { z } from 'zod';
export { resolveHostFingerprint } from './host-fingerprint';

export const RUNNER_PROTOCOL_CURRENT_VERSION = '1.0.0';
export const RUNNER_PROTOCOL_MINIMUM_VERSION = '1.0.0';
export const RUNNER_MINIMUM_VERSION = '0.1.0';
export const RUNNER_DEFAULT_CAPABILITIES = ['ANDROID'] as const;

export const runnerKindSchema = z.enum(['MACOS_AGENT']);
export const runnerCapabilitySchema = z.enum(['ANDROID']);
export const runnerDevicePlatformSchema = z.enum(['ANDROID']);
export const runnerDeviceStateSchema = z.enum(['ONLINE', 'OFFLINE', 'UNAVAILABLE']);

export const runnerProtocolVersionSchema = z.string().trim().min(1).max(40);
export const runnerVersionSchema = z.string().trim().min(1).max(40);
export const runnerLabelSchema = z.string().trim().min(1).max(120);
export const runnerDisplayIdSchema = z.string().trim().regex(/^[a-z0-9]{6}$/);
export const runnerHostFingerprintSchema = z.string().trim().min(1).max(200);
export const runnerCapabilitiesSchema = z.array(runnerCapabilitySchema).max(8).default([]);

export const compatibilityMetadataSchema = z.object({
    currentProtocolVersion: z.string().min(1),
    minimumSupportedProtocolVersion: z.string().min(1),
    minimumSupportedRunnerVersion: z.string().min(1),
    upgradeRequired: z.boolean(),
});

export const runnerTransportMetadataSchema = z.object({
    heartbeatIntervalSeconds: z.number().int().positive(),
    claimLongPollTimeoutSeconds: z.number().int().positive(),
    deviceSyncIntervalSeconds: z.number().int().positive(),
});
export const RUNNER_DEFAULT_TRANSPORT = runnerTransportMetadataSchema.parse({
    heartbeatIntervalSeconds: 45,
    claimLongPollTimeoutSeconds: 30,
    deviceSyncIntervalSeconds: 45,
});

export const registerRunnerRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    hostFingerprint: runnerHostFingerprintSchema,
    label: runnerLabelSchema,
    kind: runnerKindSchema,
    capabilities: runnerCapabilitiesSchema,
});

export const registerRunnerResponseSchema = z.object({
    runnerId: z.string().min(1),
    compatibility: compatibilityMetadataSchema,
    transport: runnerTransportMetadataSchema,
    credentialExpiresAt: z.string().datetime(),
    rotationRequired: z.boolean(),
});

export const heartbeatRunnerRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    hostFingerprint: runnerHostFingerprintSchema,
});

export const heartbeatRunnerResponseSchema = z.object({
    runnerId: z.string().min(1),
    compatibility: compatibilityMetadataSchema,
    transport: runnerTransportMetadataSchema,
    credentialExpiresAt: z.string().datetime(),
    rotationRequired: z.boolean(),
});

export const verifyRunnerCredentialRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
});

export const verifyRunnerCredentialResponseSchema = z.object({
    runnerId: z.string().min(1),
    compatibility: compatibilityMetadataSchema,
    transport: runnerTransportMetadataSchema,
    credentialExpiresAt: z.string().datetime(),
    rotationRequired: z.boolean(),
});

export const shutdownRunnerRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    reason: z.string().trim().min(1).max(200).optional(),
});

export const shutdownRunnerResponseSchema = z.object({
    runnerId: z.string().min(1),
    status: z.literal('OFFLINE'),
    compatibility: compatibilityMetadataSchema,
    rotationRequired: z.boolean(),
});

export const unpairRunnerRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    reason: z.string().trim().min(1).max(200).optional(),
});

export const unpairRunnerResponseSchema = z.object({
    runnerId: z.string().min(1),
    unpaired: z.literal(true),
    compatibility: compatibilityMetadataSchema,
    rotationRequired: z.boolean(),
});

export const createPairingTokenResponseSchema = z.object({
    token: z.string().min(1),
    expiresAt: z.string().datetime(),
    compatibility: compatibilityMetadataSchema,
    transport: runnerTransportMetadataSchema,
});

export const pairingExchangeRequestSchema = z.object({
    pairingToken: z.string().trim().min(1),
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    hostFingerprint: runnerHostFingerprintSchema,
    displayId: runnerDisplayIdSchema,
    label: runnerLabelSchema,
    kind: runnerKindSchema,
    capabilities: runnerCapabilitiesSchema,
});

export const pairingExchangeResponseSchema = z.object({
    runnerId: z.string().min(1),
    runnerToken: z.string().min(1),
    credentialExpiresAt: z.string().datetime(),
    compatibility: compatibilityMetadataSchema,
    transport: runnerTransportMetadataSchema,
    rotationRequired: z.boolean(),
});

export const deviceSyncItemSchema = z.object({
    deviceId: z.string().trim().min(1).max(120),
    platform: runnerDevicePlatformSchema,
    name: z.string().trim().min(1).max(200),
    state: runnerDeviceStateSchema,
    metadata: z.record(z.string(), z.unknown()).optional(),
});

export const deviceSyncRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    devices: z.array(deviceSyncItemSchema).max(100),
});

export const deviceSyncResponseSchema = z.object({
    runnerId: z.string().min(1),
    syncedAt: z.string().datetime(),
    deviceCount: z.number().int().min(0),
    compatibility: compatibilityMetadataSchema,
    rotationRequired: z.boolean(),
});

export const claimJobSchema = z.object({
    runId: z.string().min(1),
    testCaseId: z.string().min(1),
    requiredCapability: z.string().nullable(),
    requestedDeviceId: z.string().nullable(),
    leaseExpiresAt: z.string().datetime(),
});

export const claimJobRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
});

export const claimJobResponseSchema = z.object({
    compatibility: compatibilityMetadataSchema,
    rotationRequired: z.boolean(),
    job: claimJobSchema.nullable(),
});

export const runnerEventInputSchema = z.object({
    kind: z.string().trim().min(1).max(80),
    message: z.string().trim().max(5000).optional(),
    payload: z.unknown().optional(),
    artifactKey: z.string().trim().max(500).optional(),
});

export const ingestEventsRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    events: z.array(runnerEventInputSchema).min(1).max(200),
});

export const ingestEventsResponseSchema = z.object({
    accepted: z.number().int().min(0),
    nextSequence: z.number().int().min(1),
    compatibility: compatibilityMetadataSchema,
    rotationRequired: z.boolean(),
});

export const uploadArtifactRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    filename: z.string().trim().min(1).max(255),
    mimeType: z.string().trim().min(1).max(120),
    contentBase64: z.string().min(1),
});

export const uploadArtifactResponseSchema = z.object({
    fileId: z.string().min(1),
    artifactKey: z.string().min(1),
    compatibility: compatibilityMetadataSchema,
    rotationRequired: z.boolean(),
});

export const completeRunRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    result: z.string().max(100_000).optional(),
});

export const failRunRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
    error: z.string().trim().min(1).max(10_000),
    result: z.string().max(100_000).optional(),
});

export const runCompletionResponseSchema = z.object({
    runId: z.string().min(1),
    status: z.string().min(1),
    compatibility: compatibilityMetadataSchema,
    rotationRequired: z.boolean(),
});

export const jobDetailsRequestSchema = z.object({
    protocolVersion: runnerProtocolVersionSchema,
    runnerVersion: runnerVersionSchema,
});

export const runFileSchema = z.object({
    id: z.string().min(1),
    filename: z.string().min(1),
    storedName: z.string().min(1),
    mimeType: z.string().min(1),
    size: z.number().int().nonnegative(),
});

export const midsceneModelOptionsSchema = z.object({
    baseUrl: z.string().min(1).optional(),
    mainModel: z.string().min(1).optional(),
    mainModelFamily: z.string().min(1).optional(),
    planningModel: z.string().min(1).optional(),
    planningModelFamily: z.string().min(1).optional(),
    insightModel: z.string().min(1).optional(),
    insightModelFamily: z.string().min(1).optional(),
    temperature: z.number().finite().optional(),
});

export const jobDetailsResponseSchema = z.object({
    runId: z.string().min(1),
    testCaseId: z.string().min(1),
    projectId: z.string().min(1),
    compatibility: compatibilityMetadataSchema,
    rotationRequired: z.boolean(),
    config: z.object({
        url: z.string().optional(),
        prompt: z.string().optional(),
        steps: z.array(z.record(z.string(), z.unknown())).optional(),
        browserConfig: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
        openRouterApiKey: z.string().min(1),
        aiProvider: z.string().min(1).optional(),
        midsceneModelOptions: midsceneModelOptionsSchema.optional(),
        files: z.array(runFileSchema),
        resolvedVariables: z.record(z.string(), z.string()),
        resolvedFiles: z.record(z.string(), z.string()),
    }),
});

export type RunnerKind = z.infer<typeof runnerKindSchema>;
export type RunnerCapability = z.infer<typeof runnerCapabilitySchema>;
export type RegisterRunnerRequest = z.infer<typeof registerRunnerRequestSchema>;
export type RegisterRunnerResponse = z.infer<typeof registerRunnerResponseSchema>;
export type HeartbeatRunnerRequest = z.infer<typeof heartbeatRunnerRequestSchema>;
export type HeartbeatRunnerResponse = z.infer<typeof heartbeatRunnerResponseSchema>;
export type VerifyRunnerCredentialRequest = z.infer<typeof verifyRunnerCredentialRequestSchema>;
export type VerifyRunnerCredentialResponse = z.infer<typeof verifyRunnerCredentialResponseSchema>;
export type ShutdownRunnerRequest = z.infer<typeof shutdownRunnerRequestSchema>;
export type ShutdownRunnerResponse = z.infer<typeof shutdownRunnerResponseSchema>;
export type UnpairRunnerRequest = z.infer<typeof unpairRunnerRequestSchema>;
export type UnpairRunnerResponse = z.infer<typeof unpairRunnerResponseSchema>;
export type CreatePairingTokenResponse = z.infer<typeof createPairingTokenResponseSchema>;
export type PairingExchangeRequest = z.infer<typeof pairingExchangeRequestSchema>;
export type PairingExchangeResponse = z.infer<typeof pairingExchangeResponseSchema>;
export type DeviceSyncItem = z.infer<typeof deviceSyncItemSchema>;
export type DeviceSyncRequest = z.infer<typeof deviceSyncRequestSchema>;
export type DeviceSyncResponse = z.infer<typeof deviceSyncResponseSchema>;
export type ClaimJob = z.infer<typeof claimJobSchema>;
export type ClaimJobRequest = z.infer<typeof claimJobRequestSchema>;
export type ClaimJobResponse = z.infer<typeof claimJobResponseSchema>;
export type RunnerEventInput = z.infer<typeof runnerEventInputSchema>;
export type IngestEventsRequest = z.infer<typeof ingestEventsRequestSchema>;
export type IngestEventsResponse = z.infer<typeof ingestEventsResponseSchema>;
export type UploadArtifactRequest = z.infer<typeof uploadArtifactRequestSchema>;
export type UploadArtifactResponse = z.infer<typeof uploadArtifactResponseSchema>;
export type CompleteRunRequest = z.infer<typeof completeRunRequestSchema>;
export type FailRunRequest = z.infer<typeof failRunRequestSchema>;
export type CompleteRunResponse = z.infer<typeof runCompletionResponseSchema>;
export type JobDetailsRequest = z.infer<typeof jobDetailsRequestSchema>;
export type JobDetailsResponse = z.infer<typeof jobDetailsResponseSchema>;
export type CompatibilityMetadata = z.infer<typeof compatibilityMetadataSchema>;
export type RunnerTransportMetadata = z.infer<typeof runnerTransportMetadataSchema>;
