import { S3Client, PutObjectCommand, GetObjectCommand, CopyObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { calculateSHA256, sanitizeFilename } from './crypto';

const region = process.env.AWS_REGION || 'eu-west-3';
const s3Client = new S3Client({ region });

const BUCKET = process.env.ODENTAS_SIGN_BUCKET || 'odentas-sign';

// Préfixes des dossiers dans le bucket
export const S3_PREFIXES = {
  SOURCE: 'source/',
  SIGNED: 'signed/',
  EVIDENCE: 'evidence/',
  SIGNATURES: 'signatures/',
  CERTS: 'certs/',
} as const;

/**
 * Upload un fichier vers S3
 */
export async function uploadToS3(params: {
  key: string;
  body: Buffer | string;
  contentType?: string;
  metadata?: Record<string, string>;
}): Promise<{ key: string; sha256: string }> {
  const { key, body, contentType = 'application/octet-stream', metadata = {} } = params;
  
  const bodyBuffer = Buffer.isBuffer(body) ? body : Buffer.from(body);
  const sha256 = calculateSHA256(bodyBuffer);
  
  await s3Client.send(new PutObjectCommand({
    Bucket: BUCKET,
    Key: key,
    Body: bodyBuffer,
    ContentType: contentType,
    Metadata: {
      ...metadata,
      sha256,
      uploaded_at: new Date().toISOString(),
    },
  }));
  
  console.log(`[S3] ✅ Uploaded: ${key} (${bodyBuffer.length} bytes, SHA256: ${sha256})`);
  
  return { key, sha256 };
}

/**
 * Télécharge un fichier depuis S3
 */
export async function downloadFromS3(key: string): Promise<Buffer> {
  const response = await s3Client.send(new GetObjectCommand({
    Bucket: BUCKET,
    Key: key,
  }));
  
  if (!response.Body) {
    throw new Error(`Fichier introuvable: ${key}`);
  }
  
  const chunks: Uint8Array[] = [];
  for await (const chunk of response.Body as any) {
    chunks.push(chunk);
  }
  
  return Buffer.concat(chunks);
}

/**
 * Génère une URL pré-signée pour télécharger un fichier
 */
export async function getPresignedDownloadUrl(key: string, expiresIn: number = 3600): Promise<string> {
  const command = new GetObjectCommand({
    Bucket: BUCKET,
    Key: key,
  });
  
  return await getSignedUrl(s3Client, command, { expiresIn });
}

/**
 * Upload une image de signature
 */
export async function uploadSignatureImage(params: {
  requestId: string;
  signerId: string;
  imageBase64: string;
}): Promise<string> {
  const { requestId, signerId, imageBase64 } = params;
  
  // Extraire le type MIME et les données
  const matches = imageBase64.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
  if (!matches || matches.length !== 3) {
    throw new Error('Format image base64 invalide');
  }
  
  const mimeType = matches[1];
  const data = matches[2];
  const buffer = Buffer.from(data, 'base64');
  
  // Déterminer l'extension
  const extension = mimeType === 'image/png' ? 'png' : 'jpg';
  
  // Clé S3
  const key = `${S3_PREFIXES.SIGNATURES}${requestId}/${signerId}.${extension}`;
  
  await uploadToS3({
    key,
    body: buffer,
    contentType: mimeType,
    metadata: {
      request_id: requestId,
      signer_id: signerId,
    },
  });
  
  return key;
}

/**
 * Upload le bundle de preuves (evidence)
 */
export async function uploadEvidenceBundle(params: {
  requestRef: string;
  evidence: any;
}): Promise<string> {
  const { requestRef, evidence } = params;
  
  const key = `${S3_PREFIXES.EVIDENCE}${sanitizeFilename(requestRef)}.json`;
  
  await uploadToS3({
    key,
    body: JSON.stringify(evidence, null, 2),
    contentType: 'application/json',
    metadata: {
      request_ref: requestRef,
      created_at: new Date().toISOString(),
    },
  });
  
  return key;
}

/**
 * Copie un fichier vers le dossier d'archivage avec Object Lock
 */
export async function copyToArchive(params: {
  sourceKey: string;
  destinationKey: string;
  retainUntilDate: Date;
}): Promise<void> {
  const { sourceKey, destinationKey, retainUntilDate } = params;
  
  await s3Client.send(new CopyObjectCommand({
    Bucket: BUCKET,
    CopySource: `${BUCKET}/${sourceKey}`,
    Key: destinationKey,
    ObjectLockMode: 'COMPLIANCE',
    ObjectLockRetainUntilDate: retainUntilDate,
    Metadata: {
      archived_at: new Date().toISOString(),
      retain_until: retainUntilDate.toISOString(),
    },
  }));
  
  console.log(`[S3] 🔒 Archived with Object Lock: ${destinationKey} (retain until ${retainUntilDate.toISOString()})`);
}

/**
 * Vérifie si un fichier existe dans S3
 */
export async function fileExistsInS3(key: string): Promise<boolean> {
  try {
    await s3Client.send(new GetObjectCommand({
      Bucket: BUCKET,
      Key: key,
    }));
    return true;
  } catch (error: any) {
    if (error.name === 'NoSuchKey' || error.$metadata?.httpStatusCode === 404) {
      return false;
    }
    throw error;
  }
}
