import type { AvProjectV1, LegacyAvProjectV0 } from '../project.types';
import { migrateV0ToV1, type ProjectMigrationWarning } from './v0-to-v1';

export { migrateV0ToV1 };
export type { ProjectMigrationWarning };

export interface ProjectMigrationResult {
  project: AvProjectV1;
  fromVersion: 0 | 1;
  toVersion: 1;
  warnings: ProjectMigrationWarning[];
}

export class ProjectMigrationError extends Error {
  readonly schemaVersion: unknown;

  constructor(message: string, schemaVersion: unknown) {
    super(message);
    this.name = 'ProjectMigrationError';
    this.schemaVersion = schemaVersion;
  }
}

export function migrateProjectDocument(value: unknown): ProjectMigrationResult {
  if (!isRecord(value)) {
    throw new ProjectMigrationError(
      'A project document must be a JSON object.',
      undefined
    );
  }
  if (value.schemaVersion === 1) {
    let project: AvProjectV1;
    try {
      project = structuredClone(value) as unknown as AvProjectV1;
    } catch {
      throw new ProjectMigrationError(
        'The project contains a value that cannot be migrated.',
        value.schemaVersion
      );
    }
    return {
      project,
      fromVersion: 1,
      toVersion: 1,
      warnings: [],
    };
  }
  if (value.schemaVersion === 0) {
    const migration = migrateV0ToV1(value as unknown as LegacyAvProjectV0);
    return {
      project: migration.project,
      fromVersion: 0,
      toVersion: 1,
      warnings: migration.warnings,
    };
  }
  throw new ProjectMigrationError(
    value.schemaVersion === undefined
      ? 'The project document does not declare a schema version.'
      : `Project schema version ${String(value.schemaVersion)} is not supported.`,
    value.schemaVersion
  );
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}
