import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const region = process.env.AWS_REGION || 'eu-central-1';
const accessKeyId = process.env.AWS_ACCESS_KEY_ID || 'AKIAYWGKACVKBFHPAXMU';
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY || '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';
const bucketName = process.env.S3_BUCKET_NAME || 'apkagermanschool';

export function getS3Client(): S3Client {
  if (!accessKeyId || !secretAccessKey) {
    throw new Error('AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) are not set in environment variables.');
  }

  return new S3Client({
    region,
    forcePathStyle: true,
    credentials: {
      accessKeyId,
      secretAccessKey,
    },
  });
}

export interface PresignedUrlResult {
  uploadUrl: string;
  fileKey: string;
  fileUrl: string;
}

/**
 * Generates a secure PutObject presigned URL for direct S3 client upload.
 * @param filename Original filename
 * @param fileType MIME type (e.g. video/mp4)
 * @param category Folder prefix in S3 (e.g. 'lessons-videos')
 * @returns {PresignedUrlResult}
 */
export async function generateUploadPresignedUrl(
  filename: string,
  fileType: string,
  category: string = 'lessons-videos'
): Promise<PresignedUrlResult> {
  const s3 = getS3Client();

  // Sanitize filename and create unique timestamped key
  const sanitizedFilename = filename.replace(/[^a-zA-Z0-9.-]/g, '_');
  const uniquePrefix = `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
  const fileKey = `${category}/${uniquePrefix}-${sanitizedFilename}`;

  const command = new PutObjectCommand({
    Bucket: bucketName,
    Key: fileKey,
    ContentType: fileType,
  });

  // Presigned URL valid for 15 minutes (900 seconds)
  const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 900 });

  const fileUrl = `https://${bucketName}.s3.${region}.amazonaws.com/${fileKey}`;

  return {
    uploadUrl,
    fileKey,
    fileUrl,
  };
}

/**
 * Normalizes and extracts clean S3 object key from full URLs, leading slashes, or direct keys.
 */
export function cleanS3Key(inputKey: string): string {
  if (!inputKey) return '';
  let key = inputKey.trim();

  // If it's a full URL
  if (key.startsWith('http://') || key.startsWith('https://')) {
    try {
      const parsedUrl = new URL(key);
      key = parsedUrl.pathname;
    } catch {
      // Ignore URL parse error
    }
  }

  // Strip leading slash
  if (key.startsWith('/')) {
    key = key.substring(1);
  }

  return key;
}

/**
 * Generates a temporary time-limited GetObject presigned URL for secure video streaming.
 * Keeps the S3 bucket private so raw video files cannot be downloaded or accessed publicly.
 * @param fileKey S3 object key or full S3 URL
 * @param expiresInSeconds Duration in seconds (default 2 hours / 7200 seconds)
 */
export async function generateSecureStreamPresignedUrl(
  fileKey: string,
  expiresInSeconds: number = 7200
): Promise<string> {
  // If non-S3 external URL (e.g. YouTube/Vimeo/Cloudflare/local http), return as is
  if (
    (fileKey.startsWith('http://') || fileKey.startsWith('https://')) &&
    !fileKey.includes('s3.') &&
    !fileKey.includes('amazonaws.com')
  ) {
    return fileKey;
  }

  const s3 = getS3Client();
  const normalizedKey = cleanS3Key(fileKey);
  let targetKey = normalizedKey;

  // If bare filename without folder prefix (e.g. "1790237597962.mp4"), prepend default "lessons-videos/"
  if (!targetKey.includes('/')) {
    targetKey = `lessons-videos/${targetKey}`;
  }

  const command = new GetObjectCommand({
    Bucket: bucketName,
    Key: targetKey,
  });

  // Pure offline HMAC presigned URL generation (no AWS network call needed, avoiding 403 HeadObject IAM errors)
  return getSignedUrl(s3, command, { expiresIn: expiresInSeconds });
}

/**
 * Checks if a file exists in the S3 bucket using HeadObject.
 * @param fileKey S3 object key
 * @returns File metadata if exists, null if not found
 */
export async function verifyS3FileExists(fileKey: string) {
  try {
    const s3 = getS3Client();
    const command = new HeadObjectCommand({
      Bucket: bucketName,
      Key: fileKey,
    });

    const response = await s3.send(command);
    return {
      exists: true,
      contentLengthBytes: response.ContentLength || 0,
      contentType: response.ContentType || 'unknown',
      lastModified: response.LastModified,
      bucket: bucketName,
      fileKey,
    };
  } catch (error: any) {
    if (
      error.name === 'NotFound' ||
      error.name === 'AccessDenied' ||
      error.$metadata?.httpStatusCode === 404 ||
      error.$metadata?.httpStatusCode === 403
    ) {
      return { exists: false, bucket: bucketName, fileKey };
    }
    return { exists: false, bucket: bucketName, fileKey, error: error.message };
  }
}
