#!/usr/bin/env node
/**
 * Generates VaultVersions and VaultSql for Swift and Kotlin from TypeScript source.
 * This allows native iOS and Android code to create vaults with the correct SQL schema.
 */

const fs = require('fs');
const path = require('path');

// Paths
const REPO_ROOT = path.join(__dirname, '../../..');
const TS_VERSIONS_SOURCE = path.join(REPO_ROOT, 'core/vault/src/sql/VaultVersions.ts');
const TS_SQL_SOURCE = path.join(REPO_ROOT, 'core/vault/src/sql/SqlConstants.ts');

// Output paths
// Note: VaultModels is a framework that can be imported by other targets (including AliasVaultUITests)
const SWIFT_OUTPUT_DIR = path.join(REPO_ROOT, 'apps/mobile-app/ios/VaultModels');
const KOTLIN_OUTPUT_DIR = path.join(REPO_ROOT, 'apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/models');

/**
 * Parse VaultVersions from TypeScript source
 */
function parseVaultVersions(tsContent) {
  const versions = [];

  // Find the VAULT_VERSIONS array
  const match = tsContent.match(/export const VAULT_VERSIONS[^=]*=\s*\[([\s\S]*?)\];/);
  if (!match) {
    throw new Error('Could not find VAULT_VERSIONS in source');
  }

  const body = match[1];

  // Match each version object
  const versionRegex = /\{\s*revision:\s*(\d+),\s*version:\s*'([^']+)',\s*description:\s*'([^']+)',\s*releaseVersion:\s*'([^']+)',\s*compatibleUpToVersion:\s*'([^']+)',?\s*\}/g;
  let versionMatch;

  while ((versionMatch = versionRegex.exec(body)) !== null) {
    versions.push({
      revision: parseInt(versionMatch[1], 10),
      version: versionMatch[2],
      description: versionMatch[3],
      releaseVersion: versionMatch[4],
      compatibleUpToVersion: versionMatch[5]
    });
  }

  return versions;
}

/**
 * Parse COMPLETE_SCHEMA_SQL from TypeScript source
 */
function parseCompleteSchemaSql(tsContent) {
  // Find the COMPLETE_SCHEMA_SQL constant
  const match = tsContent.match(/export const COMPLETE_SCHEMA_SQL\s*=\s*`([\s\S]*?)`;/);
  if (!match) {
    throw new Error('Could not find COMPLETE_SCHEMA_SQL in source');
  }

  return match[1].trim();
}

/**
 * Parse MIGRATION_SCRIPTS from TypeScript source
 */
function parseMigrationScripts(tsContent) {
  const migrations = {};

  // Find the MIGRATION_SCRIPTS object
  const match = tsContent.match(/export const MIGRATION_SCRIPTS:\s*Record<number,\s*string>\s*=\s*\{([\s\S]*?)\n\};/);
  if (!match) {
    // MIGRATION_SCRIPTS might be optional or have different format
    console.log('Note: MIGRATION_SCRIPTS not found or has different format');
    return migrations;
  }

  const body = match[1];

  // Match each migration: number: `sql`
  const migrationRegex = /(\d+):\s*`([\s\S]*?)`/g;
  let migrationMatch;

  while ((migrationMatch = migrationRegex.exec(body)) !== null) {
    migrations[parseInt(migrationMatch[1], 10)] = migrationMatch[2].trim();
  }

  return migrations;
}

/**
 * Escape string for Swift multiline string literal
 */
function escapeSwiftString(str) {
  // Swift multiline strings handle most escaping, but we need to escape backslashes and quotes
  return str
    .replace(/\\/g, '\\\\')
    .replace(/"/g, '\\"');
}

/**
 * Escape string for Kotlin multiline string literal (trimIndent)
 */
function escapeKotlinString(str) {
  // Kotlin raw strings don't need much escaping, but $ needs escaping
  return str.replace(/\$/g, '\\$');
}

/**
 * Generate Swift VaultVersions struct
 */
function generateSwiftVaultVersions(versions) {
  const latestVersion = versions[versions.length - 1];

  const versionsArray = versions.map(v => {
    return `        VaultVersion(
            revision: ${v.revision},
            version: "${v.version}",
            description: "${v.description}",
            releaseVersion: "${v.releaseVersion}",
            compatibleUpToVersion: "${v.compatibleUpToVersion}"
        )`;
  }).join(',\n');

  return `// <auto-generated />
// This file is auto-generated from core/vault/src/sql/VaultVersions.ts
// Do not edit this file directly. Run './build.sh' in core/vault to regenerate.
// swiftlint:disable all

import Foundation

/// Vault database version information.
public struct VaultVersion {
    /// The migration revision number.
    public let revision: Int

    /// The internal migration version number that equals the AliasClientDb database version (e.g., "1.5.0").
    public let version: String

    /// Description of changes in this version.
    public let description: String

    /// The AliasVault release that this vault version was introduced in (e.g., "0.14.0").
    public let releaseVersion: String

    /// The last AliasVault release version that the vault was compatible with before requiring this migration.
    public let compatibleUpToVersion: String
}

/// Registry of all vault versions.
public struct VaultVersions {
    /// All vault migrations/versions in chronological order.
    public static let all: [VaultVersion] = [
${versionsArray}
    ]

    /// Get the latest vault version.
    public static var latest: VaultVersion {
        return all.last!
    }

    /// Get the latest vault version string (e.g., "1.7.0").
    public static var latestVersion: String {
        return latest.version
    }

    /// Get the latest vault revision number.
    public static var latestRevision: Int {
        return latest.revision
    }

    /// Find a vault version by revision number.
    public static func findByRevision(_ revision: Int) -> VaultVersion? {
        return all.first { $0.revision == revision }
    }

    /// Find a vault version by version string.
    public static func findByVersion(_ version: String) -> VaultVersion? {
        return all.first { $0.version == version }
    }
}

// swiftlint:enable all
`;
}

/**
 * Generate Kotlin VaultVersions object
 */
function generateKotlinVaultVersions(versions) {
  const versionsArray = versions.map(v => {
    return `        VaultVersion(
            revision = ${v.revision},
            version = "${v.version}",
            description = "${v.description}",
            releaseVersion = "${v.releaseVersion}",
            compatibleUpToVersion = "${v.compatibleUpToVersion}",
        )`;
  }).join(',\n');

  return `// <auto-generated />
// This file is auto-generated from core/vault/src/sql/VaultVersions.ts
// Do not edit this file directly. Run './build.sh' in core/vault to regenerate.
@file:Suppress("all")

package net.aliasvault.app.vaultstore.models

/**
 * Vault database version information.
 */
data class VaultVersion(
    /** The migration revision number. */
    val revision: Int,
    /** The internal migration version number that equals the AliasClientDb database version (e.g., "1.5.0"). */
    val version: String,
    /** Description of changes in this version. */
    val description: String,
    /** The AliasVault release that this vault version was introduced in (e.g., "0.14.0"). */
    val releaseVersion: String,
    /** The last AliasVault release version that the vault was compatible with before requiring this migration. */
    val compatibleUpToVersion: String,
)

/**
 * Registry of all vault versions.
 */
object VaultVersions {
    /**
     * All vault migrations/versions in chronological order.
     */
    val all: List<VaultVersion> = listOf(
${versionsArray},
    )

    /**
     * Get the latest vault version.
     */
    val latest: VaultVersion
        get() = all.last()

    /**
     * Get the latest vault version string (e.g., "1.7.0").
     */
    val latestVersion: String
        get() = latest.version

    /**
     * Get the latest vault revision number.
     */
    val latestRevision: Int
        get() = latest.revision

    /**
     * Find a vault version by revision number.
     */
    fun findByRevision(revision: Int): VaultVersion? {
        return all.find { it.revision == revision }
    }

    /**
     * Find a vault version by version string.
     */
    fun findByVersion(version: String): VaultVersion? {
        return all.find { it.version == version }
    }
}
`;
}

/**
 * Indent each line of a multi-line string for Swift multi-line string literals
 */
function indentSwiftMultilineString(str, spaces = 8) {
  const indent = ' '.repeat(spaces);
  return str.split('\n').map(line => indent + line).join('\n');
}

/**
 * Generate Swift VaultSql struct
 */
function generateSwiftVaultSql(completeSchemaSql, migrations) {
  const escapedSql = escapeSwiftString(completeSchemaSql);
  const indentedSql = indentSwiftMultilineString(escapedSql);

  // Generate migration entries
  const migrationEntries = Object.entries(migrations).map(([key, sql]) => {
    const escapedMigrationSql = escapeSwiftString(sql);
    const indentedMigrationSql = indentSwiftMultilineString(escapedMigrationSql, 12);
    return `        ${key}: """
${indentedMigrationSql}
            """`;
  }).join(',\n');

  const migrationsDict = Object.keys(migrations).length > 0
    ? `
    /// Migration SQL scripts indexed by migration number.
    /// Key is the source migration number (migration FROM this version).
    public static let migrations: [Int: String] = [
${migrationEntries}
    ]`
    : `
    /// Migration SQL scripts indexed by migration number.
    public static let migrations: [Int: String] = [:]`;

  return `// <auto-generated />
// This file is auto-generated from core/vault/src/sql/SqlConstants.ts
// Do not edit this file directly. Run './build.sh' in core/vault to regenerate.
// swiftlint:disable all

import Foundation

/// SQL statements for vault creation and migration.
public struct VaultSql {
    /// Complete database schema SQL for creating a new vault with the latest schema.
    public static let completeSchema = """
${indentedSql}
        """
${migrationsDict}

    /// Get migration SQL for upgrading from a specific revision.
    public static func getMigrationSql(fromRevision: Int) -> String? {
        return migrations[fromRevision]
    }
}

// swiftlint:enable all
`;
}

/**
 * Generate Kotlin VaultSql object
 */
function generateKotlinVaultSql(completeSchemaSql, migrations) {
  const escapedSql = escapeKotlinString(completeSchemaSql);

  // Generate migration entries
  const migrationEntries = Object.entries(migrations).map(([key, sql]) => {
    const escapedMigrationSql = escapeKotlinString(sql);
    return `        ${key} to """
${escapedMigrationSql}
        """.trimIndent()`;
  }).join(',\n');

  const migrationsMap = Object.keys(migrations).length > 0
    ? `
    /**
     * Migration SQL scripts indexed by migration number.
     * Key is the source migration number (migration FROM this version).
     */
    val migrations: Map<Int, String> = mapOf(
${migrationEntries},
    )`
    : `
    /**
     * Migration SQL scripts indexed by migration number.
     */
    val migrations: Map<Int, String> = emptyMap()`;

  return `// <auto-generated />
// This file is auto-generated from core/vault/src/sql/SqlConstants.ts
// Do not edit this file directly. Run './build.sh' in core/vault to regenerate.
@file:Suppress("all")

package net.aliasvault.app.vaultstore.models

/**
 * SQL statements for vault creation and migration.
 */
object VaultSql {
    /**
     * Complete database schema SQL for creating a new vault with the latest schema.
     */
    val completeSchema = """
${escapedSql}
    """.trimIndent()
${migrationsMap}

    /**
     * Get migration SQL for upgrading from a specific revision.
     */
    fun getMigrationSql(fromRevision: Int): String? {
        return migrations[fromRevision]
    }
}
`;
}

/**
 * Ensure directory exists
 */
function ensureDir(filePath) {
  const dir = path.dirname(filePath);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
}

/**
 * Main execution
 */
function main() {
  console.log('Generating vault SQL for Swift and Kotlin...\n');

  // Read TypeScript VaultVersions source
  if (!fs.existsSync(TS_VERSIONS_SOURCE)) {
    throw new Error(`Source file not found: ${TS_VERSIONS_SOURCE}`);
  }

  const tsVersionsContent = fs.readFileSync(TS_VERSIONS_SOURCE, 'utf8');
  const versions = parseVaultVersions(tsVersionsContent);

  if (versions.length === 0) {
    throw new Error('No vault versions found in source file');
  }

  console.log(`Parsed ${versions.length} vault versions`);
  console.log(`Latest version: ${versions[versions.length - 1].version} (revision ${versions[versions.length - 1].revision})`);

  // Read TypeScript SqlConstants source
  if (!fs.existsSync(TS_SQL_SOURCE)) {
    throw new Error(`Source file not found: ${TS_SQL_SOURCE}`);
  }

  const tsSqlContent = fs.readFileSync(TS_SQL_SOURCE, 'utf8');
  const completeSchemaSql = parseCompleteSchemaSql(tsSqlContent);
  const migrations = parseMigrationScripts(tsSqlContent);

  console.log(`Parsed complete schema SQL (${completeSchemaSql.length} chars)`);
  console.log(`Parsed ${Object.keys(migrations).length} migration scripts`);

  // Generate Swift VaultVersions
  const swiftVersionsPath = path.join(SWIFT_OUTPUT_DIR, 'VaultVersions.swift');
  ensureDir(swiftVersionsPath);
  const swiftVersionsContent = generateSwiftVaultVersions(versions);
  fs.writeFileSync(swiftVersionsPath, swiftVersionsContent, 'utf8');
  console.log(`Generated: ${swiftVersionsPath}`);

  // Generate Swift VaultSql
  const swiftSqlPath = path.join(SWIFT_OUTPUT_DIR, 'VaultSql.swift');
  ensureDir(swiftSqlPath);
  const swiftSqlContent = generateSwiftVaultSql(completeSchemaSql, migrations);
  fs.writeFileSync(swiftSqlPath, swiftSqlContent, 'utf8');
  console.log(`Generated: ${swiftSqlPath}`);

  // Generate Kotlin VaultVersions
  const kotlinVersionsPath = path.join(KOTLIN_OUTPUT_DIR, 'VaultVersions.kt');
  ensureDir(kotlinVersionsPath);
  const kotlinVersionsContent = generateKotlinVaultVersions(versions);
  fs.writeFileSync(kotlinVersionsPath, kotlinVersionsContent, 'utf8');
  console.log(`Generated: ${kotlinVersionsPath}`);

  // Generate Kotlin VaultSql
  const kotlinSqlPath = path.join(KOTLIN_OUTPUT_DIR, 'VaultSql.kt');
  ensureDir(kotlinSqlPath);
  const kotlinSqlContent = generateKotlinVaultSql(completeSchemaSql, migrations);
  fs.writeFileSync(kotlinSqlPath, kotlinSqlContent, 'utf8');
  console.log(`Generated: ${kotlinSqlPath}`);

  console.log('\nVault SQL generation complete!');
}

main();
