import { z } from 'zod';

/** Per-type connection config. Secret fields are optional so an edit can leave
 *  them blank to keep the stored (encrypted) value; create-time omission is
 *  surfaced by the destination's "Test connection" instead. */
const filesystemConfigSchema = z.object({
  directory: z.string().min(1, 'Directory is required'),
});

const sftpConfigSchema = z.object({
  host: z.string().min(1, 'Host is required'),
  port: z.number().int().positive().max(65535).optional(),
  username: z.string().min(1, 'Username is required'),
  password: z.string().optional(),
  privateKey: z.string().optional(),
  passphrase: z.string().optional(),
  remoteDir: z.string().min(1, 'Remote directory is required'),
  hostFingerprint: z.string().optional(),
});

const s3ConfigSchema = z.object({
  endpoint: z.string().min(1, 'Endpoint is required'),
  region: z.string().optional(),
  bucket: z.string().min(1, 'Bucket is required'),
  accessKey: z.string().min(1, 'Access key is required'),
  secretKey: z.string().optional(),
  prefix: z.string().optional(),
  useSSL: z.boolean().optional(),
  port: z.number().int().positive().max(65535).optional(),
});

const CONFIG_SCHEMA_BY_TYPE = {
  filesystem: filesystemConfigSchema,
  sftp: sftpConfigSchema,
  s3: s3ConfigSchema,
} as const;

export const backupDestinationSchema = z
  .object({
    name: z.string().min(1).max(120),
    type: z.enum(['sftp', 's3', 'filesystem']),
    enabled: z.boolean().optional(),
    config: z.record(z.string(), z.unknown()),
    retentionCount: z.number().int().min(0).max(10000).nullable().optional(),
    encryptBundle: z.boolean().optional(),
    encryptionKey: z.string().optional(),
  })
  .superRefine((val, ctx) => {
    const schema = CONFIG_SCHEMA_BY_TYPE[val.type];
    const result = schema.safeParse(val.config);
    if (!result.success) {
      for (const issue of result.error.issues) {
        ctx.addIssue({ ...issue, path: ['config', ...issue.path] });
      }
    }
    if (val.encryptBundle && !val.encryptionKey) {
      // Allowed on update (keeps existing key); the route/service enforces a key
      // exists before a push. Surfaced here only as a soft hint via no error.
    }
  });

export const backupScheduleSchema = z.object({
  schedule: z.enum(['off', 'daily', 'weekly']),
});

export type BackupDestinationInput = z.infer<typeof backupDestinationSchema>;
