import { db } from '../db/client.js';
import { ensureSchema } from '../db/schema.js';
import { buildBirdPhotoObjectKey, getImageExtensionFromContentType, getS3ImageStorageConfig } from '../storage/imageStorageConfig.js';
import { deleteS3Object, putS3Object } from '../storage/s3Client.js';

type BirdPhotoMigrationRow = {
  id: string;
  workspace_id: number;
  name: string;
  photo_data_url: string;
};

const parseDataImage = (dataUrl: string) => {
  const match = /^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/.exec(dataUrl);
  if (!match) {
    return null;
  }

  return {
    contentType: match[1],
    content: Buffer.from(match[2], 'base64'),
  };
};

const getArgValue = (name: string) => {
  const prefix = `${name}=`;
  const match = process.argv.find((arg) => arg.startsWith(prefix));
  return match ? match.slice(prefix.length) : null;
};

const dryRun = !process.argv.includes('--apply');
const keepDataUrl = process.argv.includes('--keep-data-url');
const limitArg = getArgValue('--limit');
const limit = limitArg ? Number(limitArg) : null;

if (limit !== null && (!Number.isInteger(limit) || limit <= 0)) {
  console.error('Invalid --limit value. Use a positive integer.');
  process.exit(2);
}

const run = async () => {
  await ensureSchema();

  const s3Config = getS3ImageStorageConfig();

  if (!s3Config) {
    throw new Error('S3 image storage is not fully configured. Set IMAGE_STORAGE_PROVIDER=s3 and the S3_* environment variables.');
  }

  const result = await db.query<BirdPhotoMigrationRow>(
    `SELECT id, workspace_id, name, photo_data_url
     FROM birds
     WHERE photo_object_key IS NULL
       AND photo_data_url LIKE 'data:image/%'
     ORDER BY created_at ASC
     ${limit ? 'LIMIT $1' : ''}`,
    limit ? [limit] : undefined,
  );

  if (dryRun) {
    console.log(`Dry run: ${result.rows.length} bird photo(s) would be migrated to bucket ${s3Config.bucket}.`);
    console.log('Run with --apply to upload objects and update rows.');
    return;
  }

  let migrated = 0;
  let skipped = 0;
  let failed = 0;

  for (const bird of result.rows) {
    const parsedImage = parseDataImage(bird.photo_data_url);

    if (!parsedImage) {
      skipped += 1;
      console.warn(`Skipping bird ${bird.id} (${bird.name}): invalid data URL.`);
      continue;
    }

    const objectKey = buildBirdPhotoObjectKey({
      workspaceId: bird.workspace_id,
      birdId: bird.id,
      extension: getImageExtensionFromContentType(parsedImage.contentType),
    });

    try {
      await putS3Object({
        config: s3Config,
        objectKey,
        content: parsedImage.content,
        contentType: parsedImage.contentType,
      });

      const updateResult = await db.query(
        `UPDATE birds
         SET photo_object_key = $2,
             photo_content_type = $3,
             photo_updated_at = CURRENT_TIMESTAMP,
             photo_data_url = CASE WHEN $4::boolean THEN photo_data_url ELSE NULL END
         WHERE id = $1
           AND photo_object_key IS NULL
           AND photo_data_url LIKE 'data:image/%'`,
        [bird.id, objectKey, parsedImage.contentType, keepDataUrl],
      );

      if (updateResult.rowCount !== 1) {
        await deleteS3Object({ config: s3Config, objectKey });
        skipped += 1;
        console.warn(`Skipped bird ${bird.id} (${bird.name}): row changed before update.`);
        continue;
      }

      migrated += 1;
      console.log(`Migrated bird ${bird.id} (${bird.name}) -> ${objectKey}`);
    } catch (error) {
      failed += 1;
      console.error(`Failed to migrate bird ${bird.id} (${bird.name}):`, error);
    }
  }

  console.log(`Migration complete: migrated=${migrated}, skipped=${skipped}, failed=${failed}`);

  if (failed > 0) {
    process.exitCode = 1;
  }
};

run()
  .catch((error) => {
    console.error('Bird photo migration failed:', error);
    process.exitCode = 1;
  })
  .finally(async () => {
    await db.close();
  });
