import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { PrismaClient } from '@prisma/client';
import { get4RunrDataDir } from '@4runr/shared';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
import {
  isDockerAvailable,
  isDockerRunning,
  areContainersRunning,
  startDockerCompose,
  ensure4RunrStackRunning,
  waitForHealthy,
  getDockerStatus
} from './docker-manager.js';

/**
 * Database initialization result
 */
export interface DatabaseInitResult {
  mode: 'docker' | 'memory';
  client: PrismaClient | null;
  databaseUrl?: string;
  error?: string;
  warnings?: string[];
}

/**
 * Initialize database for Gateway
 * Handles Docker auto-start, migrations, and fallback to memory mode
 * 
 * @param logger - Fastify logger or compatible logger
 */
export async function initializeDatabase(logger: any): Promise<DatabaseInitResult> {
  const warnings: string[] = [];
  
  // Check explicit memory mode
  const persistenceMode = process.env['GATEWAY_PERSISTENCE'] || 'auto';
  if (persistenceMode === 'memory') {
    logger.info('GATEWAY_PERSISTENCE=memory - skipping database initialization');
    return { mode: 'memory', client: null };
  }
  
  // Check if DATABASE_URL is explicitly provided (e.g., production)
  if (process.env['DATABASE_URL'] && persistenceMode === 'postgres') {
    logger.info('Using explicitly configured DATABASE_URL');
    return await initializeWithExistingDatabase(logger);
  }
  
  // Auto mode: Try Docker-based local database
  logger.info('Checking Docker availability for local database...');
  
  const dockerStatus = await getDockerStatus();
  
  if (!dockerStatus.available) {
    logger.warn('⚠️  Docker not available. Falling back to memory-only mode.');
    warnings.push('Docker not installed - install Docker Desktop to enable persistent storage');
    return { mode: 'memory', client: null, warnings };
  }
  
  if (!dockerStatus.running) {
    logger.warn('⚠️  Docker daemon not running. Falling back to memory-only mode.');
    warnings.push('Docker is installed but not running - start Docker Desktop to enable persistent storage');
    return { mode: 'memory', client: null, warnings };
  }
  
  // Docker is available - ensure both Postgres and Redis are running
  try {
    logger.info('Ensuring 4Runr Docker containers (Postgres + Redis) are running...');
    await startDockerContainers(logger);

    // Wait for containers to be healthy (Postgres first — slower cold start)
    logger.info('Waiting for database to be ready...');
    await waitForHealthy('4runr-postgres', 60000);
    await waitForHealthy('4runr-redis', 15000);
    logger.info('✓ Database containers healthy');
    
    // Build DATABASE_URL and REDIS_URL for local Docker setup (localhost not 'postgres' hostname)
    const databaseUrl = 'postgresql://4runr:4runr_dev_pass@localhost:5432/4runr_local';
    const redisUrl = 'redis://localhost:6379';
    
    // Set environment variables so Redis client can connect when first requested
    process.env['DATABASE_URL'] = databaseUrl;
    process.env['REDIS_URL'] = redisUrl;
    
    // Run migrations
    logger.info('Running database migrations...');
    await runMigrations(databaseUrl, logger);
    logger.info('✓ Migrations complete');
    
    // Create Prisma client
    const client = new PrismaClient({
      datasources: {
        db: {
          url: databaseUrl
        }
      },
      log: [
        { level: 'warn', emit: 'event' },
        { level: 'error', emit: 'event' }
      ]
    });
    
    // Wire up Prisma logs to Fastify logger
    client.$on('warn', (e: any) => logger.warn({ prisma: true }, e.message));
    client.$on('error', (e: any) => logger.error({ prisma: true }, e.message));
    
    // Test connection
    await client.$connect();
    logger.info('✓ Database connection established');
    
    return {
      mode: 'docker',
      client,
      databaseUrl
    };
    
  } catch (err) {
    const errorMsg = err instanceof Error ? err.message : String(err);
    logger.error({ err }, 'Failed to initialize Docker database');
    logger.warn('⚠️  Falling back to memory-only mode');
    
    return {
      mode: 'memory',
      client: null,
      error: errorMsg,
      warnings: ['Database initialization failed - running in memory-only mode', errorMsg]
    };
  }
}

/**
 * Initialize with an existing database (production mode)
 */
async function initializeWithExistingDatabase(logger: any): Promise<DatabaseInitResult> {
  try {
    const databaseUrl = process.env['DATABASE_URL']!;
    
    // Run migrations
    logger.info('Running database migrations...');
    await runMigrations(databaseUrl, logger);
    logger.info('✓ Migrations complete');
    
    // Create Prisma client
    const client = new PrismaClient({
      log: [
        { level: 'warn', emit: 'event' },
        { level: 'error', emit: 'event' }
      ]
    });
    
    client.$on('warn', (e: any) => logger.warn({ prisma: true }, e.message));
    client.$on('error', (e: any) => logger.error({ prisma: true }, e.message));
    
    await client.$connect();
    logger.info('✓ Database connection established');
    
    return {
      mode: 'docker', // Could be 'postgres' but we use 'docker' as generic DB mode
      client,
      databaseUrl
    };
    
  } catch (err) {
    const errorMsg = err instanceof Error ? err.message : String(err);
    logger.error({ err }, 'Failed to connect to database');
    
    return {
      mode: 'memory',
      client: null,
      error: errorMsg,
      warnings: ['Database connection failed - running in memory-only mode', errorMsg]
    };
  }
}

/**
 * Start Docker containers
 */
async function startDockerContainers(logger: any): Promise<void> {
  // Get user data directory
  const dataDir = get4RunrDataDir();
  const composeFilePath = path.join(dataDir, 'docker-compose.yml');

  // Bundled template next to Gateway (global 4runr-os install). Always sync so npm updates
  // (e.g. healthcheck fixes) apply without manual docker compose — users only run `4r`.
  const possiblePaths = [
    path.join(process.cwd(), 'docker-compose.local.yml'), // npm run start cwd = apps/gateway (global + repo)
    path.join(__dirname, '../../docker-compose.local.yml'), // dist/db -> apps/gateway
    path.join(__dirname, '../../../docker-compose.local.yml'), // dist/src/db or nested
    path.join(__dirname, '../../../../docker-compose.local.yml'),
    path.join(process.cwd(), 'apps/gateway/docker-compose.local.yml'),
  ];

  let templatePath: string | null = null;
  for (const tryPath of possiblePaths) {
    if (fs.existsSync(tryPath)) {
      templatePath = tryPath;
      break;
    }
  }

  if (templatePath) {
    logger.info(`Syncing Docker Compose template from ${templatePath} → ${composeFilePath}`);
    fs.copyFileSync(templatePath, composeFilePath);
  } else if (!fs.existsSync(composeFilePath)) {
    throw new Error(`Docker Compose template not found. Tried: ${possiblePaths.join(', ')}`);
  } else {
    logger.warn(`Docker Compose template missing; using existing ${composeFilePath}`);
  }

  // Start or restart containers (watchdog stops both when 4r exits)
  await ensure4RunrStackRunning(composeFilePath);
}

/**
 * Run Prisma migrations
 */
async function runMigrations(databaseUrl: string, logger: any): Promise<void> {
  // dist/src/db -> monorepo root: ../../../../../prisma. Also support global 4runr-os (cwd often apps/gateway).
  const candidates = [
    path.join(process.cwd(), '..', '..', 'prisma', 'schema.prisma'),
    path.join(__dirname, '../../../../../prisma/schema.prisma'),
    path.join(__dirname, '../../../../prisma/schema.prisma'),
    path.join(__dirname, '../../../prisma/schema.prisma'),
    path.join(process.cwd(), 'prisma', 'schema.prisma'),
  ];
  let actualSchemaPath: string | null = null;
  for (const p of candidates) {
    if (fs.existsSync(p)) {
      actualSchemaPath = p;
      break;
    }
  }
  if (!actualSchemaPath) {
    logger.warn(
      `⚠️  Prisma schema not found (tried ${candidates.length} locations relative to Gateway), skipping migrations`,
    );
    return;
  }
  
  try {
    // For local Docker Postgres, use db push to avoid migration compatibility
    // (migrations were created for SQLite, need Postgres-native approach)
    const isLocalDocker = databaseUrl.includes('localhost:5432');
    const command = isLocalDocker 
      ? `npx prisma db push --skip-generate --accept-data-loss`
      : `npx prisma migrate deploy`;
    
    execSync(command, {
      env: {
        ...process.env,
        DATABASE_URL: databaseUrl
      },
      cwd: path.dirname(actualSchemaPath),
      stdio: 'pipe', // Suppress output unless error
      timeout: 60000 // 60 seconds
    });
  } catch (err) {
    const errorMsg = err instanceof Error ? err.message : String(err);
    logger.error(`Migration failed: ${errorMsg}`);
    throw new Error(`Database migration failed: ${errorMsg}`);
  }
}

/**
 * Cleanup function for graceful shutdown
 */
export async function shutdownDatabase(client: PrismaClient | null, logger: any): Promise<void> {
  if (client) {
    try {
      await client.$disconnect();
      logger.info('✓ Database connection closed');
    } catch (err) {
      logger.error({ err }, 'Error disconnecting from database');
    }
  }
  
  // Stop Docker containers (preserves data volumes)
  try {
    const { stop4RunrContainers } = await import('./docker-manager.js');
    await stop4RunrContainers();
    logger.info('✓ Docker containers stopped (data preserved)');
  } catch (err) {
    // Ignore errors - containers might not be running or Docker unavailable
    logger.debug('Could not stop Docker containers (may not be running)');
  }
}
